summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r3
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/r3
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/r3')
-rw-r--r--src/VBox/Runtime/r3/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/RTPathTemp.cpp86
-rw-r--r--src/VBox/Runtime/r3/alloc-ef-cpp.cpp169
-rw-r--r--src/VBox/Runtime/r3/alloc-ef.cpp1088
-rw-r--r--src/VBox/Runtime/r3/alloc-ef.h231
-rw-r--r--src/VBox/Runtime/r3/alloc.cpp317
-rw-r--r--src/VBox/Runtime/r3/allocex.cpp152
-rw-r--r--src/VBox/Runtime/r3/allocex.h96
-rw-r--r--src/VBox/Runtime/r3/darwin/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/darwin/RTCrStoreCreateSnapshotById-darwin.cpp268
-rw-r--r--src/VBox/Runtime/r3/darwin/RTFileQuerySectorSize-darwin.cpp89
-rw-r--r--src/VBox/Runtime/r3/darwin/RTMpGetDescription-generic.cpp173
-rw-r--r--src/VBox/Runtime/r3/darwin/RTPathUserDocuments-darwin.cpp123
-rw-r--r--src/VBox/Runtime/r3/darwin/RTSystemQueryDmiString-darwin.cpp163
-rw-r--r--src/VBox/Runtime/r3/darwin/filelock-darwin.cpp178
-rw-r--r--src/VBox/Runtime/r3/darwin/krnlmod-darwin.cpp470
-rw-r--r--src/VBox/Runtime/r3/darwin/mp-darwin.cpp435
-rw-r--r--src/VBox/Runtime/r3/darwin/pathhost-darwin.cpp117
-rw-r--r--src/VBox/Runtime/r3/darwin/rtProcInitExePath-darwin.cpp76
-rw-r--r--src/VBox/Runtime/r3/darwin/sched-darwin.cpp363
-rw-r--r--src/VBox/Runtime/r3/darwin/systemmem-darwin.cpp96
-rw-r--r--src/VBox/Runtime/r3/darwin/time-darwin.cpp125
-rw-r--r--src/VBox/Runtime/r3/dir.cpp917
-rw-r--r--src/VBox/Runtime/r3/dir2.cpp242
-rw-r--r--src/VBox/Runtime/r3/fileio.cpp423
-rw-r--r--src/VBox/Runtime/r3/freebsd/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/freebsd/RTFileQuerySectorSize-freebsd.cpp88
-rw-r--r--src/VBox/Runtime/r3/freebsd/fileaio-freebsd.cpp682
-rw-r--r--src/VBox/Runtime/r3/freebsd/mp-freebsd.cpp211
-rw-r--r--src/VBox/Runtime/r3/freebsd/rtProcInitExePath-freebsd.cpp139
-rw-r--r--src/VBox/Runtime/r3/freebsd/systemmem-freebsd.cpp108
-rw-r--r--src/VBox/Runtime/r3/fs.cpp274
-rw-r--r--src/VBox/Runtime/r3/ftp-server.cpp2589
-rw-r--r--src/VBox/Runtime/r3/generic/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/generic/RTLocaleQueryLocaleName-r3-generic.cpp67
-rw-r--r--src/VBox/Runtime/r3/generic/RTLocaleQueryNormalizedBaseLocaleName-r3-generic.cpp102
-rw-r--r--src/VBox/Runtime/r3/generic/RTLocaleQueryUserCountryCode-r3-generic.cpp87
-rw-r--r--src/VBox/Runtime/r3/generic/RTTimeZoneGetCurrent-generic.cpp51
-rw-r--r--src/VBox/Runtime/r3/generic/allocex-r3-generic.cpp70
-rw-r--r--src/VBox/Runtime/r3/generic/dirrel-r3-generic.cpp369
-rw-r--r--src/VBox/Runtime/r3/generic/semspinmutex-r3-generic.cpp103
-rw-r--r--src/VBox/Runtime/r3/haiku/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/haiku/rtProcInitExePath-haiku.cpp71
-rw-r--r--src/VBox/Runtime/r3/haiku/time-haiku.cpp95
-rw-r--r--src/VBox/Runtime/r3/http-server.cpp1455
-rw-r--r--src/VBox/Runtime/r3/init-data.cpp63
-rw-r--r--src/VBox/Runtime/r3/init.cpp666
-rw-r--r--src/VBox/Runtime/r3/init.h50
-rw-r--r--src/VBox/Runtime/r3/linux/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/linux/RTFileCopyPartEx-linux.cpp196
-rw-r--r--src/VBox/Runtime/r3/linux/RTFileQuerySectorSize-linux.cpp88
-rw-r--r--src/VBox/Runtime/r3/linux/RTFileSetAllocationSize-linux.cpp86
-rw-r--r--src/VBox/Runtime/r3/linux/RTProcIsRunningByName-linux.cpp128
-rw-r--r--src/VBox/Runtime/r3/linux/RTSystemFirmware-linux.cpp115
-rw-r--r--src/VBox/Runtime/r3/linux/RTSystemQueryDmiString-linux.cpp96
-rw-r--r--src/VBox/Runtime/r3/linux/RTSystemShutdown-linux.cpp111
-rw-r--r--src/VBox/Runtime/r3/linux/RTThreadGetNativeState-linux.cpp121
-rw-r--r--src/VBox/Runtime/r3/linux/fileaio-linux.cpp847
-rw-r--r--src/VBox/Runtime/r3/linux/ioqueue-iouringfile-provider.cpp940
-rw-r--r--src/VBox/Runtime/r3/linux/krnlmod-linux.cpp358
-rw-r--r--src/VBox/Runtime/r3/linux/mp-linux.cpp328
-rw-r--r--src/VBox/Runtime/r3/linux/rtProcInitExePath-linux.cpp79
-rw-r--r--src/VBox/Runtime/r3/linux/sched-linux.cpp707
-rw-r--r--src/VBox/Runtime/r3/linux/semevent-linux.cpp607
-rw-r--r--src/VBox/Runtime/r3/linux/semeventmulti-linux.cpp600
-rw-r--r--src/VBox/Runtime/r3/linux/semmutex-linux.cpp475
-rw-r--r--src/VBox/Runtime/r3/linux/semwait-linux.h233
-rw-r--r--src/VBox/Runtime/r3/linux/sysfs.cpp736
-rw-r--r--src/VBox/Runtime/r3/linux/systemmem-linux.cpp119
-rw-r--r--src/VBox/Runtime/r3/linux/thread-affinity-linux.cpp105
-rw-r--r--src/VBox/Runtime/r3/linux/time-linux.cpp169
-rw-r--r--src/VBox/Runtime/r3/linux/tpm-linux.cpp229
-rw-r--r--src/VBox/Runtime/r3/memsafer-r3.cpp691
-rw-r--r--src/VBox/Runtime/r3/netbsd/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/netbsd/rtProcInitExePath-netbsd.cpp110
-rw-r--r--src/VBox/Runtime/r3/nocrt-cerr.cpp52
-rw-r--r--src/VBox/Runtime/r3/nocrt-clearerr.cpp53
-rw-r--r--src/VBox/Runtime/r3/nocrt-cout.cpp52
-rw-r--r--src/VBox/Runtime/r3/nocrt-errno.cpp55
-rw-r--r--src/VBox/Runtime/r3/nocrt-fclose.cpp59
-rw-r--r--src/VBox/Runtime/r3/nocrt-fdopen.cpp66
-rw-r--r--src/VBox/Runtime/r3/nocrt-ferror.cpp57
-rw-r--r--src/VBox/Runtime/r3/nocrt-fflush.cpp59
-rw-r--r--src/VBox/Runtime/r3/nocrt-fgetc.cpp61
-rw-r--r--src/VBox/Runtime/r3/nocrt-fileno.cpp61
-rw-r--r--src/VBox/Runtime/r3/nocrt-fopen.cpp60
-rw-r--r--src/VBox/Runtime/r3/nocrt-fputc.cpp61
-rw-r--r--src/VBox/Runtime/r3/nocrt-fputs.cpp63
-rw-r--r--src/VBox/Runtime/r3/nocrt-fread.cpp63
-rw-r--r--src/VBox/Runtime/r3/nocrt-fseek.cpp59
-rw-r--r--src/VBox/Runtime/r3/nocrt-fseeko.cpp59
-rw-r--r--src/VBox/Runtime/r3/nocrt-ftell.cpp65
-rw-r--r--src/VBox/Runtime/r3/nocrt-ftello.cpp60
-rw-r--r--src/VBox/Runtime/r3/nocrt-fwrite.cpp63
-rw-r--r--src/VBox/Runtime/r3/nocrt-getc.cpp61
-rw-r--r--src/VBox/Runtime/r3/nocrt-per-thread-1.cpp58
-rw-r--r--src/VBox/Runtime/r3/nocrt-per-thread-2.cpp238
-rw-r--r--src/VBox/Runtime/r3/nocrt-putc.cpp61
-rw-r--r--src/VBox/Runtime/r3/nocrt-puts.cpp68
-rw-r--r--src/VBox/Runtime/r3/nocrt-setvbuf.cpp72
-rw-r--r--src/VBox/Runtime/r3/nocrt-tmpfile.cpp59
-rw-r--r--src/VBox/Runtime/r3/nocrt-tmpfile_s.cpp72
-rw-r--r--src/VBox/Runtime/r3/nt/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/nt/RTFileQueryFsSizes-nt.cpp102
-rw-r--r--src/VBox/Runtime/r3/nt/RTFileSetMode-r3-nt.cpp92
-rw-r--r--src/VBox/Runtime/r3/nt/RTPathQueryInfo-nt.cpp680
-rw-r--r--src/VBox/Runtime/r3/nt/RTPathSetMode-r3-nt.cpp98
-rw-r--r--src/VBox/Runtime/r3/nt/RTProcQueryParent-r3-nt.cpp103
-rw-r--r--src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp1011
-rw-r--r--src/VBox/Runtime/r3/nt/dirrel-r3-nt.cpp651
-rw-r--r--src/VBox/Runtime/r3/nt/fs-nt.cpp279
-rw-r--r--src/VBox/Runtime/r3/nt/internal-r3-nt.h92
-rw-r--r--src/VBox/Runtime/r3/nt/pathint-nt.cpp1161
-rw-r--r--src/VBox/Runtime/r3/nt/time-nt.cpp228
-rw-r--r--src/VBox/Runtime/r3/os2/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp121
-rw-r--r--src/VBox/Runtime/r3/os2/filelock-os2.cpp186
-rw-r--r--src/VBox/Runtime/r3/os2/mp-os2.cpp125
-rw-r--r--src/VBox/Runtime/r3/os2/pipe-os2.cpp1083
-rw-r--r--src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp71
-rw-r--r--src/VBox/Runtime/r3/os2/sched-os2.cpp244
-rw-r--r--src/VBox/Runtime/r3/os2/sems-os2.cpp392
-rw-r--r--src/VBox/Runtime/r3/os2/serialport-os2.cpp755
-rw-r--r--src/VBox/Runtime/r3/os2/systemmem-os2.cpp80
-rw-r--r--src/VBox/Runtime/r3/os2/thread-os2.cpp330
-rw-r--r--src/VBox/Runtime/r3/os2/time-os2.cpp87
-rw-r--r--src/VBox/Runtime/r3/path.cpp163
-rw-r--r--src/VBox/Runtime/r3/poll.cpp1148
-rw-r--r--src/VBox/Runtime/r3/posix/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/posix/RTFileQueryFsSizes-posix.cpp78
-rw-r--r--src/VBox/Runtime/r3/posix/RTFileSetAllocationSize-posix.cpp87
-rw-r--r--src/VBox/Runtime/r3/posix/RTHandleGetStandard-posix.cpp144
-rw-r--r--src/VBox/Runtime/r3/posix/RTMemProtect-posix.cpp105
-rw-r--r--src/VBox/Runtime/r3/posix/RTMpGetCount-posix.cpp89
-rw-r--r--src/VBox/Runtime/r3/posix/RTPathUserDocuments-posix.cpp63
-rw-r--r--src/VBox/Runtime/r3/posix/RTPathUserHome-posix.cpp173
-rw-r--r--src/VBox/Runtime/r3/posix/RTSystemQueryOSInfo-posix.cpp100
-rw-r--r--src/VBox/Runtime/r3/posix/RTSystemQueryTotalRam-posix.cpp61
-rw-r--r--src/VBox/Runtime/r3/posix/RTTimeNow-posix.cpp61
-rw-r--r--src/VBox/Runtime/r3/posix/RTTimeSet-posix.cpp60
-rw-r--r--src/VBox/Runtime/r3/posix/RTTimeZoneGetCurrent-posix.cpp262
-rw-r--r--src/VBox/Runtime/r3/posix/allocex-r3-posix.cpp120
-rw-r--r--src/VBox/Runtime/r3/posix/dir-posix.cpp733
-rw-r--r--src/VBox/Runtime/r3/posix/env-posix.cpp179
-rw-r--r--src/VBox/Runtime/r3/posix/errvars-posix.cpp88
-rw-r--r--src/VBox/Runtime/r3/posix/fileaio-posix.cpp1071
-rw-r--r--src/VBox/Runtime/r3/posix/fileio-at-posix.cpp107
-rw-r--r--src/VBox/Runtime/r3/posix/fileio-posix.cpp934
-rw-r--r--src/VBox/Runtime/r3/posix/fileio-sg-at-posix.cpp298
-rw-r--r--src/VBox/Runtime/r3/posix/fileio-sg-posix.cpp260
-rw-r--r--src/VBox/Runtime/r3/posix/fileio2-posix.cpp210
-rw-r--r--src/VBox/Runtime/r3/posix/filelock-posix.cpp148
-rw-r--r--src/VBox/Runtime/r3/posix/fs-posix.cpp346
-rw-r--r--src/VBox/Runtime/r3/posix/fs2-posix.cpp165
-rw-r--r--src/VBox/Runtime/r3/posix/fs3-posix.cpp94
-rw-r--r--src/VBox/Runtime/r3/posix/ldrNative-posix.cpp207
-rw-r--r--src/VBox/Runtime/r3/posix/localipc-posix.cpp1172
-rw-r--r--src/VBox/Runtime/r3/posix/path-posix.cpp418
-rw-r--r--src/VBox/Runtime/r3/posix/path2-posix.cpp316
-rw-r--r--src/VBox/Runtime/r3/posix/pathhost-posix.cpp294
-rw-r--r--src/VBox/Runtime/r3/posix/pipe-posix.cpp754
-rw-r--r--src/VBox/Runtime/r3/posix/process-creation-posix.cpp2408
-rw-r--r--src/VBox/Runtime/r3/posix/process-posix.cpp279
-rw-r--r--src/VBox/Runtime/r3/posix/rand-posix.cpp148
-rw-r--r--src/VBox/Runtime/r3/posix/rtmempage-exec-mmap-heap-posix.cpp797
-rw-r--r--src/VBox/Runtime/r3/posix/rtmempage-exec-mmap-posix.cpp182
-rw-r--r--src/VBox/Runtime/r3/posix/sched-posix.cpp849
-rw-r--r--src/VBox/Runtime/r3/posix/semevent-posix.cpp654
-rw-r--r--src/VBox/Runtime/r3/posix/semeventmulti-posix.cpp613
-rw-r--r--src/VBox/Runtime/r3/posix/semmutex-posix.cpp467
-rw-r--r--src/VBox/Runtime/r3/posix/semrw-posix.cpp741
-rw-r--r--src/VBox/Runtime/r3/posix/semwait.h162
-rw-r--r--src/VBox/Runtime/r3/posix/serialport-posix.cpp1269
-rw-r--r--src/VBox/Runtime/r3/posix/shmem-posix.cpp419
-rw-r--r--src/VBox/Runtime/r3/posix/symlink-posix.cpp247
-rw-r--r--src/VBox/Runtime/r3/posix/thread-posix.cpp780
-rw-r--r--src/VBox/Runtime/r3/posix/thread2-posix.cpp133
-rw-r--r--src/VBox/Runtime/r3/posix/time-posix.cpp99
-rw-r--r--src/VBox/Runtime/r3/posix/timelocal-posix.cpp215
-rw-r--r--src/VBox/Runtime/r3/posix/timer-posix.cpp847
-rw-r--r--src/VBox/Runtime/r3/posix/tls-posix.cpp119
-rw-r--r--src/VBox/Runtime/r3/posix/utf8-posix.cpp709
-rw-r--r--src/VBox/Runtime/r3/process-data.cpp67
-rw-r--r--src/VBox/Runtime/r3/process.cpp135
-rw-r--r--src/VBox/Runtime/r3/socket.cpp3186
-rw-r--r--src/VBox/Runtime/r3/solaris/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/solaris/RTFileQuerySectorSize-solaris.cpp90
-rw-r--r--src/VBox/Runtime/r3/solaris/RTSystemFirmware-solaris.cpp78
-rw-r--r--src/VBox/Runtime/r3/solaris/RTSystemQueryDmiString-solaris.cpp117
-rw-r--r--src/VBox/Runtime/r3/solaris/RTSystemShutdown-solaris.cpp112
-rw-r--r--src/VBox/Runtime/r3/solaris/coredumper-solaris.cpp2375
-rw-r--r--src/VBox/Runtime/r3/solaris/coredumper-solaris.h177
-rw-r--r--src/VBox/Runtime/r3/solaris/fileaio-solaris.cpp574
-rw-r--r--src/VBox/Runtime/r3/solaris/krnlmod-solaris.cpp358
-rw-r--r--src/VBox/Runtime/r3/solaris/mp-solaris.cpp479
-rw-r--r--src/VBox/Runtime/r3/solaris/rtProcInitExePath-solaris.cpp81
-rw-r--r--src/VBox/Runtime/r3/solaris/systemmem-solaris.cpp182
-rw-r--r--src/VBox/Runtime/r3/solaris/thread-affinity-solaris.cpp104
-rw-r--r--src/VBox/Runtime/r3/stream.cpp2698
-rw-r--r--src/VBox/Runtime/r3/tcp.cpp1082
-rw-r--r--src/VBox/Runtime/r3/test.cpp1662
-rw-r--r--src/VBox/Runtime/r3/testi.cpp214
-rw-r--r--src/VBox/Runtime/r3/udp.cpp726
-rw-r--r--src/VBox/Runtime/r3/win/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp194
-rw-r--r--src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp71
-rw-r--r--src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp143
-rw-r--r--src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp116
-rw-r--r--src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp129
-rw-r--r--src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp54
-rw-r--r--src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp223
-rw-r--r--src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp269
-rw-r--r--src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp358
-rw-r--r--src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp131
-rw-r--r--src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp183
-rw-r--r--src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp117
-rw-r--r--src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp82
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def1678
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def1640
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def395
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def430
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-win32.def62
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-win64.def74
-rw-r--r--src/VBox/Runtime/r3/win/alloc-win.cpp210
-rw-r--r--src/VBox/Runtime/r3/win/allocex-win.cpp133
-rw-r--r--src/VBox/Runtime/r3/win/dir-win.cpp169
-rw-r--r--src/VBox/Runtime/r3/win/direnum-win.cpp404
-rw-r--r--src/VBox/Runtime/r3/win/dllmain-win.cpp100
-rw-r--r--src/VBox/Runtime/r3/win/env-win.cpp520
-rw-r--r--src/VBox/Runtime/r3/win/errvars-win.cpp108
-rw-r--r--src/VBox/Runtime/r3/win/fileaio-win.cpp554
-rw-r--r--src/VBox/Runtime/r3/win/fileio-win.cpp1549
-rw-r--r--src/VBox/Runtime/r3/win/fs-win.cpp440
-rw-r--r--src/VBox/Runtime/r3/win/init-win.cpp919
-rw-r--r--src/VBox/Runtime/r3/win/internal-r3-win.h244
-rw-r--r--src/VBox/Runtime/r3/win/krnlmod-win.cpp331
-rw-r--r--src/VBox/Runtime/r3/win/ldrNative-win.cpp326
-rw-r--r--src/VBox/Runtime/r3/win/localipc-win.cpp1778
-rw-r--r--src/VBox/Runtime/r3/win/mp-win.cpp822
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp57
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp58
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm44
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp115
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-atexit-win.asm44
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp150
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm44
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp211
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp162
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp174
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-streams-win.cpp223
-rw-r--r--src/VBox/Runtime/r3/win/ntdll-mini-implib.def163
-rw-r--r--src/VBox/Runtime/r3/win/path-win.cpp743
-rw-r--r--src/VBox/Runtime/r3/win/pathint-win.cpp204
-rw-r--r--src/VBox/Runtime/r3/win/pipe-win.cpp1537
-rw-r--r--src/VBox/Runtime/r3/win/process-win.cpp2750
-rw-r--r--src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp72
-rw-r--r--src/VBox/Runtime/r3/win/sched-win.cpp333
-rw-r--r--src/VBox/Runtime/r3/win/semevent-win.cpp315
-rw-r--r--src/VBox/Runtime/r3/win/semeventmulti-win.cpp389
-rw-r--r--src/VBox/Runtime/r3/win/semmutex-win.cpp356
-rw-r--r--src/VBox/Runtime/r3/win/serialport-win.cpp1056
-rw-r--r--src/VBox/Runtime/r3/win/shmem-win.cpp473
-rw-r--r--src/VBox/Runtime/r3/win/symlink-win.cpp367
-rw-r--r--src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp68
-rw-r--r--src/VBox/Runtime/r3/win/thread-win.cpp588
-rw-r--r--src/VBox/Runtime/r3/win/thread2-win.cpp84
-rw-r--r--src/VBox/Runtime/r3/win/time-win.cpp228
-rw-r--r--src/VBox/Runtime/r3/win/time2-win.cpp164
-rw-r--r--src/VBox/Runtime/r3/win/timer-win.cpp520
-rw-r--r--src/VBox/Runtime/r3/win/tls-win.cpp230
-rw-r--r--src/VBox/Runtime/r3/win/tpm-win.cpp307
-rw-r--r--src/VBox/Runtime/r3/win/utf16locale-win.cpp58
-rw-r--r--src/VBox/Runtime/r3/win/utf8-win.cpp204
-rw-r--r--src/VBox/Runtime/r3/win/uuid-win.cpp195
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h43
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h42
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm56
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp891
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp93
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm57
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp84
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm59
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp103
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm58
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp113
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes.h155
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes.mac76
-rw-r--r--src/VBox/Runtime/r3/xml.cpp2357
288 files changed, 100770 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/Makefile.kup b/src/VBox/Runtime/r3/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/Makefile.kup
diff --git a/src/VBox/Runtime/r3/RTPathTemp.cpp b/src/VBox/Runtime/r3/RTPathTemp.cpp
new file mode 100644
index 00000000..dc43a612
--- /dev/null
+++ b/src/VBox/Runtime/r3/RTPathTemp.cpp
@@ -0,0 +1,86 @@
+/* $Id: RTPathTemp.cpp $ */
+/** @file
+ * IPRT - Path Manipulation, RTPathTemp.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/path.h>
+
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include "internal/path.h"
+
+
+RTDECL(int) RTPathTemp(char *pszPath, size_t cchPath)
+{
+ /*
+ * Try get it from the environment first.
+ */
+ static const char * const s_apszVars[] =
+ {
+ "IPRT_TMPDIR"
+#if defined(RT_OS_WINDOWS)
+ , "TMP", "TEMP", "USERPROFILE"
+#elif defined(RT_OS_OS2)
+ , "TMP", "TEMP", "TMPDIR"
+#else
+ , "TMPDIR"
+#endif
+ };
+ for (size_t iVar = 0; iVar < RT_ELEMENTS(s_apszVars); iVar++)
+ {
+ int rc = RTEnvGetEx(RTENV_DEFAULT, s_apszVars[iVar], pszPath, cchPath, NULL);
+ if (rc != VERR_ENV_VAR_NOT_FOUND)
+ return rc;
+ }
+
+ /*
+ * Here we should use some sane system default, instead we just use
+ * the typical unix temp dir for now.
+ */
+ /** @todo Windows should default to the windows directory, see GetTempPath.
+ * Some unixes has path.h and _PATH_TMP. There is also a question about
+ * whether /var/tmp wouldn't be a better place... */
+ if (cchPath < sizeof("/tmp") )
+ return VERR_BUFFER_OVERFLOW;
+ memcpy(pszPath, "/tmp", sizeof("/tmp"));
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/alloc-ef-cpp.cpp b/src/VBox/Runtime/r3/alloc-ef-cpp.cpp
new file mode 100644
index 00000000..b9496caa
--- /dev/null
+++ b/src/VBox/Runtime/r3/alloc-ef-cpp.cpp
@@ -0,0 +1,169 @@
+/* $Id: alloc-ef-cpp.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, C++ electric fence.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "alloc-ef.h"
+
+#include <iprt/asm.h>
+#include <new>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @todo test this on MSC */
+
+/** MSC declares the operators as cdecl it seems. */
+#ifdef _MSC_VER
+# define RT_EF_CDECL __cdecl
+#else
+# define RT_EF_CDECL
+#endif
+
+/** MSC doesn't use the standard namespace. */
+#ifdef _MSC_VER
+# define RT_EF_SIZE_T size_t
+#else
+# define RT_EF_SIZE_T std::size_t
+#endif
+
+/** The hint that we're throwing std::bad_alloc is not apprecitated by MSC. */
+#ifdef RT_EXCEPTIONS_ENABLED
+# ifdef _MSC_VER
+# define RT_EF_THROWS_BAD_ALLOC
+# define RT_EF_NOTHROW RT_NO_THROW_DEF
+# else
+# ifdef _GLIBCXX_THROW
+# define RT_EF_THROWS_BAD_ALLOC _GLIBCXX_THROW(std::bad_alloc)
+# elif defined(__cplusplus) && (__cplusplus + 0) < 201700
+# define RT_EF_THROWS_BAD_ALLOC throw(std::bad_alloc)
+# else
+# define RT_EF_THROWS_BAD_ALLOC noexcept(false)
+# endif
+# define RT_EF_NOTHROW throw()
+# endif
+#else /* !RT_EXCEPTIONS_ENABLED */
+# define RT_EF_THROWS_BAD_ALLOC
+# define RT_EF_NOTHROW
+#endif /* !RT_EXCEPTIONS_ENABLED */
+
+
+void *RT_EF_CDECL operator new(RT_EF_SIZE_T cb) RT_EF_THROWS_BAD_ALLOC
+{
+ void *pv = rtR3MemAlloc("new", RTMEMTYPE_NEW, cb, cb, NULL, ASMReturnAddress(), NULL, 0, NULL);
+ if (!pv)
+ throw std::bad_alloc();
+ return pv;
+}
+
+
+void *RT_EF_CDECL operator new(RT_EF_SIZE_T cb, const std::nothrow_t &) RT_EF_NOTHROW
+{
+ void *pv = rtR3MemAlloc("new nothrow", RTMEMTYPE_NEW, cb, cb, NULL, ASMReturnAddress(), NULL, 0, NULL);
+ return pv;
+}
+
+
+void RT_EF_CDECL operator delete(void *pv) RT_EF_NOTHROW
+{
+ rtR3MemFree("delete", RTMEMTYPE_DELETE, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+#ifdef __cpp_sized_deallocation
+void RT_EF_CDECL operator delete(void *pv, RT_EF_SIZE_T cb) RT_EF_NOTHROW
+{
+ NOREF(cb);
+ AssertMsgFailed(("cb ignored!\n"));
+ rtR3MemFree("delete", RTMEMTYPE_DELETE, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+}
+#endif
+
+
+void RT_EF_CDECL operator delete(void * pv, const std::nothrow_t &) RT_EF_NOTHROW
+{
+ rtR3MemFree("delete nothrow", RTMEMTYPE_DELETE, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+/*
+ *
+ * Array object form.
+ * Array object form.
+ * Array object form.
+ *
+ */
+
+void *RT_EF_CDECL operator new[](RT_EF_SIZE_T cb) RT_EF_THROWS_BAD_ALLOC
+{
+ void *pv = rtR3MemAlloc("new[]", RTMEMTYPE_NEW_ARRAY, cb, cb, NULL, ASMReturnAddress(), NULL, 0, NULL);
+ if (!pv)
+ throw std::bad_alloc();
+ return pv;
+}
+
+
+void * RT_EF_CDECL operator new[](RT_EF_SIZE_T cb, const std::nothrow_t &) RT_EF_NOTHROW
+{
+ void *pv = rtR3MemAlloc("new[] nothrow", RTMEMTYPE_NEW_ARRAY, cb, cb, NULL, ASMReturnAddress(), NULL, 0, NULL);
+ return pv;
+}
+
+
+void RT_EF_CDECL operator delete[](void * pv) RT_EF_NOTHROW
+{
+ rtR3MemFree("delete[]", RTMEMTYPE_DELETE_ARRAY, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+#ifdef __cpp_sized_deallocation
+void RT_EF_CDECL operator delete[](void * pv, RT_EF_SIZE_T cb) RT_EF_NOTHROW
+{
+ NOREF(cb);
+ AssertMsgFailed(("cb ignored!\n"));
+ rtR3MemFree("delete[]", RTMEMTYPE_DELETE_ARRAY, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+}
+#endif
+
+
+void RT_EF_CDECL operator delete[](void *pv, const std::nothrow_t &) RT_EF_NOTHROW
+{
+ rtR3MemFree("delete[] nothrow", RTMEMTYPE_DELETE_ARRAY, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+}
+
diff --git a/src/VBox/Runtime/r3/alloc-ef.cpp b/src/VBox/Runtime/r3/alloc-ef.cpp
new file mode 100644
index 00000000..4b61d906
--- /dev/null
+++ b/src/VBox/Runtime/r3/alloc-ef.cpp
@@ -0,0 +1,1088 @@
+/* $Id: alloc-ef.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, electric fence.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "alloc-ef.h"
+#include <iprt/mem.h>
+#include <iprt/log.h>
+#include <iprt/asm.h>
+#include <iprt/thread.h>
+#include <VBox/sup.h>
+#include <iprt/errcore.h>
+#ifndef IPRT_NO_CRT
+# include <errno.h>
+# include <stdio.h>
+# include <stdlib.h>
+#endif
+
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+
+#ifdef RTALLOC_REPLACE_MALLOC
+# include <VBox/dis.h>
+# include <VBox/disopcode.h>
+# include <dlfcn.h>
+# ifdef RT_OS_DARWIN
+# include <malloc/malloc.h>
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifdef RTALLOC_REPLACE_MALLOC
+# define RTMEM_REPLACMENT_ALIGN(a_cb) ((a_cb) >= 16 ? RT_ALIGN_Z(a_cb, 16) \
+ : (a_cb) >= sizeof(uintptr_t) ? RT_ALIGN_Z(a_cb, sizeof(uintptr_t)) : (a_cb))
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef RTALLOC_EFENCE_TRACE
+/** Spinlock protecting the all the block's globals. */
+static volatile uint32_t g_BlocksLock;
+/** Tree tracking the allocations. */
+static AVLPVTREE g_BlocksTree;
+# ifdef RTALLOC_EFENCE_FREE_DELAYED
+/** Tail of the delayed blocks. */
+static volatile PRTMEMBLOCK g_pBlocksDelayHead;
+/** Tail of the delayed blocks. */
+static volatile PRTMEMBLOCK g_pBlocksDelayTail;
+/** Number of bytes in the delay list (includes fences). */
+static volatile size_t g_cbBlocksDelay;
+# endif /* RTALLOC_EFENCE_FREE_DELAYED */
+# ifdef RTALLOC_REPLACE_MALLOC
+/** @name For calling the real allocation API we've replaced.
+ * @{ */
+void * (*g_pfnOrgMalloc)(size_t);
+void * (*g_pfnOrgCalloc)(size_t, size_t);
+void * (*g_pfnOrgRealloc)(void *, size_t);
+void (*g_pfnOrgFree)(void *);
+size_t (*g_pfnOrgMallocSize)(void *);
+/** @} */
+# endif
+#endif /* RTALLOC_EFENCE_TRACE */
+/** Array of pointers free watches for. */
+void *gapvRTMemFreeWatch[4] = {NULL, NULL, NULL, NULL};
+/** Enable logging of all freed memory. */
+bool gfRTMemFreeLog = false;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifdef RTALLOC_REPLACE_MALLOC
+static void rtMemReplaceMallocAndFriends(void);
+#endif
+
+
+/**
+ * Complains about something.
+ */
+static void rtmemComplain(const char *pszOp, const char *pszFormat, ...)
+{
+ va_list args;
+ fprintf(stderr, "RTMem error: %s: ", pszOp);
+ va_start(args, pszFormat);
+ vfprintf(stderr, pszFormat, args);
+ va_end(args);
+ RTAssertDoPanic();
+}
+
+/**
+ * Log an event.
+ */
+DECLINLINE(void) rtmemLog(const char *pszOp, const char *pszFormat, ...)
+{
+#if 0
+ va_list args;
+ fprintf(stderr, "RTMem info: %s: ", pszOp);
+ va_start(args, pszFormat);
+ vfprintf(stderr, pszFormat, args);
+ va_end(args);
+#else
+ NOREF(pszOp); NOREF(pszFormat);
+#endif
+}
+
+
+#ifdef RTALLOC_EFENCE_TRACE
+
+/**
+ * Acquires the lock.
+ */
+DECLINLINE(void) rtmemBlockLock(void)
+{
+ unsigned c = 0;
+ while (!ASMAtomicCmpXchgU32(&g_BlocksLock, 1, 0))
+ RTThreadSleepNoLog(((++c) >> 2) & 31);
+}
+
+
+/**
+ * Releases the lock.
+ */
+DECLINLINE(void) rtmemBlockUnlock(void)
+{
+ Assert(g_BlocksLock == 1);
+ ASMAtomicXchgU32(&g_BlocksLock, 0);
+}
+
+
+/**
+ * Creates a block.
+ */
+DECLINLINE(PRTMEMBLOCK) rtmemBlockCreate(RTMEMTYPE enmType, size_t cbUnaligned, size_t cbAligned,
+ const char *pszTag, void *pvCaller, RT_SRC_POS_DECL)
+{
+# ifdef RTALLOC_REPLACE_MALLOC
+ if (!g_pfnOrgMalloc)
+ rtMemReplaceMallocAndFriends();
+ PRTMEMBLOCK pBlock = (PRTMEMBLOCK)g_pfnOrgMalloc(sizeof(*pBlock));
+# else
+ PRTMEMBLOCK pBlock = (PRTMEMBLOCK)malloc(sizeof(*pBlock));
+# endif
+ if (pBlock)
+ {
+ pBlock->enmType = enmType;
+ pBlock->cbUnaligned = cbUnaligned;
+ pBlock->cbAligned = cbAligned;
+ pBlock->pszTag = pszTag;
+ pBlock->pvCaller = pvCaller;
+ pBlock->iLine = iLine;
+ pBlock->pszFile = pszFile;
+ pBlock->pszFunction = pszFunction;
+ }
+ return pBlock;
+}
+
+
+/**
+ * Frees a block.
+ */
+DECLINLINE(void) rtmemBlockFree(PRTMEMBLOCK pBlock)
+{
+# ifdef RTALLOC_REPLACE_MALLOC
+ g_pfnOrgFree(pBlock);
+# else
+ free(pBlock);
+# endif
+}
+
+
+/**
+ * Insert a block from the tree.
+ */
+DECLINLINE(void) rtmemBlockInsert(PRTMEMBLOCK pBlock, void *pv)
+{
+ pBlock->Core.Key = pv;
+ rtmemBlockLock();
+ bool fRc = RTAvlPVInsert(&g_BlocksTree, &pBlock->Core);
+ rtmemBlockUnlock();
+ AssertRelease(fRc);
+}
+
+
+/**
+ * Remove a block from the tree and returns it to the caller.
+ */
+DECLINLINE(PRTMEMBLOCK) rtmemBlockRemove(void *pv)
+{
+ rtmemBlockLock();
+ PRTMEMBLOCK pBlock = (PRTMEMBLOCK)RTAvlPVRemove(&g_BlocksTree, pv);
+ rtmemBlockUnlock();
+ return pBlock;
+}
+
+/**
+ * Gets a block.
+ */
+DECLINLINE(PRTMEMBLOCK) rtmemBlockGet(void *pv)
+{
+ rtmemBlockLock();
+ PRTMEMBLOCK pBlock = (PRTMEMBLOCK)RTAvlPVGet(&g_BlocksTree, pv);
+ rtmemBlockUnlock();
+ return pBlock;
+}
+
+/**
+ * Dumps one allocation.
+ */
+static DECLCALLBACK(int) RTMemDumpOne(PAVLPVNODECORE pNode, void *pvUser)
+{
+ PRTMEMBLOCK pBlock = (PRTMEMBLOCK)pNode;
+ fprintf(stderr, "%p %08lx(+%02lx) %p\n",
+ pBlock->Core.Key,
+ (unsigned long)pBlock->cbUnaligned,
+ (unsigned long)(pBlock->cbAligned - pBlock->cbUnaligned),
+ pBlock->pvCaller);
+ NOREF(pvUser);
+ return 0;
+}
+
+/**
+ * Dumps the allocated blocks.
+ * This is something which you should call from gdb.
+ */
+extern "C" void RTMemDump(void);
+void RTMemDump(void)
+{
+ fprintf(stderr, "address size(alg) caller\n");
+ RTAvlPVDoWithAll(&g_BlocksTree, true, RTMemDumpOne, NULL);
+}
+
+# ifdef RTALLOC_EFENCE_FREE_DELAYED
+
+/**
+ * Insert a delayed block.
+ */
+DECLINLINE(void) rtmemBlockDelayInsert(PRTMEMBLOCK pBlock)
+{
+ size_t cbBlock = RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) + RTALLOC_EFENCE_SIZE;
+ pBlock->Core.pRight = NULL;
+ pBlock->Core.pLeft = NULL;
+ rtmemBlockLock();
+ if (g_pBlocksDelayHead)
+ {
+ g_pBlocksDelayHead->Core.pLeft = (PAVLPVNODECORE)pBlock;
+ pBlock->Core.pRight = (PAVLPVNODECORE)g_pBlocksDelayHead;
+ g_pBlocksDelayHead = pBlock;
+ }
+ else
+ {
+ g_pBlocksDelayTail = pBlock;
+ g_pBlocksDelayHead = pBlock;
+ }
+ g_cbBlocksDelay += cbBlock;
+ rtmemBlockUnlock();
+}
+
+/**
+ * Removes a delayed block.
+ */
+DECLINLINE(PRTMEMBLOCK) rtmemBlockDelayRemove(void)
+{
+ PRTMEMBLOCK pBlock = NULL;
+ rtmemBlockLock();
+ if (g_cbBlocksDelay > RTALLOC_EFENCE_FREE_DELAYED)
+ {
+ pBlock = g_pBlocksDelayTail;
+ if (pBlock)
+ {
+ g_pBlocksDelayTail = (PRTMEMBLOCK)pBlock->Core.pLeft;
+ if (pBlock->Core.pLeft)
+ pBlock->Core.pLeft->pRight = NULL;
+ else
+ g_pBlocksDelayHead = NULL;
+ g_cbBlocksDelay -= RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) + RTALLOC_EFENCE_SIZE;
+ }
+ }
+ rtmemBlockUnlock();
+ return pBlock;
+}
+
+
+/**
+ * Dumps the freed blocks.
+ * This is something which you should call from gdb.
+ */
+extern "C" void RTMemDumpFreed(void);
+void RTMemDumpFreed(void)
+{
+ fprintf(stderr, "address size(alg) caller\n");
+ for (PRTMEMBLOCK pCur = g_pBlocksDelayHead; pCur; pCur = (PRTMEMBLOCK)pCur->Core.pRight)
+ RTMemDumpOne(&pCur->Core, NULL);
+
+}
+
+# endif /* RTALLOC_EFENCE_FREE_DELAYED */
+
+#endif /* RTALLOC_EFENCE_TRACE */
+
+
+#if defined(RTALLOC_REPLACE_MALLOC) && defined(RTALLOC_EFENCE_TRACE)
+/*
+ *
+ * Replacing malloc, calloc, realloc, & free.
+ *
+ */
+
+/** Replacement for malloc. */
+static void *rtMemReplacementMalloc(size_t cb)
+{
+ size_t cbAligned = RTMEM_REPLACMENT_ALIGN(cb);
+ void *pv = rtR3MemAlloc("r-malloc", RTMEMTYPE_RTMEMALLOC, cb, cbAligned, "heap", ASMReturnAddress(), RT_SRC_POS);
+ if (!pv)
+ pv = g_pfnOrgMalloc(cb);
+ return pv;
+}
+
+/** Replacement for calloc. */
+static void *rtMemReplacementCalloc(size_t cbItem, size_t cItems)
+{
+ size_t cb = cbItem * cItems;
+ size_t cbAligned = RTMEM_REPLACMENT_ALIGN(cb);
+ void *pv = rtR3MemAlloc("r-calloc", RTMEMTYPE_RTMEMALLOCZ, cb, cbAligned, "heap", ASMReturnAddress(), RT_SRC_POS);
+ if (!pv)
+ pv = g_pfnOrgCalloc(cbItem, cItems);
+ return pv;
+}
+
+/** Replacement for realloc. */
+static void *rtMemReplacementRealloc(void *pvOld, size_t cbNew)
+{
+ if (pvOld)
+ {
+ /* We're not strict about where the memory was allocated. */
+ PRTMEMBLOCK pBlock = rtmemBlockGet(pvOld);
+ if (pBlock)
+ {
+ size_t cbAligned = RTMEM_REPLACMENT_ALIGN(cbNew);
+ return rtR3MemRealloc("r-realloc", RTMEMTYPE_RTMEMREALLOC, pvOld, cbAligned, "heap", ASMReturnAddress(), RT_SRC_POS);
+ }
+ return g_pfnOrgRealloc(pvOld, cbNew);
+ }
+ return rtMemReplacementMalloc(cbNew);
+}
+
+/** Replacement for free(). */
+static void rtMemReplacementFree(void *pv)
+{
+ if (pv)
+ {
+ /* We're not strict about where the memory was allocated. */
+ PRTMEMBLOCK pBlock = rtmemBlockGet(pv);
+ if (pBlock)
+ rtR3MemFree("r-free", RTMEMTYPE_RTMEMFREE, pv, 0, ASMReturnAddress(), RT_SRC_POS);
+ else
+ g_pfnOrgFree(pv);
+ }
+}
+
+# ifdef RT_OS_DARWIN
+/** Replacement for malloc. */
+static size_t rtMemReplacementMallocSize(void *pv)
+{
+ size_t cb;
+ if (pv)
+ {
+ /* We're not strict about where the memory was allocated. */
+ PRTMEMBLOCK pBlock = rtmemBlockGet(pv);
+ if (pBlock)
+ cb = pBlock->cbUnaligned;
+ else
+ cb = g_pfnOrgMallocSize(pv);
+ }
+ else
+ cb = 0;
+ return cb;
+}
+# endif
+
+
+static void rtMemReplaceMallocAndFriends(void)
+{
+ struct
+ {
+ const char *pszName;
+ PFNRT pfnReplacement;
+ PFNRT pfnOrg;
+ PFNRT *ppfnJumpBack;
+ } aApis[] =
+ {
+ { "free", (PFNRT)rtMemReplacementFree, (PFNRT)free, (PFNRT *)&g_pfnOrgFree },
+ { "realloc", (PFNRT)rtMemReplacementRealloc, (PFNRT)realloc, (PFNRT *)&g_pfnOrgRealloc },
+ { "calloc", (PFNRT)rtMemReplacementCalloc, (PFNRT)calloc, (PFNRT *)&g_pfnOrgCalloc },
+ { "malloc", (PFNRT)rtMemReplacementMalloc, (PFNRT)malloc, (PFNRT *)&g_pfnOrgMalloc },
+#ifdef RT_OS_DARWIN
+ { "malloc_size", (PFNRT)rtMemReplacementMallocSize, (PFNRT)malloc_size, (PFNRT *)&g_pfnOrgMallocSize },
+#endif
+ };
+
+ /*
+ * Initialize the jump backs to avoid recursivly entering this function.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(aApis); i++)
+ *aApis[i].ppfnJumpBack = aApis[i].pfnOrg;
+
+ /*
+ * Give the user an option to skip replacing malloc.
+ */
+ if (getenv("IPRT_DONT_REPLACE_MALLOC"))
+ return;
+
+ /*
+ * Allocate a page for jump back code (we leak it).
+ */
+ uint8_t *pbExecPage = (uint8_t *)RTMemPageAlloc(PAGE_SIZE); AssertFatal(pbExecPage);
+ int rc = RTMemProtect(pbExecPage, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC); AssertFatalRC(rc);
+
+ /*
+ * Do the ground work.
+ */
+ uint8_t *pb = pbExecPage;
+ for (unsigned i = 0; i < RT_ELEMENTS(aApis); i++)
+ {
+ /* Resolve it. */
+ PFNRT pfnOrg = (PFNRT)(uintptr_t)dlsym(RTLD_DEFAULT, aApis[i].pszName);
+ if (pfnOrg)
+ aApis[i].pfnOrg = pfnOrg;
+ else
+ pfnOrg = aApis[i].pfnOrg;
+
+ /* Figure what we can replace and how much to duplicate in the jump back code. */
+# ifdef RT_ARCH_AMD64
+ uint32_t cbNeeded = 12;
+ DISCPUMODE const enmCpuMode = DISCPUMODE_64BIT;
+# elif defined(RT_ARCH_X86)
+ uint32_t const cbNeeded = 5;
+ DISCPUMODE const enmCpuMode = DISCPUMODE_32BIT;
+# else
+# error "Port me"
+# endif
+ uint32_t offJmpBack = 0;
+ uint32_t cbCopy = 0;
+ while (offJmpBack < cbNeeded)
+ {
+ DISCPUSTATE Dis;
+ uint32_t cbInstr = 1;
+ rc = DISInstr((void *)((uintptr_t)pfnOrg + offJmpBack), enmCpuMode, &Dis, &cbInstr); AssertFatalRC(rc);
+ AssertFatal(!(Dis.pCurInstr->fOpType & (DISOPTYPE_CONTROLFLOW)));
+# ifdef RT_ARCH_AMD64
+# ifdef RT_OS_DARWIN
+ /* Kludge for: cmp [malloc_def_zone_state], 1; jg 2; call _malloc_initialize; 2: */
+ if ( Dis.ModRM.Bits.Mod == 0
+ && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */
+ && (Dis.Param2.fUse & (DISUSE_IMMEDIATE16_SX8 | DISUSE_IMMEDIATE32_SX8 | DISUSE_IMMEDIATE64_SX8))
+ && Dis.Param2.uValue == 1
+ && Dis.pCurInstr->uOpcode == OP_CMP)
+ {
+ cbCopy = offJmpBack;
+
+ offJmpBack += cbInstr;
+ rc = DISInstr((void *)((uintptr_t)pfnOrg + offJmpBack), enmCpuMode, &Dis, &cbInstr); AssertFatalRC(rc);
+ if ( Dis.pCurInstr->uOpcode == OP_JNBE
+ && Dis.Param1.uDisp.i8 == 5)
+ {
+ offJmpBack += cbInstr + 5;
+ AssertFatal(offJmpBack >= cbNeeded);
+ break;
+ }
+ }
+# endif
+ AssertFatal(!(Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */));
+# endif
+ offJmpBack += cbInstr;
+ }
+ if (!cbCopy)
+ cbCopy = offJmpBack;
+
+ /* Assemble the jump back. */
+ memcpy(pb, (void *)(uintptr_t)pfnOrg, cbCopy);
+ uint32_t off = cbCopy;
+# ifdef RT_ARCH_AMD64
+ pb[off++] = 0xff; /* jmp qword [$+8 wrt RIP] */
+ pb[off++] = 0x25;
+ *(uint32_t *)&pb[off] = 0;
+ off += 4;
+ *(uint64_t *)&pb[off] = (uintptr_t)pfnOrg + offJmpBack;
+ off += 8;
+ off = RT_ALIGN_32(off, 16);
+# elif defined(RT_ARCH_X86)
+ pb[off++] = 0xe9; /* jmp rel32 */
+ *(uint32_t *)&pb[off] = (uintptr_t)pfnOrg + offJmpBack - (uintptr_t)&pb[4];
+ off += 4;
+ off = RT_ALIGN_32(off, 8);
+# else
+# error "Port me"
+# endif
+ *aApis[i].ppfnJumpBack = (PFNRT)(uintptr_t)pb;
+ pb += off;
+ }
+
+ /*
+ * Modify the APIs.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(aApis); i++)
+ {
+ pb = (uint8_t *)(uintptr_t)aApis[i].pfnOrg;
+ rc = RTMemProtect(pb, 16, RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC); AssertFatalRC(rc);
+
+# ifdef RT_ARCH_AMD64
+ /* Assemble the LdrLoadDll patch. */
+ *pb++ = 0x48; /* mov rax, qword */
+ *pb++ = 0xb8;
+ *(uint64_t *)pb = (uintptr_t)aApis[i].pfnReplacement;
+ pb += 8;
+ *pb++ = 0xff; /* jmp rax */
+ *pb++ = 0xe0;
+# elif defined(RT_ARCH_X86)
+ *pb++ = 0xe9; /* jmp rel32 */
+ *(uint32_t *)pb = (uintptr_t)aApis[i].pfnReplacement - (uintptr_t)&pb[4];
+# else
+# error "Port me"
+# endif
+ }
+}
+
+#endif /* RTALLOC_REPLACE_MALLOC && RTALLOC_EFENCE_TRACE */
+
+
+/**
+ * Internal allocator.
+ */
+RTDECL(void *) rtR3MemAlloc(const char *pszOp, RTMEMTYPE enmType, size_t cbUnaligned, size_t cbAligned,
+ const char *pszTag, void *pvCaller, RT_SRC_POS_DECL)
+{
+ /*
+ * Sanity.
+ */
+ if ( RT_ALIGN_Z(RTALLOC_EFENCE_SIZE, PAGE_SIZE) != RTALLOC_EFENCE_SIZE
+ && RTALLOC_EFENCE_SIZE <= 0)
+ {
+ rtmemComplain(pszOp, "Invalid E-fence size! %#x\n", RTALLOC_EFENCE_SIZE);
+ return NULL;
+ }
+ if (!cbUnaligned)
+ {
+#if 0
+ rtmemComplain(pszOp, "Request of ZERO bytes allocation!\n");
+ return NULL;
+#else
+ cbAligned = cbUnaligned = 1;
+#endif
+ }
+
+#ifndef RTALLOC_EFENCE_IN_FRONT
+ /* Alignment decreases fence accuracy, but this is at least partially
+ * counteracted by filling and checking the alignment padding. When the
+ * fence is in front then then no extra alignment is needed. */
+ cbAligned = RT_ALIGN_Z(cbAligned, RTALLOC_EFENCE_ALIGNMENT);
+#endif
+
+#ifdef RTALLOC_EFENCE_TRACE
+ /*
+ * Allocate the trace block.
+ */
+ PRTMEMBLOCK pBlock = rtmemBlockCreate(enmType, cbUnaligned, cbAligned, pszTag, pvCaller, RT_SRC_POS_ARGS);
+ if (!pBlock)
+ {
+ rtmemComplain(pszOp, "Failed to allocate trace block!\n");
+ return NULL;
+ }
+#endif
+
+ /*
+ * Allocate a block with page alignment space + the size of the E-fence.
+ */
+ size_t cbBlock = RT_ALIGN_Z(cbAligned, PAGE_SIZE) + RTALLOC_EFENCE_SIZE;
+ void *pvBlock = RTMemPageAlloc(cbBlock);
+ if (pvBlock)
+ {
+ /*
+ * Calc the start of the fence and the user block
+ * and then change the page protection of the fence.
+ */
+#ifdef RTALLOC_EFENCE_IN_FRONT
+ void *pvEFence = pvBlock;
+ void *pv = (char *)pvEFence + RTALLOC_EFENCE_SIZE;
+# ifdef RTALLOC_EFENCE_NOMAN_FILLER
+ memset((char *)pv + cbUnaligned, RTALLOC_EFENCE_NOMAN_FILLER, cbBlock - RTALLOC_EFENCE_SIZE - cbUnaligned);
+# endif
+#else
+ void *pvEFence = (char *)pvBlock + (cbBlock - RTALLOC_EFENCE_SIZE);
+ void *pv = (char *)pvEFence - cbAligned;
+# ifdef RTALLOC_EFENCE_NOMAN_FILLER
+ memset(pvBlock, RTALLOC_EFENCE_NOMAN_FILLER, cbBlock - RTALLOC_EFENCE_SIZE - cbAligned);
+ memset((char *)pv + cbUnaligned, RTALLOC_EFENCE_NOMAN_FILLER, cbAligned - cbUnaligned);
+# endif
+#endif
+
+#ifdef RTALLOC_EFENCE_FENCE_FILLER
+ memset(pvEFence, RTALLOC_EFENCE_FENCE_FILLER, RTALLOC_EFENCE_SIZE);
+#endif
+ int rc = RTMemProtect(pvEFence, RTALLOC_EFENCE_SIZE, RTMEM_PROT_NONE);
+ if (!rc)
+ {
+#ifdef RTALLOC_EFENCE_TRACE
+ rtmemBlockInsert(pBlock, pv);
+#endif
+ if (enmType == RTMEMTYPE_RTMEMALLOCZ)
+ memset(pv, 0, cbUnaligned);
+#ifdef RTALLOC_EFENCE_FILLER
+ else
+ memset(pv, RTALLOC_EFENCE_FILLER, cbUnaligned);
+#endif
+
+ rtmemLog(pszOp, "returns %p (pvBlock=%p cbBlock=%#x pvEFence=%p cbUnaligned=%#x)\n", pv, pvBlock, cbBlock, pvEFence, cbUnaligned);
+ return pv;
+ }
+ rtmemComplain(pszOp, "RTMemProtect failed, pvEFence=%p size %d, rc=%d\n", pvEFence, RTALLOC_EFENCE_SIZE, rc);
+ RTMemPageFree(pvBlock, cbBlock);
+ }
+ else
+ rtmemComplain(pszOp, "Failed to allocated %lu (%lu) bytes.\n", (unsigned long)cbBlock, (unsigned long)cbUnaligned);
+
+#ifdef RTALLOC_EFENCE_TRACE
+ rtmemBlockFree(pBlock);
+#endif
+ return NULL;
+}
+
+
+/**
+ * Internal free.
+ */
+RTDECL(void) rtR3MemFree(const char *pszOp, RTMEMTYPE enmType, void *pv, size_t cbUser, void *pvCaller, RT_SRC_POS_DECL)
+{
+ NOREF(enmType); RT_SRC_POS_NOREF();
+
+ /*
+ * Simple case.
+ */
+ if (!pv)
+ return;
+
+ /*
+ * Check watch points.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(gapvRTMemFreeWatch); i++)
+ if (gapvRTMemFreeWatch[i] == pv)
+ RTAssertDoPanic();
+
+#ifdef RTALLOC_EFENCE_TRACE
+ /*
+ * Find the block.
+ */
+ PRTMEMBLOCK pBlock = rtmemBlockRemove(pv);
+ if (pBlock)
+ {
+ if (gfRTMemFreeLog)
+ RTLogPrintf("RTMem %s: pv=%p pvCaller=%p cbUnaligned=%#x\n", pszOp, pv, pvCaller, pBlock->cbUnaligned);
+
+# ifdef RTALLOC_EFENCE_NOMAN_FILLER
+ /*
+ * Check whether the no man's land is untouched.
+ */
+# ifdef RTALLOC_EFENCE_IN_FRONT
+ void *pvWrong = ASMMemFirstMismatchingU8((char *)pv + pBlock->cbUnaligned,
+ RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) - pBlock->cbUnaligned,
+ RTALLOC_EFENCE_NOMAN_FILLER);
+# else
+ /* Alignment must match allocation alignment in rtMemAlloc(). */
+ void *pvWrong = ASMMemFirstMismatchingU8((char *)pv + pBlock->cbUnaligned,
+ pBlock->cbAligned - pBlock->cbUnaligned,
+ RTALLOC_EFENCE_NOMAN_FILLER);
+ if (pvWrong)
+ RTAssertDoPanic();
+ pvWrong = ASMMemFirstMismatchingU8((void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK),
+ RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) - pBlock->cbAligned,
+ RTALLOC_EFENCE_NOMAN_FILLER);
+# endif
+ if (pvWrong)
+ RTAssertDoPanic();
+# endif
+
+ /*
+ * Fill the user part of the block.
+ */
+ AssertMsg(enmType != RTMEMTYPE_RTMEMFREEZ || cbUser == pBlock->cbUnaligned,
+ ("cbUser=%#zx cbUnaligned=%#zx\n", cbUser, pBlock->cbUnaligned));
+ RT_NOREF(cbUser);
+ if (enmType == RTMEMTYPE_RTMEMFREEZ)
+ RT_BZERO(pv, pBlock->cbUnaligned);
+# ifdef RTALLOC_EFENCE_FREE_FILL
+ else
+ memset(pv, RTALLOC_EFENCE_FREE_FILL, pBlock->cbUnaligned);
+# endif
+
+# if defined(RTALLOC_EFENCE_FREE_DELAYED) && RTALLOC_EFENCE_FREE_DELAYED > 0
+ /*
+ * We're doing delayed freeing.
+ * That means we'll expand the E-fence to cover the entire block.
+ */
+ int rc = RTMemProtect(pv, pBlock->cbAligned, RTMEM_PROT_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Insert it into the free list and process pending frees.
+ */
+ rtmemBlockDelayInsert(pBlock);
+ while ((pBlock = rtmemBlockDelayRemove()) != NULL)
+ {
+ pv = pBlock->Core.Key;
+# ifdef RTALLOC_EFENCE_IN_FRONT
+ void *pvBlock = (char *)pv - RTALLOC_EFENCE_SIZE;
+# else
+ void *pvBlock = (void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK);
+# endif
+ size_t cbBlock = RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) + RTALLOC_EFENCE_SIZE;
+ rc = RTMemProtect(pvBlock, cbBlock, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ if (RT_SUCCESS(rc))
+ RTMemPageFree(pvBlock, RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) + RTALLOC_EFENCE_SIZE);
+ else
+ rtmemComplain(pszOp, "RTMemProtect(%p, %#x, RTMEM_PROT_READ | RTMEM_PROT_WRITE) -> %d\n", pvBlock, cbBlock, rc);
+ rtmemBlockFree(pBlock);
+ }
+ }
+ else
+ rtmemComplain(pszOp, "Failed to expand the efence of pv=%p cb=%d, rc=%d.\n", pv, pBlock, rc);
+
+# else /* !RTALLOC_EFENCE_FREE_DELAYED */
+
+ /*
+ * Turn of the E-fence and free it.
+ */
+# ifdef RTALLOC_EFENCE_IN_FRONT
+ void *pvBlock = (char *)pv - RTALLOC_EFENCE_SIZE;
+ void *pvEFence = pvBlock;
+# else
+ void *pvBlock = (void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK);
+ void *pvEFence = (char *)pv + pBlock->cb;
+# endif
+ int rc = RTMemProtect(pvEFence, RTALLOC_EFENCE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ if (RT_SUCCESS(rc))
+ RTMemPageFree(pvBlock, RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) + RTALLOC_EFENCE_SIZE);
+ else
+ rtmemComplain(pszOp, "RTMemProtect(%p, %#x, RTMEM_PROT_READ | RTMEM_PROT_WRITE) -> %d\n", pvEFence, RTALLOC_EFENCE_SIZE, rc);
+ rtmemBlockFree(pBlock);
+
+# endif /* !RTALLOC_EFENCE_FREE_DELAYED */
+ }
+ else
+ rtmemComplain(pszOp, "pv=%p not found! Incorrect free!\n", pv);
+
+#else /* !RTALLOC_EFENCE_TRACE */
+
+ /*
+ * We have no size tracking, so we're not doing any freeing because
+ * we cannot if the E-fence is after the block.
+ * Let's just expand the E-fence to the first page of the user bit
+ * since we know that it's around.
+ */
+ if (enmType == RTMEMTYPE_RTMEMFREEZ)
+ RT_BZERO(pv, cbUser);
+ int rc = RTMemProtect((void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK), PAGE_SIZE, RTMEM_PROT_NONE);
+ if (RT_FAILURE(rc))
+ rtmemComplain(pszOp, "RTMemProtect(%p, PAGE_SIZE, RTMEM_PROT_NONE) -> %d\n", (void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK), rc);
+#endif /* !RTALLOC_EFENCE_TRACE */
+}
+
+
+/**
+ * Internal realloc.
+ */
+RTDECL(void *) rtR3MemRealloc(const char *pszOp, RTMEMTYPE enmType, void *pvOld, size_t cbNew,
+ const char *pszTag, void *pvCaller, RT_SRC_POS_DECL)
+{
+ /*
+ * Allocate new and copy.
+ */
+ if (!pvOld)
+ return rtR3MemAlloc(pszOp, enmType, cbNew, cbNew, pszTag, pvCaller, RT_SRC_POS_ARGS);
+ if (!cbNew)
+ {
+ rtR3MemFree(pszOp, RTMEMTYPE_RTMEMREALLOC, pvOld, 0, pvCaller, RT_SRC_POS_ARGS);
+ return NULL;
+ }
+
+#ifdef RTALLOC_EFENCE_TRACE
+
+ /*
+ * Get the block, allocate the new, copy the data, free the old one.
+ */
+ PRTMEMBLOCK pBlock = rtmemBlockGet(pvOld);
+ if (pBlock)
+ {
+ void *pvRet = rtR3MemAlloc(pszOp, enmType, cbNew, cbNew, pszTag, pvCaller, RT_SRC_POS_ARGS);
+ if (pvRet)
+ {
+ memcpy(pvRet, pvOld, RT_MIN(cbNew, pBlock->cbUnaligned));
+ rtR3MemFree(pszOp, RTMEMTYPE_RTMEMREALLOC, pvOld, 0, pvCaller, RT_SRC_POS_ARGS);
+ }
+ return pvRet;
+ }
+ else
+ rtmemComplain(pszOp, "pvOld=%p was not found!\n", pvOld);
+ return NULL;
+
+#else /* !RTALLOC_EFENCE_TRACE */
+
+ rtmemComplain(pszOp, "Not supported if RTALLOC_EFENCE_TRACE isn't defined!\n");
+ return NULL;
+
+#endif /* !RTALLOC_EFENCE_TRACE */
+}
+
+
+
+
+RTDECL(void *) RTMemEfTmpAlloc(size_t cb, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ return rtR3MemAlloc("TmpAlloc", RTMEMTYPE_RTMEMALLOC, cb, cb, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfTmpAllocZ(size_t cb, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ return rtR3MemAlloc("TmpAlloc", RTMEMTYPE_RTMEMALLOCZ, cb, cb, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void) RTMemEfTmpFree(void *pv, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR3MemFree("Free", RTMEMTYPE_RTMEMFREE, pv, 0, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void) RTMemEfTmpFreeZ(void *pv, size_t cb, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR3MemFree("FreeZ", RTMEMTYPE_RTMEMFREEZ, pv, cb, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfAlloc(size_t cb, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ return rtR3MemAlloc("Alloc", RTMEMTYPE_RTMEMALLOC, cb, cb, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfAllocZ(size_t cb, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ return rtR3MemAlloc("AllocZ", RTMEMTYPE_RTMEMALLOCZ, cb, cb, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfAllocVar(size_t cbUnaligned, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+ return rtR3MemAlloc("Alloc", RTMEMTYPE_RTMEMALLOC, cbUnaligned, cbAligned, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfAllocZVar(size_t cbUnaligned, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+ return rtR3MemAlloc("AllocZ", RTMEMTYPE_RTMEMALLOCZ, cbUnaligned, cbAligned, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfRealloc(void *pvOld, size_t cbNew, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ return rtR3MemRealloc("Realloc", RTMEMTYPE_RTMEMREALLOC, pvOld, cbNew, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfReallocZ(void *pvOld, size_t cbOld, size_t cbNew, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ void *pvDst = rtR3MemRealloc("ReallocZ", RTMEMTYPE_RTMEMREALLOC, pvOld, cbNew, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+ if (pvDst && cbNew > cbOld)
+ memset((uint8_t *)pvDst + cbOld, 0, cbNew - cbOld);
+ return pvDst;
+}
+
+
+RTDECL(void) RTMemEfFree(void *pv, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR3MemFree("Free", RTMEMTYPE_RTMEMFREE, pv, 0, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void) RTMemEfFreeZ(void *pv, size_t cb, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR3MemFree("FreeZ", RTMEMTYPE_RTMEMFREEZ, pv, cb, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfDup(const void *pvSrc, size_t cb, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ void *pvDst = RTMemEfAlloc(cb, pszTag, RT_SRC_POS_ARGS);
+ if (pvDst)
+ memcpy(pvDst, pvSrc, cb);
+ return pvDst;
+}
+
+
+RTDECL(void *) RTMemEfDupEx(const void *pvSrc, size_t cbSrc, size_t cbExtra, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ void *pvDst = RTMemEfAlloc(cbSrc + cbExtra, pszTag, RT_SRC_POS_ARGS);
+ if (pvDst)
+ {
+ memcpy(pvDst, pvSrc, cbSrc);
+ memset((uint8_t *)pvDst + cbSrc, 0, cbExtra);
+ }
+ return pvDst;
+}
+
+
+
+
+/*
+ *
+ * The NP (no position) versions.
+ *
+ */
+
+
+
+RTDECL(void *) RTMemEfTmpAllocNP(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtR3MemAlloc("TmpAlloc", RTMEMTYPE_RTMEMALLOC, cb, cb, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfTmpAllocZNP(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtR3MemAlloc("TmpAllocZ", RTMEMTYPE_RTMEMALLOCZ, cb, cb, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void) RTMemEfTmpFreeNP(void *pv) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR3MemFree("Free", RTMEMTYPE_RTMEMFREE, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void) RTMemEfTmpFreeZNP(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR3MemFree("FreeZ", RTMEMTYPE_RTMEMFREEZ, pv, cb, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfAllocNP(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtR3MemAlloc("Alloc", RTMEMTYPE_RTMEMALLOC, cb, cb, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfAllocZNP(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtR3MemAlloc("AllocZ", RTMEMTYPE_RTMEMALLOCZ, cb, cb, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfAllocVarNP(size_t cbUnaligned, const char *pszTag) RT_NO_THROW_DEF
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+ return rtR3MemAlloc("Alloc", RTMEMTYPE_RTMEMALLOC, cbUnaligned, cbAligned, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfAllocZVarNP(size_t cbUnaligned, const char *pszTag) RT_NO_THROW_DEF
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+ return rtR3MemAlloc("AllocZ", RTMEMTYPE_RTMEMALLOCZ, cbUnaligned, cbAligned, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfReallocNP(void *pvOld, size_t cbNew, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtR3MemRealloc("Realloc", RTMEMTYPE_RTMEMREALLOC, pvOld, cbNew, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfReallocZNP(void *pvOld, size_t cbOld, size_t cbNew, const char *pszTag) RT_NO_THROW_DEF
+{
+ void *pvDst = rtR3MemRealloc("ReallocZ", RTMEMTYPE_RTMEMREALLOC, pvOld, cbNew, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+ if (pvDst && cbNew > cbOld)
+ memset((uint8_t *)pvDst + cbOld, 0, cbNew - cbOld);
+ return pvDst;
+}
+
+
+RTDECL(void) RTMemEfFreeNP(void *pv) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR3MemFree("Free", RTMEMTYPE_RTMEMFREE, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void) RTMemEfFreeZNP(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR3MemFree("Free", RTMEMTYPE_RTMEMFREEZ, pv, cb, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfDupNP(const void *pvSrc, size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ void *pvDst = RTMemEfAlloc(cb, pszTag, NULL, 0, NULL);
+ if (pvDst)
+ memcpy(pvDst, pvSrc, cb);
+ return pvDst;
+}
+
+
+RTDECL(void *) RTMemEfDupExNP(const void *pvSrc, size_t cbSrc, size_t cbExtra, const char *pszTag) RT_NO_THROW_DEF
+{
+ void *pvDst = RTMemEfAlloc(cbSrc + cbExtra, pszTag, NULL, 0, NULL);
+ if (pvDst)
+ {
+ memcpy(pvDst, pvSrc, cbSrc);
+ memset((uint8_t *)pvDst + cbSrc, 0, cbExtra);
+ }
+ return pvDst;
+}
+
diff --git a/src/VBox/Runtime/r3/alloc-ef.h b/src/VBox/Runtime/r3/alloc-ef.h
new file mode 100644
index 00000000..c6536e7c
--- /dev/null
+++ b/src/VBox/Runtime/r3/alloc-ef.h
@@ -0,0 +1,231 @@
+/* $Id: alloc-ef.h $ */
+/** @file
+ * IPRT - Memory Allocation, electric fence.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_alloc_ef_h
+#define IPRT_INCLUDED_SRC_r3_alloc_ef_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+#if defined(DOXYGEN_RUNNING)
+# define RTALLOC_USE_EFENCE
+# define RTALLOC_EFENCE_IN_FRONT
+# define RTALLOC_EFENCE_FREE_FILL 'f'
+#endif
+
+/** @def RTALLOC_USE_EFENCE
+ * If defined the electric fence put up for ALL allocations by RTMemAlloc(),
+ * RTMemAllocZ(), RTMemRealloc(), RTMemTmpAlloc() and RTMemTmpAllocZ().
+ */
+#if 0
+# define RTALLOC_USE_EFENCE
+#endif
+
+/** @def RTALLOC_EFENCE_SIZE
+ * The size of the fence. This must be page aligned.
+ */
+#define RTALLOC_EFENCE_SIZE PAGE_SIZE
+
+/** @def RTALLOC_EFENCE_ALIGNMENT
+ * The allocation alignment, power of two of course.
+ *
+ * Use this for working around misaligned sizes, usually stemming from
+ * allocating a string or something after the main structure. When you
+ * encounter this, please fix the allocation to RTMemAllocVar or RTMemAllocZVar.
+ */
+#if 0
+# define RTALLOC_EFENCE_ALIGNMENT (ARCH_BITS / 8)
+#else
+# define RTALLOC_EFENCE_ALIGNMENT 1
+#endif
+
+/** @def RTALLOC_EFENCE_IN_FRONT
+ * Define this to put the fence up in front of the block.
+ * The default (when this isn't defined) is to up it up after the block.
+ */
+//# define RTALLOC_EFENCE_IN_FRONT
+
+/** @def RTALLOC_EFENCE_TRACE
+ * Define this to support actual free and reallocation of blocks.
+ */
+#define RTALLOC_EFENCE_TRACE
+
+/** @def RTALLOC_EFENCE_FREE_DELAYED
+ * This define will enable free() delay and protection of the freed data
+ * while it's being delayed. The value of RTALLOC_EFENCE_FREE_DELAYED defines
+ * the threshold of the delayed blocks.
+ * Delayed blocks does not consume any physical memory, only virtual address space.
+ * Requires RTALLOC_EFENCE_TRACE.
+ */
+#define RTALLOC_EFENCE_FREE_DELAYED (20 * _1M)
+
+/** @def RTALLOC_EFENCE_FREE_FILL
+ * This define will enable memset(,RTALLOC_EFENCE_FREE_FILL,)'ing the user memory
+ * in the block before freeing/decommitting it. This is useful in GDB since GDB
+ * appears to be able to read the content of the page even after it's been
+ * decommitted.
+ * Requires RTALLOC_EFENCE_TRACE.
+ */
+#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) || defined(DOXYGEN_RUNNING)
+# define RTALLOC_EFENCE_FREE_FILL 'f'
+#endif
+
+/** @def RTALLOC_EFENCE_FILLER
+ * This define will enable memset(,RTALLOC_EFENCE_FILLER,)'ing the allocated
+ * memory when the API doesn't require it to be zero'd.
+ */
+#define RTALLOC_EFENCE_FILLER 0xef
+
+/** @def RTALLOC_EFENCE_NOMAN_FILLER
+ * This define will enable memset(,RTALLOC_EFENCE_NOMAN_FILLER,)'ing the
+ * unprotected but not allocated area of memory, the so called no man's land.
+ */
+#define RTALLOC_EFENCE_NOMAN_FILLER 0xaa
+
+/** @def RTALLOC_EFENCE_FENCE_FILLER
+ * This define will enable memset(,RTALLOC_EFENCE_FENCE_FILLER,)'ing the
+ * fence itself, as debuggers can usually read them.
+ */
+#define RTALLOC_EFENCE_FENCE_FILLER 0xcc
+
+#if defined(DOXYGEN_RUNNING)
+/** @def RTALLOC_EFENCE_CPP
+ * This define will enable the new and delete wrappers.
+ */
+# define RTALLOC_EFENCE_CPP
+#endif
+
+#if defined(RUNNING_DOXYGEN)
+/** @def RTALLOC_REPLACE_MALLOC
+ * Replaces the malloc, calloc, realloc, free and friends in libc (experimental).
+ * Set in LocalConfig.kmk. Requires RTALLOC_EFENCE_TRACE to work. */
+# define RTALLOC_REPLACE_MALLOC
+#endif
+#if defined(RTALLOC_REPLACE_MALLOC) && !defined(RTALLOC_EFENCE_TRACE)
+# error "RTALLOC_REPLACE_MALLOC requires RTALLOC_EFENCE_TRACE."
+#endif
+
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+#else
+# include <sys/mman.h>
+#endif
+#include <iprt/avl.h>
+#include <iprt/thread.h>
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Allocation types.
+ */
+typedef enum RTMEMTYPE
+{
+ RTMEMTYPE_RTMEMALLOC,
+ RTMEMTYPE_RTMEMALLOCZ,
+ RTMEMTYPE_RTMEMREALLOC,
+ RTMEMTYPE_RTMEMFREE,
+ RTMEMTYPE_RTMEMFREEZ,
+
+ RTMEMTYPE_NEW,
+ RTMEMTYPE_NEW_ARRAY,
+ RTMEMTYPE_DELETE,
+ RTMEMTYPE_DELETE_ARRAY
+} RTMEMTYPE;
+
+#ifdef RTALLOC_EFENCE_TRACE
+/**
+ * Node tracking a memory allocation.
+ */
+typedef struct RTMEMBLOCK
+{
+ /** Avl node code, key is the user block pointer. */
+ AVLPVNODECORE Core;
+ /** Allocation type. */
+ RTMEMTYPE enmType;
+ /** The unaligned size of the block. */
+ size_t cbUnaligned;
+ /** The aligned size of the block. */
+ size_t cbAligned;
+ /** The allocation tag (read-only string). */
+ const char *pszTag;
+ /** The return address of the allocator function. */
+ void *pvCaller;
+ /** Line number of the alloc call. */
+ unsigned iLine;
+ /** File from within the allocation was made. */
+ const char *pszFile;
+ /** Function from within the allocation was made. */
+ const char *pszFunction;
+} RTMEMBLOCK, *PRTMEMBLOCK;
+
+#endif
+
+
+/*******************************************************************************
+* Internal Functions *
+******************************************************************************/
+RT_C_DECLS_BEGIN
+RTDECL(void *) rtR3MemAlloc(const char *pszOp, RTMEMTYPE enmType, size_t cbUnaligned, size_t cbAligned,
+ const char *pszTag, void *pvCaller, RT_SRC_POS_DECL);
+RTDECL(void *) rtR3MemRealloc(const char *pszOp, RTMEMTYPE enmType, void *pvOld, size_t cbNew,
+ const char *pszTag, void *pvCaller, RT_SRC_POS_DECL);
+RTDECL(void) rtR3MemFree(const char *pszOp, RTMEMTYPE enmType, void *pv, size_t cbUser, void *pvCaller, RT_SRC_POS_DECL);
+RT_C_DECLS_END
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+#ifdef RTALLOC_REPLACE_MALLOC
+RT_C_DECLS_BEGIN
+extern void * (*g_pfnOrgMalloc)(size_t);
+extern void * (*g_pfnOrgCalloc)(size_t, size_t);
+extern void * (*g_pfnOrgRealloc)(void *, size_t);
+extern void (*g_pfnOrgFree)(void *);
+RT_C_DECLS_END
+#endif
+
+#endif /* !IPRT_INCLUDED_SRC_r3_alloc_ef_h */
+
diff --git a/src/VBox/Runtime/r3/alloc.cpp b/src/VBox/Runtime/r3/alloc.cpp
new file mode 100644
index 00000000..015df135
--- /dev/null
+++ b/src/VBox/Runtime/r3/alloc.cpp
@@ -0,0 +1,317 @@
+/* $Id: alloc.cpp $ */
+/** @file
+ * IPRT - Memory Allocation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#if defined(RTMEM_WRAP_TO_EF_APIS) && !defined(RTMEM_NO_WRAP_TO_EF_APIS)
+# undef RTMEM_WRAP_TO_EF_APIS
+# define RTALLOC_USE_EFENCE 1
+#endif
+
+/*#define RTMEMALLOC_USE_TRACKER*/
+/* Don't enable the tracker when building the minimal IPRT. */
+#ifdef RT_MINI
+# undef RTMEMALLOC_USE_TRACKER
+#endif
+
+#if defined(RTMEMALLOC_USE_TRACKER) && defined(RTALLOC_USE_EFENCE)
+# error "Cannot define both RTMEMALLOC_USE_TRACKER and RTALLOC_USE_EFENCE!"
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "alloc-ef.h"
+#include <iprt/mem.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#ifdef RTMEMALLOC_USE_TRACKER
+# include <iprt/memtracker.h>
+#endif
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include "internal/mem.h"
+
+#include <stdlib.h>
+
+#undef RTMemTmpAlloc
+#undef RTMemTmpAllocTag
+#undef RTMemTmpAllocZ
+#undef RTMemTmpAllocZTag
+#undef RTMemTmpFree
+#undef RTMemTmpFreeZ
+#undef RTMemAlloc
+#undef RTMemAllocTag
+#undef RTMemAllocZ
+#undef RTMemAllocZTag
+#undef RTMemAllocVar
+#undef RTMemAllocVarTag
+#undef RTMemAllocZVar
+#undef RTMemAllocZVarTag
+#undef RTMemRealloc
+#undef RTMemReallocTag
+#undef RTMemFree
+#undef RTMemFreeZ
+#undef RTMemDup
+#undef RTMemDupTag
+#undef RTMemDupEx
+#undef RTMemDupExTag
+
+#undef RTALLOC_USE_EFENCE
+
+
+#ifdef IPRT_WITH_GCC_SANITIZER
+/**
+ * Checks if @a pszTag is a leak tag.
+ *
+ * @returns true if leak tag, false if not.
+ * @param pszTag Tage to inspect.
+ */
+DECLINLINE(bool) rtMemIsLeakTag(const char *pszTag)
+{
+ char ch = *pszTag;
+ if (ch != 'w')
+ { /* likely */ }
+ else
+ return pszTag[1] == 'i'
+ && pszTag[2] == 'l'
+ && pszTag[3] == 'l'
+ && pszTag[4] == '-'
+ && pszTag[5] == 'l'
+ && pszTag[6] == 'e'
+ && pszTag[7] == 'a'
+ && pszTag[8] == 'k';
+ if (ch != 'm')
+ return false;
+ return pszTag[1] == 'm'
+ && pszTag[2] == 'a'
+ && pszTag[3] == 'y'
+ && pszTag[4] == '-'
+ && pszTag[5] == 'l'
+ && pszTag[6] == 'e'
+ && pszTag[7] == 'a'
+ && pszTag[8] == 'k';
+}
+#endif /* IPRT_WITH_GCC_SANITIZER */
+
+
+RTDECL(void *) RTMemTmpAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return RTMemAllocTag(cb, pszTag);
+}
+
+
+RTDECL(void *) RTMemTmpAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return RTMemAllocZTag(cb, pszTag);
+}
+
+
+RTDECL(void) RTMemTmpFree(void *pv) RT_NO_THROW_DEF
+{
+ RTMemFree(pv);
+}
+
+
+RTDECL(void) RTMemTmpFreeZ(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ RTMemFreeZ(pv, cb);
+}
+
+
+RTDECL(void *) RTMemAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+#ifdef RTALLOC_USE_EFENCE
+ void *pv = rtR3MemAlloc("Alloc", RTMEMTYPE_RTMEMALLOC, cb, cb, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+
+#else /* !RTALLOC_USE_EFENCE */
+
+ AssertMsg(cb, ("Allocating ZERO bytes is really not a good idea! Good luck with the next assertion!\n"));
+# ifdef RTMEMALLOC_USE_TRACKER
+ void *pv = RTMemTrackerHdrAlloc(malloc(cb + sizeof(RTMEMTRACKERHDR)), cb, pszTag, ASMReturnAddress(), RTMEMTRACKERMETHOD_ALLOC);
+# else
+ void *pv = malloc(cb); NOREF(pszTag);
+# endif
+ AssertMsg(pv, ("malloc(%#zx) failed!!!\n", cb));
+ AssertMsg( cb < RTMEM_ALIGNMENT
+ || !((uintptr_t)pv & (RTMEM_ALIGNMENT - 1))
+ || ( (cb & RTMEM_ALIGNMENT) + ((uintptr_t)pv & RTMEM_ALIGNMENT)) == RTMEM_ALIGNMENT
+ , ("pv=%p RTMEM_ALIGNMENT=%#x\n", pv, RTMEM_ALIGNMENT));
+#endif /* !RTALLOC_USE_EFENCE */
+#ifdef IPRT_WITH_GCC_SANITIZER
+ if (rtMemIsLeakTag(pszTag))
+ __lsan_ignore_object(pv);
+#endif
+ return pv;
+}
+
+
+RTDECL(void *) RTMemAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+#ifdef RTALLOC_USE_EFENCE
+ void *pv = rtR3MemAlloc("AllocZ", RTMEMTYPE_RTMEMALLOCZ, cb, cb, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+
+#else /* !RTALLOC_USE_EFENCE */
+
+ AssertMsg(cb, ("Allocating ZERO bytes is really not a good idea! Good luck with the next assertion!\n"));
+
+# ifdef RTMEMALLOC_USE_TRACKER
+ void *pv = RTMemTrackerHdrAlloc(calloc(1, cb + sizeof(RTMEMTRACKERHDR)), cb, pszTag, ASMReturnAddress(), RTMEMTRACKERMETHOD_ALLOCZ);
+#else
+ void *pv = calloc(1, cb); NOREF(pszTag);
+#endif
+ AssertMsg(pv, ("calloc(1,%#zx) failed!!!\n", cb));
+ AssertMsg( cb < RTMEM_ALIGNMENT
+ || !((uintptr_t)pv & (RTMEM_ALIGNMENT - 1))
+ || ( (cb & RTMEM_ALIGNMENT) + ((uintptr_t)pv & RTMEM_ALIGNMENT)) == RTMEM_ALIGNMENT
+ , ("pv=%p RTMEM_ALIGNMENT=%#x\n", pv, RTMEM_ALIGNMENT));
+#endif /* !RTALLOC_USE_EFENCE */
+#ifdef IPRT_WITH_GCC_SANITIZER
+ if (rtMemIsLeakTag(pszTag))
+ __lsan_ignore_object(pv);
+#endif
+ return pv;
+}
+
+
+RTDECL(void *) RTMemAllocVarTag(size_t cbUnaligned, const char *pszTag) RT_NO_THROW_DEF
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+#ifdef RTALLOC_USE_EFENCE
+ void *pv = rtR3MemAlloc("AllocVar", RTMEMTYPE_RTMEMALLOC, cbUnaligned, cbAligned, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+#else
+ void *pv = RTMemAllocTag(cbAligned, pszTag);
+#endif
+ return pv;
+}
+
+
+RTDECL(void *) RTMemAllocZVarTag(size_t cbUnaligned, const char *pszTag) RT_NO_THROW_DEF
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+#ifdef RTALLOC_USE_EFENCE
+ void *pv = rtR3MemAlloc("AllocZVar", RTMEMTYPE_RTMEMALLOCZ, cbUnaligned, cbAligned, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+#else
+ void *pv = RTMemAllocZTag(cbAligned, pszTag);
+#endif
+ return pv;
+}
+
+
+RTDECL(void *) RTMemReallocTag(void *pvOld, size_t cbNew, const char *pszTag) RT_NO_THROW_DEF
+{
+#ifdef RTALLOC_USE_EFENCE
+ void *pv = rtR3MemRealloc("Realloc", RTMEMTYPE_RTMEMREALLOC, pvOld, cbNew, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+
+#else /* !RTALLOC_USE_EFENCE */
+# ifdef RT_STRICT
+ const uintptr_t uOld = (uintptr_t)pvOld; /* overzealous gcc 12 complains it's used over realloc */
+# endif
+
+# ifdef RTMEMALLOC_USE_TRACKER
+ void *pvRealOld = RTMemTrackerHdrReallocPrep(pvOld, 0, pszTag, ASMReturnAddress());
+ size_t cbRealNew = cbNew || !pvRealOld ? cbNew + sizeof(RTMEMTRACKERHDR) : 0;
+ void *pvNew = realloc(pvRealOld, cbRealNew);
+ void *pv = RTMemTrackerHdrReallocDone(pvNew, cbNew, pvOld, pszTag, ASMReturnAddress());
+# else
+ void *pv = realloc(pvOld, cbNew); NOREF(pszTag);
+# endif
+ AssertMsg(pv || !cbNew, ("realloc(%p, %#zx) failed!!!\n", uOld, cbNew));
+ AssertMsg( cbNew < RTMEM_ALIGNMENT
+ || !((uintptr_t)pv & (RTMEM_ALIGNMENT - 1))
+ || ( (cbNew & RTMEM_ALIGNMENT) + ((uintptr_t)pv & RTMEM_ALIGNMENT)) == RTMEM_ALIGNMENT
+ , ("pv=%p RTMEM_ALIGNMENT=%#x\n", pv, RTMEM_ALIGNMENT));
+#endif /* !RTALLOC_USE_EFENCE */
+ return pv;
+}
+
+
+RTDECL(void) RTMemFree(void *pv) RT_NO_THROW_DEF
+{
+ if (pv)
+#ifdef RTALLOC_USE_EFENCE
+ rtR3MemFree("Free", RTMEMTYPE_RTMEMFREE, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+#else
+# ifdef RTMEMALLOC_USE_TRACKER
+ pv = RTMemTrackerHdrFree(pv, 0, NULL, ASMReturnAddress(), RTMEMTRACKERMETHOD_FREE);
+# endif
+ free(pv);
+#endif
+}
+
+
+RTDECL(void) RTMemFreeZ(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ if (pv)
+ {
+#ifdef RTALLOC_USE_EFENCE
+ rtR3MemFree("Free", RTMEMTYPE_RTMEMFREEZ, pv, cb, ASMReturnAddress(), NULL, 0, NULL);
+#else
+# ifdef RTMEMALLOC_USE_TRACKER
+ pv = RTMemTrackerHdrFree(pv, cb, NULL, ASMReturnAddress(), RTMEMTRACKERMETHOD_FREE);
+# endif
+ RT_BZERO(pv, cb);
+ free(pv);
+#endif
+ }
+}
+
+
+
+DECLHIDDEN(void *) rtMemBaseAlloc(size_t cb)
+{
+ Assert(cb > 0 && cb < _1M);
+ return malloc(cb);
+}
+
+
+DECLHIDDEN(void) rtMemBaseFree(void *pv)
+{
+ free(pv);
+}
+
diff --git a/src/VBox/Runtime/r3/allocex.cpp b/src/VBox/Runtime/r3/allocex.cpp
new file mode 100644
index 00000000..64eb19f9
--- /dev/null
+++ b/src/VBox/Runtime/r3/allocex.cpp
@@ -0,0 +1,152 @@
+/* $Id: allocex.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Extended Alloc and Free Functions for Ring-3, posix.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTMEM_NO_WRAP_TO_EF_APIS
+#include <iprt/mem.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include "internal/magics.h"
+#include "allocex.h"
+
+
+RTDECL(int) RTMemAllocExTag(size_t cb, size_t cbAlignment, uint32_t fFlags, const char *pszTag, void **ppv) RT_NO_THROW_DEF
+{
+ RT_NOREF_PV(pszTag);
+
+ /*
+ * Validate and adjust input.
+ */
+ AssertMsgReturn(!(fFlags & ~RTMEMALLOCEX_FLAGS_VALID_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(RT_IS_POWER_OF_TWO(cbAlignment), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(cbAlignment <= sizeof(void *), ("%zu (%#x)\n", cbAlignment, cbAlignment), VERR_UNSUPPORTED_ALIGNMENT);
+
+ if (fFlags & RTMEMALLOCEX_FLAGS_ANY_CTX)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Align the request.
+ */
+ size_t cbAligned = cb;
+ if (cbAlignment)
+ cbAligned = RT_ALIGN_Z(cb, cbAlignment);
+ else
+ cbAligned = RT_ALIGN_Z(cb, sizeof(uint64_t));
+ AssertMsgReturn(cbAligned >= cb && cbAligned <= ~(size_t)0, ("cbAligned=%#zx cb=%#zx", cbAligned, cb),
+ VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate the requested memory.
+ */
+ void *pv;
+ if (fFlags & (RTMEMALLOCEX_FLAGS_16BIT_REACH | RTMEMALLOCEX_FLAGS_32BIT_REACH))
+ {
+ int rc;
+ if (fFlags & RTMEMALLOCEX_FLAGS_16BIT_REACH)
+ rc = rtMemAllocEx16BitReach(cbAligned + sizeof(RTMEMHDRR3), fFlags, &pv);
+ else
+ rc = rtMemAllocEx32BitReach(cbAligned + sizeof(RTMEMHDRR3), fFlags, &pv);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ else if (fFlags & RTMEMALLOCEX_FLAGS_EXEC)
+ {
+ pv = RTMemPageAlloc(cbAligned + sizeof(RTMEMHDRR3));
+ if (pv)
+ {
+ if (fFlags & RTMEMALLOCEX_FLAGS_ZEROED)
+ RT_BZERO(pv, cbAligned + sizeof(RTMEMHDRR3));
+
+ int rc = RTMemProtect(pv, cbAligned + sizeof(RTMEMHDRR3), RTMEM_PROT_EXEC | RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ if (RT_FAILURE(rc))
+ {
+ RTMemPageFree(pv, cbAligned + sizeof(RTMEMHDRR3));
+ return rc;
+ }
+ }
+ }
+ else if (fFlags & RTMEMALLOCEX_FLAGS_ZEROED)
+ pv = RTMemAllocZ(cbAligned + sizeof(RTMEMHDRR3));
+ else
+ pv = RTMemAlloc(cbAligned + sizeof(RTMEMHDRR3));
+ if (!pv)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Fill in the header and return.
+ */
+ PRTMEMHDRR3 pHdr = (PRTMEMHDRR3)pv;
+ pHdr->u32Magic = RTMEMHDR_MAGIC;
+ pHdr->fFlags = fFlags;
+ pHdr->cb = (uint32_t)cbAligned;
+ pHdr->cbReq = (uint32_t)cb;
+
+ *ppv = pHdr + 1;
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMemAllocExTag);
+
+
+RTDECL(void) RTMemFreeEx(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ if (!pv)
+ return;
+ AssertPtr(pv);
+
+ PRTMEMHDRR3 pHdr = (PRTMEMHDRR3)pv - 1;
+ AssertMsg(pHdr->u32Magic == RTMEMHDR_MAGIC, ("pHdr->u32Magic=%RX32 pv=%p cb=%#x\n", pHdr->u32Magic, pv, cb));
+ pHdr->u32Magic = RTMEMHDR_MAGIC_DEAD;
+ Assert(pHdr->cbReq == cb); RT_NOREF_PV(cb);
+
+ if (pHdr->fFlags & (RTMEMALLOCEX_FLAGS_16BIT_REACH | RTMEMALLOCEX_FLAGS_32BIT_REACH))
+ rtMemFreeExYyBitReach(pHdr, pHdr->cb + sizeof(*pHdr), pHdr->fFlags);
+ else if (pHdr->fFlags & RTMEMALLOCEX_FLAGS_EXEC)
+ {
+ RTMemProtect(pHdr, pHdr->cb + sizeof(*pHdr), RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ RTMemPageFree(pHdr, pHdr->cb + sizeof(*pHdr));
+ }
+ else
+ RTMemFree(pHdr);
+}
+RT_EXPORT_SYMBOL(RTMemFreeEx);
+
diff --git a/src/VBox/Runtime/r3/allocex.h b/src/VBox/Runtime/r3/allocex.h
new file mode 100644
index 00000000..91ab656d
--- /dev/null
+++ b/src/VBox/Runtime/r3/allocex.h
@@ -0,0 +1,96 @@
+/* $Id: allocex.h $ */
+/** @file
+ * IPRT - Memory Allocation, Extended Alloc and Free Functions for Ring-3.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_allocex_h
+#define IPRT_INCLUDED_SRC_r3_allocex_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+/**
+ * Heading for extended memory allocations in ring-3.
+ */
+typedef struct RTMEMHDRR3
+{
+ /** Magic (RTMEMHDR_MAGIC). */
+ uint32_t u32Magic;
+ /** Block flags (RTMEMALLOCEX_FLAGS_*). */
+ uint32_t fFlags;
+ /** The actual size of the block, header not included. */
+ uint32_t cb;
+ /** The requested allocation size. */
+ uint32_t cbReq;
+} RTMEMHDRR3;
+/** Pointer to a ring-3 extended memory header. */
+typedef RTMEMHDRR3 *PRTMEMHDRR3;
+
+
+/**
+ * Allocate memory in below 64KB.
+ *
+ * @returns IPRT status code.
+ * @param cbAlloc Number of bytes to allocate (including the
+ * header if the caller needs one).
+ * @param fFlags Allocation flags.
+ * @param ppv Where to return the pointer to the memory.
+ */
+DECLHIDDEN(int) rtMemAllocEx16BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv);
+
+/**
+ * Allocate memory in below 4GB.
+ *
+ * @returns IPRT status code.
+ * @param cbAlloc Number of bytes to allocate (including the
+ * header if the caller needs one).
+ * @param fFlags Allocation flags.
+ * @param ppv Where to return the pointer to the memory.
+ */
+DECLHIDDEN(int) rtMemAllocEx32BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv);
+
+
+/**
+ * Frees memory allocated by rtMemAllocEx16BitReach and rtMemAllocEx32BitReach.
+ *
+ * @param pv Start of allocation.
+ * @param cb Allocation size.
+ * @param fFlags Allocation flags.
+ */
+DECLHIDDEN(void) rtMemFreeExYyBitReach(void *pv, size_t cb, uint32_t fFlags);
+
+#endif /* !IPRT_INCLUDED_SRC_r3_allocex_h */
+
diff --git a/src/VBox/Runtime/r3/darwin/Makefile.kup b/src/VBox/Runtime/r3/darwin/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/Makefile.kup
diff --git a/src/VBox/Runtime/r3/darwin/RTCrStoreCreateSnapshotById-darwin.cpp b/src/VBox/Runtime/r3/darwin/RTCrStoreCreateSnapshotById-darwin.cpp
new file mode 100644
index 00000000..aa26ac2d
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/RTCrStoreCreateSnapshotById-darwin.cpp
@@ -0,0 +1,268 @@
+/* $Id: RTCrStoreCreateSnapshotById-darwin.cpp $ */
+/** @file
+ * IPRT - RTCrStoreCreateSnapshotById, Darwin.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/crypto/store.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+
+/* HACK ALERT! Shut up those deprecated messages on SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext. */
+#include <CoreFoundation/CoreFoundation.h>
+#undef DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
+#define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
+
+#include <Security/Security.h>
+
+
+/**
+ * Checks the trust settings of the certificate.
+ *
+ * @returns true if not out-right distructed, otherwise false.
+ * @param hCert The certificate.
+ * @param enmTrustDomain The trust settings domain to check relative to.
+ */
+static bool rtCrStoreIsDarwinCertTrustworthy(SecCertificateRef hCert, SecTrustSettingsDomain enmTrustDomain)
+{
+ bool fResult = true;
+ CFArrayRef hTrustSettings;
+ OSStatus orc = SecTrustSettingsCopyTrustSettings(hCert, enmTrustDomain, &hTrustSettings);
+ if (orc == noErr)
+ {
+ CFIndex const cTrustSettings = CFArrayGetCount(hTrustSettings);
+ for (CFIndex i = 0; i < cTrustSettings; i++)
+ {
+ CFDictionaryRef hDict = (CFDictionaryRef)CFArrayGetValueAtIndex(hTrustSettings, i);
+ AssertContinue(CFGetTypeID(hDict) == CFDictionaryGetTypeID());
+
+ CFNumberRef hNum = (CFNumberRef)CFDictionaryGetValue(hDict, kSecTrustSettingsResult);
+ if (hNum)
+ {
+ AssertContinue(CFGetTypeID(hNum) == CFNumberGetTypeID());
+ SInt32 iNum;
+ if (CFNumberGetValue(hNum, kCFNumberSInt32Type, &iNum))
+ {
+ if (iNum == kSecTrustSettingsResultDeny)
+ {
+ fResult = false;
+ break;
+ }
+ }
+ /* No need to release hNum (get rule). */
+ }
+ /* No need to release hDict (get rule). */
+ }
+ CFRelease(hTrustSettings);
+ }
+ else if (orc != errSecItemNotFound)
+ {
+ AssertFailed();
+ fResult = false;
+ }
+ return fResult;
+}
+
+
+static int rtCrStoreAddCertsFromNativeKeychain(RTCRSTORE hStore, SecKeychainRef hKeychain, SecTrustSettingsDomain enmTrustDomain,
+ int rc, PRTERRINFO pErrInfo)
+{
+ /** @todo The SecKeychainSearchCreateFromAttributes and
+ * SecKeychainSearchCopyNext APIs have been officially deprecated since 10.7
+ * according to the header files. However, the perferred API,
+ * SecItemCopyMatching (and possibly SecTrustCopyAnchorCertificates) would
+ * require a larger rewrite here and that's just not worth it right now. We can
+ * do that should these APIs be removed (unlikely given the amount of grep hits
+ * in the public 10.15.3 sources). */
+
+ /*
+ * Enumerate the certificates in the keychain.
+ */
+ RT_GCC_NO_WARN_DEPRECATED_BEGIN
+ SecKeychainSearchRef hSearch;
+ OSStatus orc = SecKeychainSearchCreateFromAttributes(hKeychain, kSecCertificateItemClass, NULL, &hSearch);
+ if (orc == noErr)
+ {
+ SecKeychainItemRef hItem;
+ while ((orc = SecKeychainSearchCopyNext(hSearch, &hItem)) == noErr)
+ {
+ Assert(CFGetTypeID(hItem) == SecCertificateGetTypeID());
+ SecCertificateRef hCert = (SecCertificateRef)hItem;
+
+ /*
+ * Check if the current certificate is at all trusted, skip it if it's isn't.
+ */
+ if (rtCrStoreIsDarwinCertTrustworthy(hCert, enmTrustDomain))
+ {
+ /*
+ * Get the certificate data.
+ */
+ CFDataRef hEncodedCert = SecCertificateCopyData(hCert);
+ Assert(hEncodedCert);
+ if (hEncodedCert)
+ {
+ CFIndex cbEncoded = CFDataGetLength(hEncodedCert);
+ const uint8_t *pbEncoded = CFDataGetBytePtr(hEncodedCert);
+
+ RTERRINFOSTATIC StaticErrInfo;
+ int rc2 = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_X509_DER | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
+ pbEncoded, cbEncoded, RTErrInfoInitStatic(&StaticErrInfo));
+ if (RT_FAILURE(rc2))
+ {
+ if (RTErrInfoIsSet(&StaticErrInfo.Core))
+ RTErrInfoAddF(pErrInfo, -rc2, " %s", StaticErrInfo.Core.pszMsg);
+ else
+ RTErrInfoAddF(pErrInfo, -rc2, " %Rrc adding cert", rc2);
+ rc = -rc2;
+ }
+
+ CFRelease(hEncodedCert);
+ }
+ }
+
+ CFRelease(hItem);
+ }
+ if (orc != errSecItemNotFound)
+ rc = RTErrInfoAddF(pErrInfo, -VERR_SEARCH_ERROR,
+ " SecKeychainSearchCopyNext failed with %#x", orc);
+ CFRelease(hSearch);
+ }
+ else
+ rc = RTErrInfoAddF(pErrInfo, -VERR_SEARCH_ERROR,
+ " SecKeychainSearchCreateFromAttributes failed with %#x", orc);
+ RT_GCC_NO_WARN_DEPRECATED_END
+ return rc;
+}
+
+
+static int rtCrStoreAddCertsFromNativeKeychainFile(RTCRSTORE hStore, const char *pszKeychain,
+ SecTrustSettingsDomain enmTrustDomain,
+ int rc, PRTERRINFO pErrInfo)
+{
+ /*
+ * Open the keychain and call common worker to do the job.
+ */
+ SecKeychainRef hKeychain;
+ OSStatus orc = SecKeychainOpen(pszKeychain, &hKeychain);
+ if (orc == noErr)
+ {
+ rc = rtCrStoreAddCertsFromNativeKeychain(hStore, hKeychain, enmTrustDomain, rc, pErrInfo);
+
+ CFRelease(hKeychain);
+ }
+ else if (RTFileExists(pszKeychain))
+ rc = RTErrInfoAddF(pErrInfo, -VERR_OPEN_FAILED, " SecKeychainOpen failed with %#x on '%s'", orc, pszKeychain);
+ return rc;
+}
+
+
+static int rtCrStoreAddCertsFromNativeKeystoreDomain(RTCRSTORE hStore, SecPreferencesDomain enmDomain,
+ SecTrustSettingsDomain enmTrustDomain,
+ int rc, PRTERRINFO pErrInfo)
+{
+ /*
+ * Get a list of keystores for this domain and call common worker on each.
+ */
+ CFArrayRef hKeychains;
+ OSStatus orc = SecKeychainCopyDomainSearchList(enmDomain, &hKeychains);
+ if (orc == noErr)
+ {
+ CFIndex const cEntries = CFArrayGetCount(hKeychains);
+ for (CFIndex i = 0; i < cEntries; i++)
+ {
+ SecKeychainRef hKeychain = (SecKeychainRef)CFArrayGetValueAtIndex(hKeychains, i);
+ Assert(CFGetTypeID(hKeychain) == SecKeychainGetTypeID());
+ CFRetain(hKeychain);
+
+ rc = rtCrStoreAddCertsFromNativeKeychain(hStore, hKeychain, enmTrustDomain, rc, pErrInfo);
+
+ CFRelease(hKeychain);
+ }
+
+ CFRelease(hKeychains);
+ }
+ else
+ rc = RTErrInfoAddF(pErrInfo, -VERR_SEARCH_ERROR,
+ " SecKeychainCopyDomainSearchList failed with %#x on %d", orc, enmDomain);
+ return rc;
+}
+
+
+RTDECL(int) RTCrStoreCreateSnapshotById(PRTCRSTORE phStore, RTCRSTOREID enmStoreId, PRTERRINFO pErrInfo)
+{
+ AssertReturn(enmStoreId > RTCRSTOREID_INVALID && enmStoreId < RTCRSTOREID_END, VERR_INVALID_PARAMETER);
+
+ /*
+ * Create an empty in-memory store.
+ */
+ RTCRSTORE hStore;
+ int rc = RTCrStoreCreateInMem(&hStore, 128);
+ if (RT_SUCCESS(rc))
+ {
+ *phStore = hStore;
+
+ /*
+ * Load the certificates corresponding to the given virtual store ID.
+ */
+ switch (enmStoreId)
+ {
+ case RTCRSTOREID_USER_TRUSTED_CAS_AND_CERTIFICATES:
+ rc = rtCrStoreAddCertsFromNativeKeystoreDomain(hStore, kSecPreferencesDomainUser,
+ kSecTrustSettingsDomainUser, rc, pErrInfo);
+ break;
+
+ case RTCRSTOREID_SYSTEM_TRUSTED_CAS_AND_CERTIFICATES:
+ rc = rtCrStoreAddCertsFromNativeKeystoreDomain(hStore, kSecPreferencesDomainSystem,
+ kSecTrustSettingsDomainSystem, rc, pErrInfo);
+ rc = rtCrStoreAddCertsFromNativeKeychainFile(hStore,
+ "/System/Library/Keychains/SystemRootCertificates.keychain",
+ kSecTrustSettingsDomainSystem, rc, pErrInfo);
+ break;
+
+ default:
+ AssertFailed(); /* implement me */
+ }
+ }
+ else
+ RTErrInfoSet(pErrInfo, rc, "RTCrStoreCreateInMem failed");
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCreateSnapshotById);
+
diff --git a/src/VBox/Runtime/r3/darwin/RTFileQuerySectorSize-darwin.cpp b/src/VBox/Runtime/r3/darwin/RTFileQuerySectorSize-darwin.cpp
new file mode 100644
index 00000000..bf246d48
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/RTFileQuerySectorSize-darwin.cpp
@@ -0,0 +1,89 @@
+/* $Id: RTFileQuerySectorSize-darwin.cpp $ */
+/** @file
+ * IPRT - RTFileQuerySectorSize, Darwin.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#include <sys/stat.h>
+#include <sys/disk.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+RTDECL(int) RTFileQuerySectorSize(RTFILE hFile, uint32_t *pcbSector)
+{
+ AssertPtrReturn(pcbSector, VERR_INVALID_PARAMETER);
+
+ int rc;
+ int const fd = (int)RTFileToNative(hFile);
+ struct stat DevStat = { 0 };
+ if (!fstat(fd, &DevStat))
+ {
+ if (S_ISBLK(DevStat.st_mode) || S_ISCHR(DevStat.st_mode))
+ {
+ uint32_t cbLogicalBlock = 0;
+ if (!ioctl(fd, DKIOCGETBLOCKSIZE, &cbLogicalBlock))
+ {
+ AssertReturn(cbLogicalBlock > 0, VERR_INVALID_FUNCTION);
+ *pcbSector = cbLogicalBlock;
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("ioctl failed: errno=%d / %Rrc\n", errno, rc));
+ }
+ else
+ {
+ AssertMsgFailed(("not a block or character device.\n"));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("fstat failed: errno=%d / %Rrc\n", errno, rc));
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/darwin/RTMpGetDescription-generic.cpp b/src/VBox/Runtime/r3/darwin/RTMpGetDescription-generic.cpp
new file mode 100644
index 00000000..cf28744d
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/RTMpGetDescription-generic.cpp
@@ -0,0 +1,173 @@
+/* $Id: RTMpGetDescription-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, RTMpGetDescription for darwin/arm.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/mp.h>
+#include "internal/iprt.h"
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+#include <sys/sysctl.h>
+#if defined(RT_ARCH_ARM64)
+# include <IOKit/IOKitLib.h>
+#endif
+
+
+RTDECL(int) RTMpGetDescription(RTCPUID idCpu, char *pszBuf, size_t cbBuf)
+{
+ /*
+ * Check that the specified cpu is valid & online.
+ */
+ if (idCpu != NIL_RTCPUID && !RTMpIsCpuOnline(idCpu))
+ return RTMpIsCpuPossible(idCpu)
+ ? VERR_CPU_OFFLINE
+ : VERR_CPU_NOT_FOUND;
+
+ /*
+ * For ARM there are typically two different types of cores, so look up the
+ * processor in the IODeviceTree and get the core name and type from there
+ * if we can.
+ */
+ char szExtra[256];
+ size_t cchExtra = 0;
+
+#if defined(RT_ARCH_ARM64)
+ char szArmCpuPath[64];
+ RTStrPrintf(szArmCpuPath, sizeof(szArmCpuPath), "IODeviceTree:/cpus/cpu%x", idCpu); /** @todo Hex? M1 Max only has 10 cores... */
+ io_registry_entry_t hIoRegEntry = IORegistryEntryFromPath(kIOMasterPortDefault, szArmCpuPath);
+ if (hIoRegEntry != MACH_PORT_NULL)
+ {
+ /* This property is typically "E" or "P". Don't know why it's mapped
+ to a CFDataRef rather than a CFStringRef... */
+ CFTypeRef hValRef = IORegistryEntryCreateCFProperty(hIoRegEntry, CFSTR("cluster-type"), kCFAllocatorDefault, kNilOptions);
+ if (hValRef)
+ {
+ if (CFGetTypeID(hValRef) == CFDataGetTypeID())
+ {
+ size_t const cbData = CFDataGetLength((CFDataRef)hValRef);
+ uint8_t const * const pbData = (uint8_t const *)CFDataGetBytePtr((CFDataRef)hValRef);
+ if (cbData > 0 && pbData != NULL)
+ {
+ int rc = RTStrValidateEncodingEx((const char *)pbData, cbData, RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+ AssertMsgRC(rc, ("%p LB %#zx: %.*Rhxs\n", pbData, cbData, cbData, pbData));
+ if (RT_SUCCESS(rc))
+ {
+ RTStrCopy(&szExtra[1], sizeof(szExtra) - 1, (const char *)pbData);
+ szExtra[0] = ' ';
+ cchExtra = strlen(szExtra);
+ }
+ }
+ }
+ else
+ AssertMsgFailed(("%p=%#lx\n", hValRef, CFGetTypeID(hValRef)));
+
+ CFRelease(hValRef);
+ }
+
+ /* The compatible property is an "array" of zero terminated strings.
+ For the M1 mini the first entry is either "apple,firestorm" (P cores)
+ or "apple,icestorm" (E cores). We extract the bits after the comma
+ and append it to the extra string. (Again, dunno why it's a CFDataRef.) */
+ hValRef = IORegistryEntryCreateCFProperty(hIoRegEntry, CFSTR("compatible"), kCFAllocatorDefault, 0);
+ if (hValRef)
+ {
+ if (CFGetTypeID(hValRef) == CFDataGetTypeID())
+ {
+ size_t const cbData = CFDataGetLength((CFDataRef)hValRef);
+ uint8_t const * const pbData = (uint8_t const *)CFDataGetBytePtr((CFDataRef)hValRef);
+ if (cbData > 0 && pbData != NULL)
+ {
+ Assert(pbData[cbData - 1] == '\0');
+ if (pbData[cbData - 1] == '\0')
+ {
+ size_t offData = 0;
+ while (offData < cbData)
+ {
+ const char *psz = (const char *)&pbData[offData];
+ size_t const cch = strlen(psz);
+
+ if (RTStrStartsWith(psz, "apple,"))
+ {
+ psz += sizeof("apple,") - 1;
+ psz = RTStrStripL(psz);
+ if (*psz)
+ {
+ if (RTStrIsValidEncoding(psz))
+ cchExtra += RTStrPrintf(&szExtra[cchExtra], sizeof(szExtra) - cchExtra, " (%s)", psz);
+ else
+ AssertFailed();
+ }
+ }
+
+ /* advance */
+ offData += cch + 1;
+ }
+ }
+ }
+ }
+ else
+ AssertMsgFailed(("%p=%#lx\n", hValRef, CFGetTypeID(hValRef)));
+ CFRelease(hValRef);
+ }
+
+ IOObjectRelease(hIoRegEntry);
+ }
+#endif
+ szExtra[cchExtra] = '\0';
+
+ /*
+ * Just use the sysctl machdep.cpu.brand_string value for now.
+ */
+ char szBrand[128] = {0};
+ size_t cb = sizeof(szBrand);
+ int rc = sysctlbyname("machdep.cpu.brand_string", &szBrand, &cb, NULL, 0);
+ if (rc == -1)
+ szBrand[0] = '\0';
+
+ char *pszStripped = RTStrStrip(szBrand);
+ if (*pszStripped == '\0')
+ pszStripped = strcpy(szBrand, "Unknown");
+
+ rc = RTStrCopy(pszBuf, cbBuf, pszStripped);
+ if (cchExtra > 0 && RT_SUCCESS(rc))
+ rc = RTStrCat(pszBuf, cbBuf, szExtra);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTMpGetDescription);
+
diff --git a/src/VBox/Runtime/r3/darwin/RTPathUserDocuments-darwin.cpp b/src/VBox/Runtime/r3/darwin/RTPathUserDocuments-darwin.cpp
new file mode 100644
index 00000000..296c3857
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/RTPathUserDocuments-darwin.cpp
@@ -0,0 +1,123 @@
+/* $Id: RTPathUserDocuments-darwin.cpp $ */
+/** @file
+ * IPRT - RTPathUserDocuments, darwin ring-3.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/path.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+# include <sysdir.h>
+#else
+# include <NSSystemDirectories.h>
+#endif
+#include <sys/syslimits.h>
+#ifdef IPRT_USE_CORE_SERVICE_FOR_USER_DOCUMENTS
+# include <CoreServices/CoreServices.h>
+#endif
+
+
+RTDECL(int) RTPathUserDocuments(char *pszPath, size_t cchPath)
+{
+ /*
+ * Validate input
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(cchPath, VERR_INVALID_PARAMETER);
+
+ /*
+ * Try NSSystemDirectories first since that works for directories that doesn't exist.
+ * The NSSystemDirectories API was renamed in 10.12 to sysdir.
+ */
+ int rc = VERR_PATH_NOT_FOUND;
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+ sysdir_search_path_enumeration_state EnmState = sysdir_start_search_path_enumeration(SYSDIR_DIRECTORY_DOCUMENT,
+ SYSDIR_DOMAIN_MASK_USER);
+#else
+ NSSearchPathEnumerationState EnmState = NSStartSearchPathEnumeration(NSDocumentDirectory, NSUserDomainMask);
+#endif
+ if (EnmState != 0)
+ {
+ char szTmp[PATH_MAX];
+ szTmp[0] = szTmp[PATH_MAX - 1] = '\0';
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+ EnmState = sysdir_get_next_search_path_enumeration(EnmState, szTmp);
+#else
+ EnmState = NSGetNextSearchPathEnumeration(EnmState, szTmp);
+#endif
+ if (EnmState != 0)
+ {
+ size_t cchTmp = strlen(szTmp);
+ if (cchTmp >= cchPath)
+ return VERR_BUFFER_OVERFLOW;
+
+ if (szTmp[0] == '~' && szTmp[1] == '/')
+ {
+ /* Expand tilde. */
+ rc = RTPathUserHome(pszPath, cchPath - cchTmp + 2);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = RTPathAppend(pszPath, cchPath, &szTmp[2]);
+ }
+ else
+ rc = RTStrCopy(pszPath, cchPath, szTmp);
+ return rc;
+ }
+ }
+
+#ifdef IPRT_USE_CORE_SERVICE_FOR_USER_DOCUMENTS
+ /*
+ * Fall back on FSFindFolder in case the above should fail...
+ */
+ FSRef ref;
+ OSErr err = FSFindFolder(kOnAppropriateDisk, kDocumentsFolderType, false /* createFolder */, &ref);
+ if (err == noErr)
+ {
+ err = FSRefMakePath(&ref, (UInt8*)pszPath, cchPath);
+ if (err == noErr)
+ return VINF_SUCCESS;
+ }
+#endif
+ Assert(RT_FAILURE_NP(rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/darwin/RTSystemQueryDmiString-darwin.cpp b/src/VBox/Runtime/r3/darwin/RTSystemQueryDmiString-darwin.cpp
new file mode 100644
index 00000000..0c644229
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/RTSystemQueryDmiString-darwin.cpp
@@ -0,0 +1,163 @@
+/* $Id: RTSystemQueryDmiString-darwin.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryDmiString, darwin ring-3.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include <mach/mach_port.h>
+#include <IOKit/IOKitLib.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define IOCLASS_PLATFORMEXPERTDEVICE "IOPlatformExpertDevice"
+#define PROP_PRODUCT_NAME "product-name"
+#define PROP_PRODUCT_VERSION "version"
+#define PROP_PRODUCT_SERIAL "IOPlatformSerialNumber"
+#define PROP_PRODUCT_UUID "IOPlatformUUID"
+#define PROP_MANUFACTURER "manufacturer"
+
+
+RTDECL(int) RTSystemQueryDmiString(RTSYSDMISTR enmString, char *pszBuf, size_t cbBuf)
+{
+ AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbBuf > 0, VERR_INVALID_PARAMETER);
+ *pszBuf = '\0';
+ AssertReturn(enmString > RTSYSDMISTR_INVALID && enmString < RTSYSDMISTR_END, VERR_INVALID_PARAMETER);
+
+ CFStringRef PropStringRef = NULL;
+ switch (enmString)
+ {
+ case RTSYSDMISTR_PRODUCT_NAME: PropStringRef = CFSTR(PROP_PRODUCT_NAME); break;
+ case RTSYSDMISTR_PRODUCT_VERSION: PropStringRef = CFSTR(PROP_PRODUCT_VERSION); break;
+ case RTSYSDMISTR_PRODUCT_SERIAL: PropStringRef = CFSTR(PROP_PRODUCT_SERIAL); break;
+ case RTSYSDMISTR_PRODUCT_UUID: PropStringRef = CFSTR(PROP_PRODUCT_UUID); break;
+ case RTSYSDMISTR_MANUFACTURER: PropStringRef = CFSTR(PROP_MANUFACTURER); break;
+ default:
+ return VERR_NOT_SUPPORTED;
+ }
+
+ mach_port_t MasterPort;
+ kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &MasterPort);
+ if (kr != kIOReturnSuccess)
+ {
+ if (kr == KERN_NO_ACCESS)
+ return VERR_ACCESS_DENIED;
+ return RTErrConvertFromDarwinIO(kr);
+ }
+
+ CFDictionaryRef ClassToMatch = IOServiceMatching(IOCLASS_PLATFORMEXPERTDEVICE);
+ if (!ClassToMatch)
+ return VERR_NOT_SUPPORTED;
+
+ /* IOServiceGetMatchingServices will always consume ClassToMatch. */
+ io_iterator_t Iterator;
+ kr = IOServiceGetMatchingServices(MasterPort, ClassToMatch, &Iterator);
+ if (kr != kIOReturnSuccess)
+ return RTErrConvertFromDarwinIO(kr);
+
+ int rc = VERR_NOT_SUPPORTED;
+ io_service_t ServiceObject;
+ while ((ServiceObject = IOIteratorNext(Iterator)))
+ {
+ if ( enmString == RTSYSDMISTR_PRODUCT_NAME
+ || enmString == RTSYSDMISTR_PRODUCT_VERSION
+ || enmString == RTSYSDMISTR_MANUFACTURER
+ )
+ {
+ CFDataRef DataRef = (CFDataRef)IORegistryEntryCreateCFProperty(ServiceObject, PropStringRef,
+ kCFAllocatorDefault, kNilOptions);
+ if (DataRef)
+ {
+ size_t cbData = CFDataGetLength(DataRef);
+ const char *pchData = (const char *)CFDataGetBytePtr(DataRef);
+ rc = RTStrCopyEx(pszBuf, cbBuf, pchData, cbData);
+ CFRelease(DataRef);
+ break;
+ }
+ }
+ else
+ {
+ CFStringRef StringRef = (CFStringRef)IORegistryEntryCreateCFProperty(ServiceObject, PropStringRef,
+ kCFAllocatorDefault, kNilOptions);
+ if (StringRef)
+ {
+ Boolean fRc = CFStringGetCString(StringRef, pszBuf, cbBuf, kCFStringEncodingUTF8);
+ if (fRc)
+ rc = VINF_SUCCESS;
+ else
+ {
+ CFIndex cwc = CFStringGetLength(StringRef);
+ size_t cbTmp = cwc + 1;
+ char *pszTmp = (char *)RTMemTmpAlloc(cbTmp);
+ int cTries = 1;
+ while ( pszTmp
+ && (fRc = CFStringGetCString(StringRef, pszTmp, cbTmp, kCFStringEncodingUTF8)) == FALSE
+ && cTries++ < 4)
+ {
+ RTMemTmpFree(pszTmp);
+ cbTmp *= 2;
+ pszTmp = (char *)RTMemTmpAlloc(cbTmp);
+ }
+ if (fRc)
+ rc = RTStrCopy(pszBuf, cbBuf, pszTmp);
+ else if (!pszTmp)
+ rc = VERR_NO_TMP_MEMORY;
+ else
+ rc = VERR_ACCESS_DENIED;
+ RTMemFree(pszTmp);
+ }
+ CFRelease(StringRef);
+ break;
+ }
+ }
+ }
+
+ IOObjectRelease(ServiceObject);
+ IOObjectRelease(Iterator);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/darwin/filelock-darwin.cpp b/src/VBox/Runtime/r3/darwin/filelock-darwin.cpp
new file mode 100644
index 00000000..16d7137d
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/filelock-darwin.cpp
@@ -0,0 +1,178 @@
+/* $Id: filelock-darwin.cpp $ */
+/** @file
+ * IPRT - File Locking, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <iprt/file.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/file.h"
+#include "internal/fs.h"
+
+
+
+
+RTR3DECL(int) RTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /* Check arguments. */
+ if (fLock & ~RTFILE_LOCK_MASK)
+ {
+ AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Validate offset.
+ */
+ if ( sizeof(off_t) < sizeof(cbLock)
+ && ( (offLock >> 32) != 0
+ || (cbLock >> 32) != 0
+ || ((offLock + cbLock) >> 32) != 0))
+ {
+ AssertMsgFailed(("64-bit file i/o not supported! offLock=%lld cbLock=%lld\n", offLock, cbLock));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /* Prepare flock structure. */
+ struct flock fl;
+ Assert(RTFILE_LOCK_WRITE);
+ fl.l_type = (fLock & RTFILE_LOCK_WRITE) ? F_WRLCK : F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = (off_t)offLock;
+ fl.l_len = (off_t)cbLock;
+ fl.l_pid = 0;
+
+ Assert(RTFILE_LOCK_WAIT);
+ if (fcntl(RTFileToNative(hFile), (fLock & RTFILE_LOCK_WAIT) ? F_SETLKW : F_SETLK, &fl) >= 0)
+ return VINF_SUCCESS;
+ int iErr = errno;
+ if (iErr == ENOTSUP)
+ {
+ /*
+ * This is really bad hack for getting VDIs to work somewhat
+ * safely on SMB mounts.
+ */
+ /** @todo we need to keep track of these locks really. Anyone requiring to lock more
+ * than one part of a file will have to fix this. */
+ unsigned f = 0;
+ Assert(RTFILE_LOCK_WAIT);
+ if (fLock & RTFILE_LOCK_WAIT)
+ f |= LOCK_NB;
+ if (fLock & RTFILE_LOCK_WRITE)
+ f |= LOCK_EX;
+ else
+ f |= LOCK_SH;
+ if (!flock(RTFileToNative(hFile), f))
+ return VINF_SUCCESS;
+ iErr = errno;
+ if (iErr == EWOULDBLOCK)
+ return VERR_FILE_LOCK_VIOLATION;
+ }
+
+ if ( iErr == EAGAIN
+ || iErr == EACCES)
+ return VERR_FILE_LOCK_VIOLATION;
+
+ return RTErrConvertFromErrno(iErr);
+}
+
+
+RTR3DECL(int) RTFileChangeLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ /** @todo We never returns VERR_FILE_NOT_LOCKED for now. */
+ return RTFileLock(hFile, fLock, offLock, cbLock);
+}
+
+
+RTR3DECL(int) RTFileUnlock(RTFILE hFile, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /*
+ * Validate offset.
+ */
+ if ( sizeof(off_t) < sizeof(cbLock)
+ && ( (offLock >> 32) != 0
+ || (cbLock >> 32) != 0
+ || ((offLock + cbLock) >> 32) != 0))
+ {
+ AssertMsgFailed(("64-bit file i/o not supported! offLock=%lld cbLock=%lld\n", offLock, cbLock));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /* Prepare flock structure. */
+ struct flock fl;
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = (off_t)offLock;
+ fl.l_len = (off_t)cbLock;
+ fl.l_pid = 0;
+
+ if (fcntl(RTFileToNative(hFile), F_SETLK, &fl) >= 0)
+ return VINF_SUCCESS;
+
+ int iErr = errno;
+ if (iErr == ENOTSUP)
+ {
+ /* A SMB hack, see RTFileLock. */
+ if (!flock(RTFileToNative(hFile), LOCK_UN))
+ return VINF_SUCCESS;
+ }
+
+ /** @todo check error codes for non existing lock. */
+ if ( iErr == EAGAIN
+ || iErr == EACCES)
+ return VERR_FILE_LOCK_VIOLATION;
+
+ return RTErrConvertFromErrno(iErr);
+}
+
diff --git a/src/VBox/Runtime/r3/darwin/krnlmod-darwin.cpp b/src/VBox/Runtime/r3/darwin/krnlmod-darwin.cpp
new file mode 100644
index 00000000..756786b0
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/krnlmod-darwin.cpp
@@ -0,0 +1,470 @@
+/* $Id: krnlmod-darwin.cpp $ */
+/** @file
+ * IPRT - Kernel module, Darwin.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYSTEM
+#include <iprt/krnlmod.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/ldr.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/string.h>
+#include <iprt/types.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <libkern/OSReturn.h>
+
+
+/** @name Missing/private IOKitLib declarations and definitions.
+ * @{ */
+/** OSKextCopyLoadedKextInfo in IOKit. */
+typedef CFDictionaryRef (* PFNOSKEXTCOPYLOADEDKEXTINFO)(CFArrayRef, CFArrayRef);
+/** KextManagerLoadKextWithURL in IOKit. */
+typedef OSReturn (* PFNKEXTMANAGERLOADKEXTWITHURL)(CFURLRef, CFArrayRef);
+/** KextManagerLoadKextWithIdentifier in IOKit */
+typedef OSReturn (* PFNKEXTMANAGERLOADKEXTWITHIDENTIFIER)(CFStringRef, CFArrayRef);
+/** KextManagerUnloadKextWithIdentifier in IOKit */
+typedef OSReturn (* PFNKEXTMANAGERUNLOADKEXTWITHIDENTIFIER)(CFStringRef);
+
+#ifndef kOSBundleRetainCountKey
+# define kOSBundleRetainCountKey CFSTR("OSBundleRetainCount")
+#endif
+#ifndef kOSBundleLoadSizeKey
+# define kOSBundleLoadSizeKey CFSTR("OSBundleLoadSize")
+#endif
+#ifndef kOSBundleLoadAddressKey
+# define kOSBundleLoadAddressKey CFSTR("OSBundleLoadAddress")
+#endif
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Internal kernel information record state.
+ */
+typedef struct RTKRNLMODINFOINT
+{
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** The dictionary containing our data. */
+ CFDictionaryRef hDictKext;
+} RTKRNLMODINFOINT;
+/** Pointer to the internal kernel module information record. */
+typedef RTKRNLMODINFOINT *PRTKRNLMODINFOINT;
+/** Pointer to a const internal kernel module information record. */
+typedef const RTKRNLMODINFOINT *PCRTKRNLMODINFOINT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTONCE g_GetIoKitApisOnce = RTONCE_INITIALIZER;
+static PFNOSKEXTCOPYLOADEDKEXTINFO g_pfnOSKextCopyLoadedKextInfo = NULL;
+static PFNKEXTMANAGERLOADKEXTWITHURL g_pfnKextManagerLoadKextWithUrl = NULL;
+static PFNKEXTMANAGERLOADKEXTWITHIDENTIFIER g_pfnKextManagerLoadKextWithIdentifier = NULL;
+static PFNKEXTMANAGERUNLOADKEXTWITHIDENTIFIER g_pfnKextManagerUnloadKextWithIdentifier = NULL;
+
+/** Do-once callback for setting g_pfnOSKextCopyLoadedKextInfo. */
+static DECLCALLBACK(int) rtKrnlModDarwinResolveIoKitApis(void *pvUser)
+{
+ RTLDRMOD hMod;
+// int rc = RTLdrLoad("/System/Library/Frameworks/IOKit.framework/Versions/Current/IOKit", &hMod);
+ int rc = RTLdrLoadEx("/System/Library/Frameworks/IOKit.framework/Versions/Current/IOKit", &hMod, RTLDRLOAD_FLAGS_NO_SUFFIX, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTLdrGetSymbol(hMod, "OSKextCopyLoadedKextInfo", (void **)&g_pfnOSKextCopyLoadedKextInfo);
+ RTLdrGetSymbol(hMod, "KextManagerLoadKextWithURL", (void **)&g_pfnKextManagerLoadKextWithUrl);
+ RTLdrGetSymbol(hMod, "KextManagerLoadKextWithIdentifier", (void **)&g_pfnKextManagerLoadKextWithIdentifier);
+ RTLdrGetSymbol(hMod, "KextManagerUnloadKextWithIdentifier", (void **)&g_pfnKextManagerUnloadKextWithIdentifier);
+ }
+
+ RT_NOREF(pvUser);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Returns the kext information dictionary structure matching the given name.
+ *
+ * @returns Pointer to the matching module information record on success or NULL if not found.
+ * @param pszName The name to look for.
+ */
+static CFDictionaryRef rtKrnlModDarwinGetKextInfoByName(const char *pszName)
+{
+ CFDictionaryRef hDictKext = NULL;
+
+ RTOnce(&g_GetIoKitApisOnce, rtKrnlModDarwinResolveIoKitApis, NULL);
+ if (g_pfnOSKextCopyLoadedKextInfo)
+ {
+ CFStringRef hKextName = CFStringCreateWithCString(kCFAllocatorDefault, pszName, kCFStringEncodingUTF8);
+ if (hKextName)
+ {
+ CFArrayRef hArrKextIdRef = CFArrayCreate(kCFAllocatorDefault, (const void **)&hKextName, 1, &kCFTypeArrayCallBacks);
+ if (hArrKextIdRef)
+ {
+ CFDictionaryRef hLoadedKexts = g_pfnOSKextCopyLoadedKextInfo(hArrKextIdRef, NULL /* all info */);
+ if (hLoadedKexts)
+ {
+ if (CFDictionaryGetCount(hLoadedKexts) > 0)
+ {
+ hDictKext = (CFDictionaryRef)CFDictionaryGetValue(hLoadedKexts, hKextName);
+ CFRetain(hDictKext);
+ }
+
+ CFRelease(hLoadedKexts);
+ }
+ CFRelease(hArrKextIdRef);
+ }
+ CFRelease(hKextName);
+ }
+ }
+
+ return hDictKext;
+}
+
+/**
+ * Destroy the given kernel module information record.
+ *
+ * @param pThis The record to destroy.
+ */
+static void rtKrnlModInfoDestroy(PRTKRNLMODINFOINT pThis)
+{
+ CFRelease(pThis->hDictKext);
+ RTMemFree(pThis);
+}
+
+
+RTDECL(int) RTKrnlModQueryLoaded(const char *pszName, bool *pfLoaded)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfLoaded, VERR_INVALID_POINTER);
+
+ CFDictionaryRef hDictKext = rtKrnlModDarwinGetKextInfoByName(pszName);
+ *pfLoaded = hDictKext != NULL;
+ if (hDictKext)
+ CFRelease(hDictKext);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTKrnlModLoadedQueryInfo(const char *pszName, PRTKRNLMODINFO phKrnlModInfo)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(phKrnlModInfo, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ CFDictionaryRef hDictKext = rtKrnlModDarwinGetKextInfoByName(pszName);
+ if (hDictKext)
+ {
+ PRTKRNLMODINFOINT pThis = (PRTKRNLMODINFOINT)RTMemAllocZ(sizeof(RTKRNLMODINFOINT));
+ if (pThis)
+ {
+ pThis->cRefs = 1;
+ pThis->hDictKext = hDictKext;
+
+ *phKrnlModInfo = pThis;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTKrnlModLoadedGetCount(void)
+{
+ uint32_t cLoadedKexts = 0;
+ RTOnce(&g_GetIoKitApisOnce, rtKrnlModDarwinResolveIoKitApis, NULL);
+ if (g_pfnOSKextCopyLoadedKextInfo)
+ {
+ CFDictionaryRef hLoadedKexts = g_pfnOSKextCopyLoadedKextInfo(NULL, NULL /* all info */);
+ if (hLoadedKexts)
+ {
+ cLoadedKexts = CFDictionaryGetCount(hLoadedKexts);
+ CFRelease(hLoadedKexts);
+ }
+ }
+
+ return cLoadedKexts;
+}
+
+
+RTDECL(int) RTKrnlModLoadedQueryInfoAll(PRTKRNLMODINFO pahKrnlModInfo, uint32_t cEntriesMax,
+ uint32_t *pcEntries)
+{
+ if (cEntriesMax > 0)
+ AssertPtrReturn(pahKrnlModInfo, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ RTOnce(&g_GetIoKitApisOnce, rtKrnlModDarwinResolveIoKitApis, NULL);
+ if (g_pfnOSKextCopyLoadedKextInfo)
+ {
+ CFDictionaryRef hLoadedKexts = g_pfnOSKextCopyLoadedKextInfo(NULL, NULL /* all info */);
+ if (hLoadedKexts)
+ {
+ uint32_t cLoadedKexts = CFDictionaryGetCount(hLoadedKexts);
+ if (cLoadedKexts <= cEntriesMax)
+ {
+ CFDictionaryRef *pahDictKext = (CFDictionaryRef *)RTMemTmpAllocZ(cLoadedKexts * sizeof(CFDictionaryRef));
+ if (pahDictKext)
+ {
+ CFDictionaryGetKeysAndValues(hLoadedKexts, NULL, (const void **)pahDictKext);
+ for (uint32_t i = 0; i < cLoadedKexts; i++)
+ {
+ PRTKRNLMODINFOINT pThis = (PRTKRNLMODINFOINT)RTMemAllocZ(sizeof(RTKRNLMODINFOINT));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->cRefs = 1;
+ pThis->hDictKext = pahDictKext[i];
+ CFRetain(pThis->hDictKext);
+ pahKrnlModInfo[i] = pThis;
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ /* Rollback. */
+ while (i-- > 0)
+ {
+ CFRelease(pahKrnlModInfo[i]->hDictKext);
+ RTMemFree(pahKrnlModInfo[i]);
+ }
+ }
+ }
+
+ if ( RT_SUCCESS(rc)
+ && pcEntries)
+ *pcEntries = cLoadedKexts;
+
+ RTMemTmpFree(pahDictKext);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ rc = VERR_BUFFER_OVERFLOW;
+
+ if (pcEntries)
+ *pcEntries = cLoadedKexts;
+ }
+
+ CFRelease(hLoadedKexts);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoRetain(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoRelease(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ if (!pThis)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0)
+ rtKrnlModInfoDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoGetRefCnt(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ uint32_t cRefCnt = 0;
+ CFNumberRef hRetainCnt = (CFNumberRef)CFDictionaryGetValue(pThis->hDictKext,
+ kOSBundleRetainCountKey);
+ if (hRetainCnt)
+ CFNumberGetValue(hRetainCnt, kCFNumberSInt32Type, &cRefCnt);
+
+ return cRefCnt;
+}
+
+
+RTDECL(const char *) RTKrnlModInfoGetName(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, NULL);
+
+ const char *pszName = NULL;
+ CFStringRef hBundleId = (CFStringRef)CFDictionaryGetValue(pThis->hDictKext,
+ kCFBundleIdentifierKey);
+ if (hBundleId)
+ pszName = CFStringGetCStringPtr(hBundleId, kCFStringEncodingUTF8);
+
+ return pszName;
+}
+
+
+RTDECL(const char *) RTKrnlModInfoGetFilePath(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, NULL);
+
+ return NULL;
+}
+
+
+RTDECL(size_t) RTKrnlModInfoGetSize(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ size_t cbKrnlMod = 0;
+ CFNumberRef hKrnlModSize = (CFNumberRef)CFDictionaryGetValue(pThis->hDictKext,
+ kOSBundleLoadSizeKey);
+ if (hKrnlModSize)
+ {
+ uint32_t cbTmp = 0;
+ CFNumberGetValue(hKrnlModSize, kCFNumberSInt32Type, &cbTmp);
+ cbKrnlMod = cbTmp;
+ }
+
+ return cbKrnlMod;
+}
+
+
+RTDECL(RTR0UINTPTR) RTKrnlModInfoGetLoadAddr(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ RTR0UINTPTR uKrnlModLoadAddr = 0;
+ CFNumberRef hKrnlModLoadAddr = (CFNumberRef)CFDictionaryGetValue(pThis->hDictKext,
+ kOSBundleLoadAddressKey);
+ if (hKrnlModLoadAddr)
+ {
+ uint64_t uAddrTmp = 0;
+ CFNumberGetValue(hKrnlModLoadAddr, kCFNumberSInt64Type, &uAddrTmp);
+ uKrnlModLoadAddr = uAddrTmp;
+ }
+
+ return uKrnlModLoadAddr;
+}
+
+
+RTDECL(int) RTKrnlModInfoQueryRefModInfo(RTKRNLMODINFO hKrnlModInfo, uint32_t idx,
+ PRTKRNLMODINFO phKrnlModInfoRef)
+{
+ RT_NOREF3(hKrnlModInfo, idx, phKrnlModInfoRef);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTDECL(int) RTKrnlModLoadByName(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ RTOnce(&g_GetIoKitApisOnce, rtKrnlModDarwinResolveIoKitApis, NULL);
+ if (!g_pfnKextManagerLoadKextWithIdentifier) return VERR_NOT_SUPPORTED;
+
+ int rc = VINF_SUCCESS;
+ CFStringRef hKextName = CFStringCreateWithCString(kCFAllocatorDefault, pszName, kCFStringEncodingUTF8);
+ if (hKextName)
+ {
+ OSReturn rcOsx = g_pfnKextManagerLoadKextWithIdentifier(hKextName, NULL /*dependencyKextAndFolderURLs*/);
+ if (rcOsx != kOSReturnSuccess)
+ rc = VERR_NOT_SUPPORTED; /** @todo Convert OSReturn values. */
+ CFRelease(hKextName);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTKrnlModLoadByPath(const char *pszPath)
+{
+ AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModUnloadByName(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ RTOnce(&g_GetIoKitApisOnce, rtKrnlModDarwinResolveIoKitApis, NULL);
+ if (!g_pfnKextManagerUnloadKextWithIdentifier) return VERR_NOT_SUPPORTED;
+
+ int rc = VINF_SUCCESS;
+ CFStringRef hKextName = CFStringCreateWithCString(kCFAllocatorDefault, pszName, kCFStringEncodingUTF8);
+ if (hKextName)
+ {
+ OSReturn rcOsx = g_pfnKextManagerUnloadKextWithIdentifier(hKextName);
+ if (rcOsx != kOSReturnSuccess)
+ rc = VERR_NOT_SUPPORTED; /** @todo Convert OSReturn values. */
+ CFRelease(hKextName);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
diff --git a/src/VBox/Runtime/r3/darwin/mp-darwin.cpp b/src/VBox/Runtime/r3/darwin/mp-darwin.cpp
new file mode 100644
index 00000000..75c5433e
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/mp-darwin.cpp
@@ -0,0 +1,435 @@
+/* $Id: mp-darwin.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Darwin.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT /*RTLOGGROUP_SYSTEM*/
+#include <iprt/types.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <errno.h>
+#include <mach/mach.h>
+
+#include <CoreFoundation/CFBase.h>
+#include <IOKit/IOKitLib.h>
+/*#include <IOKit/IOBSD.h>
+#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
+#include <IOKit/storage/IOBlockStorageDevice.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IOCDMedia.h>
+#include <IOKit/scsi/SCSITaskLib.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <mach/mach_error.h>
+#include <sys/param.h>
+#include <paths.h>*/
+
+#include <iprt/mp.h>
+#include <iprt/assert.h>
+#include <iprt/cpuset.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+
+
+/**
+ * Internal worker that determines the max possible logical CPU count (hyperthreads).
+ *
+ * @returns Max cpus.
+ */
+static RTCPUID rtMpDarwinMaxLogicalCpus(void)
+{
+ int cCpus = -1;
+ size_t cb = sizeof(cCpus);
+ int rc = sysctlbyname("hw.logicalcpu_max", &cCpus, &cb, NULL, 0);
+ if (rc != -1 && cCpus >= 1)
+ return cCpus;
+ AssertFailed();
+ return 1;
+}
+
+/**
+ * Internal worker that determines the max possible physical core count.
+ *
+ * @returns Max cpus.
+ */
+static RTCPUID rtMpDarwinMaxPhysicalCpus(void)
+{
+ int cCpus = -1;
+ size_t cb = sizeof(cCpus);
+ int rc = sysctlbyname("hw.physicalcpu_max", &cCpus, &cb, NULL, 0);
+ if (rc != -1 && cCpus >= 1)
+ return cCpus;
+ AssertFailed();
+ return 1;
+}
+
+
+#if 0 /* unused */
+/**
+ * Internal worker that determines the current number of logical CPUs (hyperthreads).
+ *
+ * @returns Max cpus.
+ */
+static RTCPUID rtMpDarwinOnlineLogicalCpus(void)
+{
+ int cCpus = -1;
+ size_t cb = sizeof(cCpus);
+ int rc = sysctlbyname("hw.logicalcpu", &cCpus, &cb, NULL, 0);
+ if (rc != -1 && cCpus >= 1)
+ return cCpus;
+ AssertFailed();
+ return 1;
+}
+#endif /* unused */
+
+
+/**
+ * Internal worker that determines the current number of physical CPUs.
+ *
+ * @returns Max cpus.
+ */
+static RTCPUID rtMpDarwinOnlinePhysicalCpus(void)
+{
+ int cCpus = -1;
+ size_t cb = sizeof(cCpus);
+ int rc = sysctlbyname("hw.physicalcpu", &cCpus, &cb, NULL, 0);
+ if (rc != -1 && cCpus >= 1)
+ return cCpus;
+ AssertFailed();
+ return 1;
+}
+
+
+#if defined(RT_ARCH_ARM64)
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ /* xnu-7195.50.7.100.1/osfmk/arm64/start.s and machine_routines.c sets TPIDRRO_EL0
+ to the cpu_data_t::cpu_id value. */
+ uint64_t u64Ret;
+ __asm__ __volatile__("mrs %0,TPIDRRO_EL0\n\t" : "=r" (u64Ret));
+ return (RTCPUID)u64Ret;
+}
+#elif defined(RT_ARCH_ARM32)
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ /* xnu-7195.50.7.100.1/osfmk/arm/start.s and machine_routines.c sets TPIDRURO
+ to the cpu_data_t::cpu_id value. */
+ uint32_t u32Ret;
+ __asm__ __volatile__("mrs p15, 0, %0, c13, c0, 3\n\t" : "=r" (u32Ret));
+ return (RTCPUID)u32Ret;
+}
+#endif
+
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS && idCpu < rtMpDarwinMaxLogicalCpus() ? idCpu : -1;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu < rtMpDarwinMaxLogicalCpus() ? iCpu : NIL_RTCPUID;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return rtMpDarwinMaxLogicalCpus() - 1;
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+#if 0
+ return RTMpIsCpuPossible(idCpu);
+#else
+ /** @todo proper ring-3 support on darwin, see @bugref{3014}. */
+ natural_t cCpus;
+ processor_basic_info_t paInfo;
+ mach_msg_type_number_t cInfo;
+ kern_return_t krc = host_processor_info(mach_host_self(), PROCESSOR_BASIC_INFO,
+ &cCpus, (processor_info_array_t*)&paInfo, &cInfo);
+ AssertReturn(krc == KERN_SUCCESS, true);
+ bool const fIsOnline = idCpu < cCpus ? paInfo[idCpu].running : false;
+ vm_deallocate(mach_task_self(), (vm_address_t)paInfo, cInfo * sizeof(paInfo[0]));
+ return fIsOnline;
+#endif
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ return idCpu != NIL_RTCPUID
+ && idCpu < rtMpDarwinMaxLogicalCpus();
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+#if 0
+ RTCPUID cCpus = rtMpDarwinMaxLogicalCpus();
+ return RTCpuSetFromU64(RT_BIT_64(cCpus) - 1);
+
+#else
+ RTCpuSetEmpty(pSet);
+ RTCPUID cMax = rtMpDarwinMaxLogicalCpus();
+ for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
+ if (RTMpIsCpuPossible(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ return pSet;
+#endif
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ return rtMpDarwinMaxLogicalCpus();
+}
+
+
+RTDECL(RTCPUID) RTMpGetCoreCount(void)
+{
+ return rtMpDarwinMaxPhysicalCpus();
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ RTCpuSetEmpty(pSet);
+#if 0
+ RTCPUID cMax = rtMpDarwinMaxLogicalCpus();
+ for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
+ if (RTMpIsCpuOnline(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+#else
+ natural_t cCpus = 0;
+ processor_basic_info_t paInfo = NULL;
+ mach_msg_type_number_t cInfo = 0;
+ kern_return_t krc = host_processor_info(mach_host_self(), PROCESSOR_BASIC_INFO,
+ &cCpus, (processor_info_array_t *)&paInfo, &cInfo);
+ AssertReturn(krc == KERN_SUCCESS, pSet);
+
+ AssertStmt(cCpus <= RTCPUSET_MAX_CPUS, cCpus = RTCPUSET_MAX_CPUS);
+ for (natural_t idCpu = 0; idCpu < cCpus; idCpu++)
+ if (paInfo[idCpu].running)
+ RTCpuSetAdd(pSet, idCpu);
+
+ vm_deallocate(mach_task_self(), (vm_address_t)paInfo, cInfo * sizeof(paInfo[0]));
+#endif
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ RTCPUSET Set;
+ RTMpGetOnlineSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
+{
+ return rtMpDarwinOnlinePhysicalCpus();
+}
+
+
+RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
+{
+ /** @todo figure out how to get the current cpu speed on darwin. Have to
+ * check what powermanagement does. The powermetrics uses a private
+ * IOReportXxxx interface and *seems* (guessing) to calculate the frequency
+ * based on the frequency distribution over the last report period... This
+ * means that it's not really an suitable API for here. */
+ NOREF(idCpu);
+ return 0;
+}
+
+
+/**
+ * Worker for RTMpGetMaxFrequency.
+ * @returns Non-zero frequency in MHz on success, 0 on failure.
+ */
+static uint32_t rtMpDarwinGetMaxFrequencyFromIOService(io_service_t hCpu)
+{
+ io_struct_inband_t Buf = {0};
+ uint32_t cbActual = sizeof(Buf);
+ kern_return_t krc = IORegistryEntryGetProperty(hCpu, "clock-frequency", Buf, &cbActual);
+ Log2(("rtMpDarwinGetMaxFrequencyFromIOService: krc=%d; cbActual=%#x %.16Rhxs\n", krc, cbActual, Buf));
+ if (krc == kIOReturnSuccess)
+ {
+ switch (cbActual)
+ {
+ case sizeof(uint32_t):
+ return RT_BE2H_U32(*(uint32_t *)Buf) / 1000;
+ case sizeof(uint64_t):
+ AssertFailed();
+ return RT_BE2H_U64(*(uint64_t *)Buf) / 1000;
+ default:
+ AssertFailed();
+ }
+ }
+ return 0;
+}
+
+
+RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
+{
+ if (!RTMpIsCpuOnline(idCpu))
+ return 0;
+
+ /*
+ * Try the 'hw.cpufrequency_max' one.
+ */
+ uint64_t CpuFrequencyMax = 0;
+ size_t cb = sizeof(CpuFrequencyMax);
+ int rc = sysctlbyname("hw.cpufrequency_max", &CpuFrequencyMax, &cb, NULL, 0);
+ if (!rc)
+ return (CpuFrequencyMax + 999999) / 1000000;
+
+ /*
+ * Use the deprecated one.
+ */
+ int aiMib[2];
+ aiMib[0] = CTL_HW;
+ aiMib[1] = HW_CPU_FREQ;
+ int iDeprecatedFrequency = -1;
+ cb = sizeof(iDeprecatedFrequency);
+ rc = sysctl(aiMib, RT_ELEMENTS(aiMib), &iDeprecatedFrequency, &cb, NULL, 0);
+ if (rc != -1 && iDeprecatedFrequency >= 1)
+ return iDeprecatedFrequency;
+
+ /*
+ * The above does not work for Apple M1 / xnu 20.1.0, so go look at the I/O registry instead.
+ *
+ * A sample ARM layout:
+ * | +-o cpu1@1 <class IOPlatformDevice, id 0x100000110, registered, matched, active, busy 0 (182 ms), retain 8>
+ * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021b, registered, matched, active, busy 0 (1 ms), retain 6>
+ * | +-o cpu2@2 <class IOPlatformDevice, id 0x100000111, registered, matched, active, busy 0 (175 ms), retain 8>
+ * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021c, registered, matched, active, busy 0 (3 ms), retain 6>
+ * | +-o cpu3@3 <class IOPlatformDevice, id 0x100000112, registered, matched, active, busy 0 (171 ms), retain 8>
+ * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021d, registered, matched, active, busy 0 (1 ms), retain 6>
+ * | +-o cpu4@100 <class IOPlatformDevice, id 0x100000113, registered, matched, active, busy 0 (171 ms), retain 8>
+ * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021e, registered, matched, active, busy 0 (1 ms), retain 6>
+ * | +-o cpu5@101 <class IOPlatformDevice, id 0x100000114, registered, matched, active, busy 0 (179 ms), retain 8>
+ * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021f, registered, matched, active, busy 0 (9 ms), retain 6>
+ * | +-o cpu6@102 <class IOPlatformDevice, id 0x100000115, registered, matched, active, busy 0 (172 ms), retain 8>
+ * | | +-o AppleARMCPU <class AppleARMCPU, id 0x100000220, registered, matched, active, busy 0 (1 ms), retain 6>
+ * | +-o cpu7@103 <class IOPlatformDevice, id 0x100000116, registered, matched, active, busy 0 (175 ms), retain 8>
+ * | | +-o AppleARMCPU <class AppleARMCPU, id 0x100000221, registered, matched, active, busy 0 (5 ms), retain 6>
+ * | +-o cpus <class IOPlatformDevice, id 0x10000010e, registered, matched, active, busy 0 (12 ms), retain 15>
+ *
+ */
+
+#if 1 /* simpler way to get at it inspired by powermetrics, this is also used
+ in the arm version of RTMpGetDescription. */
+ /* Assume names on the form "cpu<N>" are only for CPUs. */
+ char szCpuPath[64];
+# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ RTStrPrintf(szCpuPath, sizeof(szCpuPath), "IODeviceTree:/cpus/CPU%X", idCpu);
+# else
+ RTStrPrintf(szCpuPath, sizeof(szCpuPath), "IODeviceTree:/cpus/cpu%x", idCpu); /** @todo Hex? M1 Max only has 10 cores... */
+# endif
+ io_registry_entry_t hIoRegEntry = IORegistryEntryFromPath(kIOMasterPortDefault, szCpuPath);
+ if (hIoRegEntry != MACH_PORT_NULL)
+ {
+ uint32_t uCpuFrequency = rtMpDarwinGetMaxFrequencyFromIOService(hIoRegEntry);
+ IOObjectRelease(hIoRegEntry);
+ if (uCpuFrequency)
+ return uCpuFrequency;
+ }
+
+#else
+ /* Assume names on the form "cpu<N>" are only for CPUs. */
+ char szCpuName[32];
+ RTStrPrintf(szCpuName, sizeof(szCpuName), "cpu%u", idCpu);
+ CFMutableDictionaryRef hMatchingDict = IOServiceNameMatching(szCpuName);
+ AssertReturn(hMatchingDict, 0);
+
+ /* Just get the first one. */
+ io_object_t hCpu = IOServiceGetMatchingService(kIOMasterPortDefault, hMatchingDict);
+ if (hCpu != 0)
+ {
+ uint32_t uCpuFrequency = rtMpDarwinGetMaxFrequencyFromIOService(hCpu);
+ IOObjectRelease(hCpu);
+ if (uCpuFrequency)
+ return uCpuFrequency;
+ }
+
+# if 1 /* Just in case... */
+ /* Create a matching dictionary for searching for CPU services in the IOKit. */
+# if defined(RT_ARCH_ARM64) || defined(RT_ARCH_ARM32)
+ hMatchingDict = IOServiceMatching("AppleARMCPU");
+# elif defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ hMatchingDict = IOServiceMatching("AppleACPICPU");
+# else
+# error "Port me!"
+# endif
+ AssertReturn(hMatchingDict, 0);
+
+ /* Perform the search and get a collection of Apple CPU services. */
+ io_iterator_t hCpuServices = IO_OBJECT_NULL;
+ IOReturn irc = IOServiceGetMatchingServices(kIOMasterPortDefault, hMatchingDict, &hCpuServices);
+ AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), 0);
+ hMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
+
+ /* Enumerate the matching services. */
+ uint32_t uCpuFrequency = 0;
+ io_object_t hCurCpu;
+ while (uCpuFrequency == 0 && (hCurCpu = IOIteratorNext(hCpuServices)) != IO_OBJECT_NULL)
+ {
+ io_object_t hParent = (io_object_t)0;
+ irc = IORegistryEntryGetParentEntry(hCurCpu, kIOServicePlane, &hParent);
+ if (irc == kIOReturnSuccess && hParent)
+ {
+ uCpuFrequency = rtMpDarwinGetMaxFrequencyFromIOService(hParent);
+ IOObjectRelease(hParent);
+ }
+ IOObjectRelease(hCurCpu);
+ }
+ IOObjectRelease(hCpuServices);
+# endif
+#endif
+ AssertFailed();
+ return 0;
+}
+
diff --git a/src/VBox/Runtime/r3/darwin/pathhost-darwin.cpp b/src/VBox/Runtime/r3/darwin/pathhost-darwin.cpp
new file mode 100644
index 00000000..58bcd1ec
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/pathhost-darwin.cpp
@@ -0,0 +1,117 @@
+/* $Id: pathhost-darwin.cpp $ */
+/** @file
+ * IPRT - Path Conversions, Darwin.
+ *
+ * On darwin path names on the disk are decomposed using normalization
+ * form D (NFD). Since this behavior is unique for the Mac, we will precompose
+ * the path name strings we get from the XNU kernel.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PATH
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include "internal/iprt.h"
+
+#include "internal/path.h"
+
+
+int rtPathToNative(const char **ppszNativePath, const char *pszPath, const char *pszBasePath)
+{
+ /** @todo We should decompose the string here, but the file system will do
+ * that for us if we don't, so why bother. */
+ *ppszNativePath = (char *)pszPath;
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return VINF_SUCCESS;
+}
+
+
+void rtPathFreeNative(char const *pszNativePath, const char *pszPath)
+{
+ Assert(pszNativePath == pszPath || !pszNativePath);
+ NOREF(pszNativePath);
+ NOREF(pszPath);
+}
+
+
+int rtPathFromNative(char const **ppszPath, const char *pszNativePath, const char *pszBasePath)
+{
+ /** @todo We must compose the codepoints in the string here. We get file names
+ * in normalization form D so we'll end up with normalization form C
+ * whatever approach we take. */
+ int rc = RTStrValidateEncodingEx(pszNativePath, RTSTR_MAX, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ *ppszPath = pszNativePath;
+ else
+ *ppszPath = NULL;
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return rc;
+}
+
+
+void rtPathFreeIprt(const char *pszPath, const char *pszNativePath)
+{
+ Assert(pszPath == pszNativePath || !pszPath);
+ NOREF(pszPath); NOREF(pszNativePath);
+}
+
+
+int rtPathFromNativeCopy(char *pszPath, size_t cbPath, const char *pszNativePath, const char *pszBasePath)
+{
+ /** @todo We must compose the codepoints in the string here. We get file names
+ * in normalization form D so we'll end up with normalization form C
+ * whatever approach we take. */
+ int rc = RTStrValidateEncodingEx(pszNativePath, RTSTR_MAX, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ rc = RTStrCopyEx(pszPath, cbPath, pszNativePath, RTSTR_MAX);
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return rc;
+}
+
+
+int rtPathFromNativeDup(char **ppszPath, const char *pszNativePath, const char *pszBasePath)
+{
+ /** @todo We must compose the codepoints in the string here. We get file names
+ * in normalization form D so we'll end up with normalization form C
+ * whatever approach we take. */
+ int rc = RTStrValidateEncodingEx(pszNativePath, RTSTR_MAX, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ rc = RTStrDupEx(ppszPath, pszNativePath);
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/darwin/rtProcInitExePath-darwin.cpp b/src/VBox/Runtime/r3/darwin/rtProcInitExePath-darwin.cpp
new file mode 100644
index 00000000..af6116eb
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/rtProcInitExePath-darwin.cpp
@@ -0,0 +1,76 @@
+/* $Id: rtProcInitExePath-darwin.cpp $ */
+/** @file
+ * IPRT - rtProcInitName, Darwin.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#ifdef RT_OS_DARWIN
+# include <mach-o/dyld.h>
+#endif
+
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include "internal/process.h"
+#include "internal/path.h"
+
+
+DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath)
+{
+ /*
+ * Query the image name from the dynamic linker, convert and return it.
+ */
+ const char *pszImageName = _dyld_get_image_name(0);
+ AssertReturn(pszImageName, VERR_INTERNAL_ERROR);
+
+ char szTmpPath[PATH_MAX + 1];
+ const char *psz = realpath(pszImageName, szTmpPath);
+ int rc;
+ if (psz)
+ rc = rtPathFromNativeCopy(pszPath, cchPath, szTmpPath, NULL);
+ else
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, strlen(pszImageName), pszPath), rc);
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/darwin/sched-darwin.cpp b/src/VBox/Runtime/r3/darwin/sched-darwin.cpp
new file mode 100644
index 00000000..3e819cb0
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/sched-darwin.cpp
@@ -0,0 +1,363 @@
+/* $Id: sched-darwin.cpp $ */
+/** @file
+ * IPRT - Scheduling, Darwin.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <mach/thread_act.h>
+#include <mach/thread_policy.h>
+#include <mach/thread_info.h>
+#include <mach/host_info.h>
+#include <mach/mach_init.h>
+#include <mach/mach_host.h>
+#include <sched.h>
+#include <pthread.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include "internal/sched.h"
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Configuration of one priority.
+ */
+typedef struct
+{
+ /** The priority. */
+ RTPROCPRIORITY enmPriority;
+ /** The name of this priority. */
+ const char *pszName;
+ /** Array scheduler attributes corresponding to each of the thread types. */
+ struct
+ {
+ /** For sanity include the array index. */
+ RTTHREADTYPE enmType;
+ /** The desired mach base_priority value. */
+ int iBasePriority;
+ /** The suggested priority value. (Same as iBasePriority seems to do the
+ * trick.) */
+ int iPriority;
+ } aTypes[RTTHREADTYPE_END];
+} PROCPRIORITY;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Array of static priority configurations.
+ *
+ * ASSUMES that pthread_setschedparam takes a sched_priority argument in the
+ * range 0..127, which is translated into mach base_priority 0..63 and mach
+ * importance -31..32 (among other things). We also ASSUMES SCHED_OTHER.
+ *
+ * The base_priority range can be checked with tstDarwinSched, we're assuming it's
+ * 0..63 for user processes.
+ *
+ * Further we observe that fseventsd and mds both run at (mach) priority 50,
+ * while Finder runs at 47. At priority 63 we find the dynamic pager, the login
+ * window, UserEventAgent, SystemUIServer and coreaudiod. We do not wish to upset the
+ * dynamic pager, UI or audio, but we wish for I/O to not be bothered by spotlight
+ * (mds/fseventsd).
+ */
+static const PROCPRIORITY g_aPriorities[] =
+{
+ {
+ RTPROCPRIORITY_DEFAULT, "Default",
+ {
+ { RTTHREADTYPE_INVALID, INT_MIN, INT_MIN },
+ { RTTHREADTYPE_INFREQUENT_POLLER, 29, 29 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, 30, 30 },
+ { RTTHREADTYPE_EMULATION, 31, 31 }, /* the default priority */
+ { RTTHREADTYPE_DEFAULT, 32, 32 },
+ { RTTHREADTYPE_GUI, 32, 32 },
+ { RTTHREADTYPE_MAIN_WORKER, 32, 32 },
+ { RTTHREADTYPE_VRDP_IO, 39, 39 },
+ { RTTHREADTYPE_DEBUGGER, 42, 42 },
+ { RTTHREADTYPE_MSG_PUMP, 47, 47 },
+ { RTTHREADTYPE_IO, 52, 52 },
+ { RTTHREADTYPE_TIMER, 55, 55 }
+ }
+ },
+ {
+ RTPROCPRIORITY_LOW, "Low",
+ {
+ { RTTHREADTYPE_INVALID, INT_MIN, INT_MIN },
+ { RTTHREADTYPE_INFREQUENT_POLLER, 20, 20 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, 22, 22 },
+ { RTTHREADTYPE_EMULATION, 24, 24 },
+ { RTTHREADTYPE_DEFAULT, 28, 28 },
+ { RTTHREADTYPE_GUI, 29, 29 },
+ { RTTHREADTYPE_MAIN_WORKER, 30, 30 },
+ { RTTHREADTYPE_VRDP_IO, 31, 31 },
+ { RTTHREADTYPE_DEBUGGER, 31, 31 },
+ { RTTHREADTYPE_MSG_PUMP, 31, 31 },
+ { RTTHREADTYPE_IO, 31, 31 },
+ { RTTHREADTYPE_TIMER, 31, 31 }
+ }
+ },
+ {
+ RTPROCPRIORITY_NORMAL, "Normal",
+ {
+ { RTTHREADTYPE_INVALID, INT_MIN, INT_MIN },
+ { RTTHREADTYPE_INFREQUENT_POLLER, 29, 29 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, 30, 30 },
+ { RTTHREADTYPE_EMULATION, 31, 31 }, /* the default priority */
+ { RTTHREADTYPE_DEFAULT, 32, 32 },
+ { RTTHREADTYPE_GUI, 32, 32 },
+ { RTTHREADTYPE_MAIN_WORKER, 32, 32 },
+ { RTTHREADTYPE_VRDP_IO, 39, 39 },
+ { RTTHREADTYPE_DEBUGGER, 42, 42 },
+ { RTTHREADTYPE_MSG_PUMP, 47, 47 },
+ { RTTHREADTYPE_IO, 52, 52 },
+ { RTTHREADTYPE_TIMER, 55, 55 }
+ }
+ },
+ {
+ RTPROCPRIORITY_HIGH, "High",
+ {
+ { RTTHREADTYPE_INVALID, INT_MIN, INT_MIN },
+ { RTTHREADTYPE_INFREQUENT_POLLER, 30, 30 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, 31, 31 },
+ { RTTHREADTYPE_EMULATION, 32, 32 },
+ { RTTHREADTYPE_DEFAULT, 40, 40 },
+ { RTTHREADTYPE_GUI, 41, 41 },
+ { RTTHREADTYPE_MAIN_WORKER, 43, 43 },
+ { RTTHREADTYPE_VRDP_IO, 45, 45 },
+ { RTTHREADTYPE_DEBUGGER, 47, 47 },
+ { RTTHREADTYPE_MSG_PUMP, 49, 49 },
+ { RTTHREADTYPE_IO, 57, 57 },
+ { RTTHREADTYPE_TIMER, 61, 61 }
+ }
+ },
+ /* last */
+ {
+ RTPROCPRIORITY_FLAT, "Flat",
+ {
+ { RTTHREADTYPE_INVALID, INT_MIN, INT_MIN },
+ { RTTHREADTYPE_INFREQUENT_POLLER, 31, 31 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, 31, 31 },
+ { RTTHREADTYPE_EMULATION, 31, 31 },
+ { RTTHREADTYPE_DEFAULT, 31, 31 },
+ { RTTHREADTYPE_GUI, 31, 31 },
+ { RTTHREADTYPE_MAIN_WORKER, 31, 31 },
+ { RTTHREADTYPE_VRDP_IO, 31, 31 },
+ { RTTHREADTYPE_DEBUGGER, 31, 31 },
+ { RTTHREADTYPE_MSG_PUMP, 31, 31 },
+ { RTTHREADTYPE_IO, 31, 31 },
+ { RTTHREADTYPE_TIMER, 31, 31 }
+ }
+ },
+};
+
+
+/**
+ * The dynamic default priority configuration.
+ *
+ * This can be recalulated at runtime depending on what the
+ * system allow us to do. Presently we don't do this as it seems
+ * Darwin generally lets us do whatever we want.
+ *
+ * @remarks this is the same as "Normal" above.
+ */
+static PROCPRIORITY g_aDefaultPriority =
+{
+ RTPROCPRIORITY_DEFAULT, "Default",
+ {
+ { RTTHREADTYPE_INVALID, INT_MIN, INT_MIN },
+ { RTTHREADTYPE_INFREQUENT_POLLER, 29, 29 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, 30, 30 },
+ { RTTHREADTYPE_EMULATION, 31, 31 }, /* the default priority */
+ { RTTHREADTYPE_DEFAULT, 32, 32 },
+ { RTTHREADTYPE_GUI, 32, 32 },
+ { RTTHREADTYPE_MAIN_WORKER, 32, 32 },
+ { RTTHREADTYPE_VRDP_IO, 39, 39 },
+ { RTTHREADTYPE_DEBUGGER, 42, 42 },
+ { RTTHREADTYPE_MSG_PUMP, 47, 47 },
+ { RTTHREADTYPE_IO, 52, 52 },
+ { RTTHREADTYPE_TIMER, 55, 55 }
+ }
+};
+
+
+/** Pointer to the current priority configuration. */
+static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority;
+
+
+/**
+ * Get's the priority information for the current thread.
+ *
+ * @returns The base priority
+ * @param pThread The thread to get it for. NULL for current.
+ */
+static int rtSchedDarwinGetBasePriority(PRTTHREADINT pThread)
+{
+ /* the base_priority. */
+ mach_msg_type_number_t Count = POLICY_TIMESHARE_INFO_COUNT;
+ struct policy_timeshare_info TSInfo = {0,0,0,0,0};
+ kern_return_t krc = thread_info(!pThread ? mach_thread_self() : pthread_mach_thread_np((pthread_t)pThread->Core.Key),
+ THREAD_SCHED_TIMESHARE_INFO, (thread_info_t)&TSInfo, &Count);
+ Assert(krc == KERN_SUCCESS); NOREF(krc);
+
+ return TSInfo.base_priority;
+}
+
+
+DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+
+ /*
+ * Get the current priority.
+ */
+ int iBasePriority = rtSchedDarwinGetBasePriority(NULL);
+ Assert(iBasePriority >= 0 && iBasePriority <= 63);
+
+ /*
+ * If it doesn't match the default, select the closest one from the table.
+ */
+ int offBest = RT_ABS(g_pProcessPriority->aTypes[enmType].iBasePriority - iBasePriority);
+ if (offBest)
+ {
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aPriorities); i++)
+ {
+ int off = RT_ABS(g_aPriorities[i].aTypes[enmType].iBasePriority - iBasePriority);
+ if (off < offBest)
+ {
+ g_pProcessPriority = &g_aPriorities[i];
+ if (!off)
+ break;
+ offBest = off;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority)
+{
+ Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST);
+
+ /*
+ * No checks necessary, we assume we can set any priority in the user process range.
+ */
+ const PROCPRIORITY *pProcessPriority = &g_aDefaultPriority;
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aPriorities); i++)
+ if (g_aPriorities[i].enmPriority == enmPriority)
+ {
+ pProcessPriority = &g_aPriorities[i];
+ break;
+ }
+ Assert(pProcessPriority != &g_aDefaultPriority);
+ ASMAtomicUoWritePtr(&g_pProcessPriority, pProcessPriority);
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ AssertMsg(g_pProcessPriority && g_pProcessPriority->aTypes[enmType].enmType == enmType,
+ ("enmType=%d entry=%d\n", enmType, g_pProcessPriority->aTypes[enmType].enmType));
+
+ /*
+ * Get the current policy and params first since there are
+ * opaque members in the param structure and we don't wish to
+ * change the policy.
+ */
+ int iSchedPolicy = SCHED_OTHER;
+ struct sched_param SchedParam = {0, {0,0,0,0} };
+ int err = pthread_getschedparam((pthread_t)pThread->Core.Key, &iSchedPolicy, &SchedParam);
+ if (!err)
+ {
+ int const iDesiredBasePriority = g_pProcessPriority->aTypes[enmType].iBasePriority;
+ int iPriority = g_pProcessPriority->aTypes[enmType].iPriority;
+
+ /*
+ * First try with the given pthread priority number.
+ * Then make adjustments in case we missed the desired base priority (interface
+ * changed or whatever - its using an obsolete mach api).
+ */
+ SchedParam.sched_priority = iPriority;
+ err = pthread_setschedparam((pthread_t)pThread->Core.Key, iSchedPolicy, &SchedParam);
+ if (!err)
+ {
+ int i = 0;
+ int iBasePriority = rtSchedDarwinGetBasePriority(pThread);
+
+ while ( !err
+ && iBasePriority < iDesiredBasePriority
+ && i++ < 256)
+ {
+ SchedParam.sched_priority = ++iPriority;
+ err = pthread_setschedparam((pthread_t)pThread->Core.Key, iSchedPolicy, &SchedParam);
+ iBasePriority = rtSchedDarwinGetBasePriority(pThread);
+ }
+
+ while ( !err
+ && iPriority > 0
+ && iBasePriority > iDesiredBasePriority
+ && i++ < 256)
+ {
+ SchedParam.sched_priority = --iPriority;
+ err = pthread_setschedparam((pthread_t)pThread->Core.Key, iSchedPolicy, &SchedParam);
+ iBasePriority = rtSchedDarwinGetBasePriority(pThread);
+ }
+
+ return VINF_SUCCESS;
+ }
+ }
+ int rc = RTErrConvertFromErrno(err);
+ AssertMsgRC(rc, ("rc=%Rrc err=%d iSchedPolicy=%d sched_priority=%d\n",
+ rc, err, iSchedPolicy, SchedParam.sched_priority));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/darwin/systemmem-darwin.cpp b/src/VBox/Runtime/r3/darwin/systemmem-darwin.cpp
new file mode 100644
index 00000000..8c389d27
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/systemmem-darwin.cpp
@@ -0,0 +1,96 @@
+/* $Id: systemmem-darwin.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryTotalRam, darwin ring-3.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+#include <mach/mach.h>
+
+
+
+RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ int aiMib[2];
+ aiMib[0] = CTL_HW;
+ aiMib[1] = HW_MEMSIZE;
+ uint64_t cbPhysMem = UINT64_MAX;
+ size_t cb = sizeof(cbPhysMem);
+ int rc = sysctl(aiMib, RT_ELEMENTS(aiMib), &cbPhysMem, &cb, NULL, 0);
+ if (rc == 0)
+ {
+ *pcb = cbPhysMem;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ static mach_port_t volatile s_hSelfPort = 0;
+ mach_port_t hSelfPort = s_hSelfPort;
+ if (hSelfPort == 0)
+ s_hSelfPort = hSelfPort = mach_host_self();
+
+ vm_statistics_data_t VmStats;
+ mach_msg_type_number_t cItems = sizeof(VmStats) / sizeof(natural_t);
+
+ kern_return_t krc = host_statistics(hSelfPort, HOST_VM_INFO, (host_info_t)&VmStats, &cItems);
+ if (krc == KERN_SUCCESS)
+ {
+ uint64_t cPages = VmStats.inactive_count;
+ cPages += VmStats.free_count;
+ *pcb = cPages * PAGE_SIZE;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromDarwin(krc);
+}
+
diff --git a/src/VBox/Runtime/r3/darwin/time-darwin.cpp b/src/VBox/Runtime/r3/darwin/time-darwin.cpp
new file mode 100644
index 00000000..bc18badd
--- /dev/null
+++ b/src/VBox/Runtime/r3/darwin/time-darwin.cpp
@@ -0,0 +1,125 @@
+/* $Id: time-darwin.cpp $ */
+/** @file
+ * IPRT - Time, Darwin.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#define RTTIME_INCL_TIMEVAL
+#include <mach/mach_time.h>
+#include <mach/kern_return.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <iprt/time.h>
+#include <iprt/assert.h>
+#include "internal/time.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static struct mach_timebase_info g_Info = { 0, 0 };
+static double g_rdFactor = 0.0;
+static bool g_fFailedToGetTimeBaseInfo = false;
+
+
+/**
+ * Perform lazy init (pray we're not racing anyone in a bad way).
+ */
+static void rtTimeDarwinLazyInit(void)
+{
+ struct mach_timebase_info Info;
+ if (mach_timebase_info(&Info) == KERN_SUCCESS)
+ {
+ g_rdFactor = (double)Info.numer / (double)Info.denom;
+ g_Info = Info;
+ }
+ else
+ {
+ g_fFailedToGetTimeBaseInfo = true;
+ Assert(g_Info.denom == 0 && g_Info.numer == 0 && g_rdFactor == 0.0);
+ }
+}
+
+
+/**
+ * Internal worker.
+ * @returns Nanosecond timestamp.
+ */
+DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void)
+{
+ /* Lazy init. */
+ if (RT_UNLIKELY(g_Info.denom == 0 && !g_fFailedToGetTimeBaseInfo))
+ rtTimeDarwinLazyInit();
+
+ /* special case: absolute time is in nanoseconds */
+ if (g_Info.denom == 1 && g_Info.numer == 1)
+ return mach_absolute_time();
+
+ /* general case: multiply by factor to get nanoseconds. */
+ if (g_rdFactor != 0.0)
+ return mach_absolute_time() * g_rdFactor;
+
+ /* worst case: fallback to gettimeofday(). */
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (uint64_t)tv.tv_sec * RT_NS_1SEC_64
+ + (uint64_t)(tv.tv_usec * RT_NS_1US);
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ /** @todo find nanosecond API for getting time of day. */
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return RTTimeSpecSetTimeval(pTime, &tv);
+}
+
diff --git a/src/VBox/Runtime/r3/dir.cpp b/src/VBox/Runtime/r3/dir.cpp
new file mode 100644
index 00000000..02058632
--- /dev/null
+++ b/src/VBox/Runtime/r3/dir.cpp
@@ -0,0 +1,917 @@
+/* $Id: dir.cpp $ */
+/** @file
+ * IPRT - Directory Manipulation, Part 1.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/dir.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+#define RTDIR_AGNOSTIC
+#include "internal/dir.h"
+#include "internal/path.h"
+
+
+static DECLCALLBACK(bool) rtDirFilterWinNtMatch(PRTDIRINTERNAL pDir, const char *pszName);
+static DECLCALLBACK(bool) rtDirFilterWinNtMatchNoWildcards(PRTDIRINTERNAL pDir, const char *pszName);
+DECLINLINE(bool) rtDirFilterWinNtMatchEon(PCRTUNICP puszFilter);
+static bool rtDirFilterWinNtMatchDosStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter);
+static bool rtDirFilterWinNtMatchStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter);
+static bool rtDirFilterWinNtMatchBase(unsigned iDepth, const char *pszName, PCRTUNICP puszFilter);
+
+
+
+RTDECL(int) RTDirCreateFullPath(const char *pszPath, RTFMODE fMode)
+{
+ return RTDirCreateFullPathEx(pszPath, fMode, 0);
+}
+
+
+RTDECL(int) RTDirCreateFullPathEx(const char *pszPath, RTFMODE fMode, uint32_t fFlags)
+{
+ /*
+ * Resolve the path.
+ */
+ char *pszAbsPath = RTPathAbsDup(pszPath);
+ if (!pszAbsPath)
+ return VERR_NO_TMP_MEMORY;
+
+ /*
+ * Iterate the path components making sure each of them exists.
+ */
+ /* skip volume name */
+ char *psz = &pszAbsPath[rtPathVolumeSpecLen(pszAbsPath)];
+
+ /* skip the root slash if any */
+ if (RTPATH_IS_SLASH(*psz))
+ psz++;
+
+ /* iterate over path components. */
+ int rc = VINF_SUCCESS;
+ do
+ {
+ /* the next component is NULL, stop iterating */
+ if (!*psz)
+ break;
+#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
+ char *psz2 = strchr(psz, '/');
+ psz = strchr(psz, RTPATH_SLASH);
+ if (psz2 && (!psz || (uintptr_t)psz2 < (uintptr_t)psz))
+ psz = psz;
+#else
+ psz = strchr(psz, RTPATH_SLASH);
+#endif
+ if (psz)
+ *psz = '\0';
+
+ /*
+ * ASSUME that RTDirCreate will return VERR_ALREADY_EXISTS and not VERR_ACCESS_DENIED in those cases
+ * where the directory exists but we don't have write access to the parent directory.
+ */
+ rc = RTDirCreate(pszAbsPath, fMode, fFlags);
+ if (rc == VERR_ALREADY_EXISTS)
+ rc = VINF_SUCCESS;
+
+ if (!psz)
+ break;
+ *psz++ = RTPATH_DELIMITER;
+ } while (RT_SUCCESS(rc));
+
+ RTStrFree(pszAbsPath);
+ return rc;
+}
+
+
+/**
+ * Filter a the filename in the against a filter.
+ *
+ * @returns true if the name matches the filter.
+ * @returns false if the name doesn't match filter.
+ * @param pDir The directory handle.
+ * @param pszName The path to match to the filter.
+ */
+static DECLCALLBACK(bool) rtDirFilterWinNtMatchNoWildcards(PRTDIRINTERNAL pDir, const char *pszName)
+{
+ /*
+ * Walk the string and compare.
+ */
+ PCRTUNICP pucFilter = pDir->puszFilter;
+ const char *psz = pszName;
+ RTUNICP uc;
+ do
+ {
+ int rc = RTStrGetCpEx(&psz, &uc);
+ AssertRCReturn(rc, false);
+ RTUNICP ucFilter = *pucFilter++;
+ if ( uc != ucFilter
+ && RTUniCpToUpper(uc) != ucFilter)
+ return false;
+ } while (uc);
+ return true;
+}
+
+
+/**
+ * Matches end of name.
+ */
+DECLINLINE(bool) rtDirFilterWinNtMatchEon(PCRTUNICP puszFilter)
+{
+ RTUNICP ucFilter;
+ while ( (ucFilter = *puszFilter) == '>'
+ || ucFilter == '<'
+ || ucFilter == '*'
+ || ucFilter == '"')
+ puszFilter++;
+ return !ucFilter;
+}
+
+
+/**
+ * Recursive star matching.
+ * Practically the same as normal star, except that the dos star stops
+ * when hitting the last dot.
+ *
+ * @returns true on match.
+ * @returns false on miss.
+ */
+static bool rtDirFilterWinNtMatchDosStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter)
+{
+ AssertReturn(iDepth++ < 256, false);
+
+ /*
+ * If there is no dos star, we should work just like the NT star.
+ * Since that's generally faster algorithms, we jump down to there if we can.
+ */
+ const char *pszDosDot = strrchr(pszNext, '.');
+ if (!pszDosDot && uc == '.')
+ pszDosDot = pszNext - 1;
+ if (!pszDosDot)
+ return rtDirFilterWinNtMatchStar(iDepth, uc, pszNext, puszFilter);
+
+ /*
+ * Inspect the next filter char(s) until we find something to work on.
+ */
+ RTUNICP ucFilter = *puszFilter++;
+ switch (ucFilter)
+ {
+ /*
+ * The star expression is the last in the pattern.
+ * We're fine if the name ends with a dot.
+ */
+ case '\0':
+ return !pszDosDot[1];
+
+ /*
+ * Simplified by brute force.
+ */
+ case '>': /* dos question mark */
+ case '?':
+ case '*':
+ case '<': /* dos star */
+ case '"': /* dos dot */
+ {
+ puszFilter--;
+ const char *pszStart = pszNext;
+ do
+ {
+ if (rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
+ return true;
+ int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
+ } while ((intptr_t)pszDosDot - (intptr_t)pszNext >= -1);
+
+ /* backtrack and do the current char. */
+ pszNext = RTStrPrevCp(NULL, pszStart); AssertReturn(pszNext, false);
+ return rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter);
+ }
+
+ /*
+ * Ok, we've got zero or more characters.
+ * We'll try match starting at each occurrence of this character.
+ */
+ default:
+ {
+ if ( RTUniCpToUpper(uc) == ucFilter
+ && rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
+ return true;
+ do
+ {
+ int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
+ if ( RTUniCpToUpper(uc) == ucFilter
+ && rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
+ return true;
+ } while ((intptr_t)pszDosDot - (intptr_t)pszNext >= -1);
+ return false;
+ }
+ }
+ /* won't ever get here! */
+}
+
+
+/**
+ * Recursive star matching.
+ *
+ * @returns true on match.
+ * @returns false on miss.
+ */
+static bool rtDirFilterWinNtMatchStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter)
+{
+ AssertReturn(iDepth++ < 256, false);
+
+ /*
+ * Inspect the next filter char(s) until we find something to work on.
+ */
+ for (;;)
+ {
+ RTUNICP ucFilter = *puszFilter++;
+ switch (ucFilter)
+ {
+ /*
+ * The star expression is the last in the pattern.
+ * Cool, that means we're done!
+ */
+ case '\0':
+ return true;
+
+ /*
+ * Just in case (doubt we ever get here), just merge it with the current one.
+ */
+ case '*':
+ break;
+
+ /*
+ * Skip a fixed number of chars.
+ * Figure out how many by walking the filter ignoring '*'s.
+ */
+ case '?':
+ {
+ unsigned cQms = 1;
+ while ((ucFilter = *puszFilter) == '*' || ucFilter == '?')
+ {
+ cQms += ucFilter == '?';
+ puszFilter++;
+ }
+ do
+ {
+ if (!uc)
+ return false;
+ int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
+ } while (--cQms > 0);
+ /* done? */
+ if (!ucFilter)
+ return true;
+ break;
+ }
+
+ /*
+ * The simple way is to try char by char and match the remaining
+ * expression. If it's trailing we're done.
+ */
+ case '>': /* dos question mark */
+ {
+ if (rtDirFilterWinNtMatchEon(puszFilter))
+ return true;
+ const char *pszStart = pszNext;
+ do
+ {
+ if (rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
+ return true;
+ int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
+ } while (uc);
+
+ /* backtrack and do the current char. */
+ pszNext = RTStrPrevCp(NULL, pszStart); AssertReturn(pszNext, false);
+ return rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter);
+ }
+
+ /*
+ * This bugger is interesting.
+ * Time for brute force. Iterate the name char by char.
+ */
+ case '<':
+ {
+ do
+ {
+ if (rtDirFilterWinNtMatchDosStar(iDepth, uc, pszNext, puszFilter))
+ return true;
+ int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
+ } while (uc);
+ return false;
+ }
+
+ /*
+ * This guy matches a '.' or the end of the name.
+ * It's very simple if the rest of the filter expression also matches eon.
+ */
+ case '"':
+ if (rtDirFilterWinNtMatchEon(puszFilter))
+ return true;
+ ucFilter = '.';
+ RT_FALL_THRU();
+
+ /*
+ * Ok, we've got zero or more characters.
+ * We'll try match starting at each occurrence of this character.
+ */
+ default:
+ {
+ do
+ {
+ if ( RTUniCpToUpper(uc) == ucFilter
+ && rtDirFilterWinNtMatchBase(iDepth, pszNext, puszFilter))
+ return true;
+ int rc = RTStrGetCpEx(&pszNext, &uc); AssertRCReturn(rc, false);
+ } while (uc);
+ return false;
+ }
+ }
+ } /* for (;;) */
+
+ /* won't ever get here! */
+}
+
+
+/**
+ * Filter a the filename in the against a filter.
+ *
+ * The rules are as follows:
+ * '?' Matches exactly one char.
+ * '*' Matches zero or more chars.
+ * '<' The dos star, matches zero or more chars except the DOS dot.
+ * '>' The dos question mark, matches one char, but dots and end-of-name eats them.
+ * '"' The dos dot, matches a dot or end-of-name.
+ *
+ * @returns true if the name matches the filter.
+ * @returns false if the name doesn't match filter.
+ * @param iDepth The recursion depth.
+ * @param pszName The path to match to the filter.
+ * @param puszFilter The filter string.
+ */
+static bool rtDirFilterWinNtMatchBase(unsigned iDepth, const char *pszName, PCRTUNICP puszFilter)
+{
+ AssertReturn(iDepth++ < 256, false);
+
+ /*
+ * Walk the string and match it up char by char.
+ */
+ RTUNICP uc;
+ do
+ {
+ RTUNICP ucFilter = *puszFilter++;
+ int rc = RTStrGetCpEx(&pszName, &uc); AssertRCReturn(rc, false);
+ switch (ucFilter)
+ {
+ /* Exactly one char. */
+ case '?':
+ if (!uc)
+ return false;
+ break;
+
+ /* One char, but the dos dot and end-of-name eats '>' and '<'. */
+ case '>': /* dos ? */
+ if (!uc)
+ return rtDirFilterWinNtMatchEon(puszFilter);
+ if (uc == '.')
+ {
+ while ((ucFilter = *puszFilter) == '>' || ucFilter == '<')
+ puszFilter++;
+ if (ucFilter == '"' || ucFilter == '.') /* not 100% sure about the last dot */
+ ++puszFilter;
+ else /* the does question mark doesn't match '.'s, so backtrack. */
+ pszName = RTStrPrevCp(NULL, pszName);
+ }
+ break;
+
+ /* Match a dot or the end-of-name. */
+ case '"': /* dos '.' */
+ if (uc != '.')
+ {
+ if (uc)
+ return false;
+ return rtDirFilterWinNtMatchEon(puszFilter);
+ }
+ break;
+
+ /* zero or more */
+ case '*':
+ return rtDirFilterWinNtMatchStar(iDepth, uc, pszName, puszFilter);
+ case '<': /* dos '*' */
+ return rtDirFilterWinNtMatchDosStar(iDepth, uc, pszName, puszFilter);
+
+
+ /* uppercased match */
+ default:
+ {
+ if (RTUniCpToUpper(uc) != ucFilter)
+ return false;
+ break;
+ }
+ }
+ } while (uc);
+
+ return true;
+}
+
+
+/**
+ * Filter a the filename in the against a filter.
+ *
+ * @returns true if the name matches the filter.
+ * @returns false if the name doesn't match filter.
+ * @param pDir The directory handle.
+ * @param pszName The path to match to the filter.
+ */
+static DECLCALLBACK(bool) rtDirFilterWinNtMatch(PRTDIRINTERNAL pDir, const char *pszName)
+{
+ return rtDirFilterWinNtMatchBase(0, pszName, pDir->puszFilter);
+}
+
+
+/**
+ * Initializes a WinNt like wildcard filter.
+ *
+ * @returns Pointer to the filter function.
+ * @returns NULL if the filter doesn't filter out anything.
+ * @param pDir The directory handle (not yet opened).
+ */
+static PFNRTDIRFILTER rtDirFilterWinNtInit(PRTDIRINTERNAL pDir)
+{
+ /*
+ * Check for the usual * and <"< (*.* in DOS language) patterns.
+ */
+ if ( (pDir->cchFilter == 1 && pDir->pszFilter[0] == '*')
+ || (pDir->cchFilter == 3 && !memcmp(pDir->pszFilter, "<\".>", 3))
+ )
+ return NULL;
+
+ /*
+ * Uppercase the expression, also do a little optimizations when possible.
+ */
+ bool fHaveWildcards = false;
+ unsigned iRead = 0;
+ unsigned iWrite = 0;
+ while (iRead < pDir->cucFilter)
+ {
+ RTUNICP uc = pDir->puszFilter[iRead++];
+ if (uc == '*')
+ {
+ fHaveWildcards = true;
+ /* remove extra stars. */
+ RTUNICP uc2;
+ while ((uc2 = pDir->puszFilter[iRead + 1]) == '*')
+ iRead++;
+ }
+ else if (uc == '?' || uc == '>' || uc == '<' || uc == '"')
+ fHaveWildcards = true;
+ else
+ uc = RTUniCpToUpper(uc);
+ pDir->puszFilter[iWrite++] = uc;
+ }
+ pDir->puszFilter[iWrite] = 0;
+ pDir->cucFilter = iWrite;
+
+ return fHaveWildcards
+ ? rtDirFilterWinNtMatch
+ : rtDirFilterWinNtMatchNoWildcards;
+}
+
+
+/**
+ * Common worker for opening a directory.
+ *
+ * @returns IPRT status code.
+ * @param phDir Where to store the directory handle.
+ * @param pszPath The specified path.
+ * @param pszFilter Pointer to where the filter start in the path.
+ * NULL if no filter.
+ * @param enmFilter The type of filter to apply.
+ * @param fFlags RTDIR_F_XXX.
+ * @param hRelativeDir The directory @a pvNativeRelative is relative
+ * to, ~(uintptr_t)0 if absolute.
+ * @param pvNativeRelative The native relative path. NULL if absolute or
+ * we're to use (consume) hRelativeDir.
+ */
+static int rtDirOpenCommon(RTDIR *phDir, const char *pszPath, const char *pszFilter, RTDIRFILTER enmFilter,
+ uint32_t fFlags, uintptr_t hRelativeDir, void *pvNativeRelative)
+{
+ /*
+ * Expand the path.
+ *
+ * The purpose of this exercise to have the abs path around
+ * for querying extra information about the objects we list.
+ * As a sideeffect we also validate the path here.
+ *
+ * Note! The RTDIR_F_NO_ABS_PATH mess is there purely for allowing us to
+ * work around PATH_MAX using CWD on linux and other unixy systems.
+ */
+ char *pszAbsPath;
+ size_t cbFilter; /* includes '\0' (thus cb and not cch). */
+ size_t cucFilter0; /* includes U+0. */
+ bool fDirSlash = false;
+ if (!pszFilter)
+ {
+ if (*pszPath != '\0')
+ {
+ const char *pszLast = strchr(pszPath, '\0') - 1;
+ if (RTPATH_IS_SLASH(*pszLast))
+ fDirSlash = true;
+ }
+
+ cbFilter = cucFilter0 = 0;
+ if (!(fFlags & RTDIR_F_NO_ABS_PATH))
+ pszAbsPath = RTPathAbsExDup(NULL, pszPath, RTPATHABS_F_ENSURE_TRAILING_SLASH);
+ else
+ {
+ size_t cchTmp = strlen(pszPath);
+ pszAbsPath = RTStrAlloc(cchTmp + 2);
+ if (pszAbsPath)
+ {
+ memcpy(pszAbsPath, pszPath, cchTmp);
+ pszAbsPath[cchTmp] = RTPATH_SLASH;
+ pszAbsPath[cchTmp + 1 - fDirSlash] = '\0';
+ }
+ }
+ }
+ else
+ {
+ cbFilter = strlen(pszFilter) + 1;
+ cucFilter0 = RTStrUniLen(pszFilter) + 1;
+
+ if (pszFilter != pszPath)
+ {
+ /* yea, I'm lazy. sue me. */
+ char *pszTmp = RTStrDup(pszPath);
+ if (!pszTmp)
+ return VERR_NO_MEMORY;
+ pszTmp[pszFilter - pszPath] = '\0';
+ if (!(fFlags & RTDIR_F_NO_ABS_PATH))
+ {
+ pszAbsPath = RTPathAbsExDup(NULL, pszTmp, RTPATHABS_F_ENSURE_TRAILING_SLASH);
+ RTStrFree(pszTmp);
+ }
+ else
+ {
+ pszAbsPath = pszTmp;
+ RTPathEnsureTrailingSeparator(pszAbsPath, strlen(pszPath) + 1);
+ }
+ }
+ else if (!(fFlags & RTDIR_F_NO_ABS_PATH))
+ pszAbsPath = RTPathAbsExDup(NULL, ".", RTPATHABS_F_ENSURE_TRAILING_SLASH);
+ else
+ pszAbsPath = RTStrDup("." RTPATH_SLASH_STR);
+ fDirSlash = true;
+ }
+ if (!pszAbsPath)
+ return VERR_NO_MEMORY;
+ Assert(strchr(pszAbsPath, '\0')[-1] == RTPATH_SLASH);
+
+ /*
+ * Allocate and initialize the directory handle.
+ *
+ * The posix definition of Data.d_name allows it to be < NAME_MAX + 1,
+ * thus the horrible ugliness here. Solaris uses d_name[1] for instance.
+ */
+ size_t const cchAbsPath = strlen(pszAbsPath);
+ size_t const cbDir = rtDirNativeGetStructSize(pszAbsPath);
+ size_t const cbAllocated = cbDir
+ + cucFilter0 * sizeof(RTUNICP)
+ + cbFilter
+ + cchAbsPath + 1 + 4;
+ PRTDIRINTERNAL pDir = (PRTDIRINTERNAL)RTMemAllocZ(cbAllocated);
+ if (!pDir)
+ {
+ RTStrFree(pszAbsPath);
+ return VERR_NO_MEMORY;
+ }
+ uint8_t *pb = (uint8_t *)pDir + cbDir;
+
+ /* initialize it */
+ pDir->u32Magic = RTDIR_MAGIC;
+ pDir->cbSelf = cbDir;
+ if (cbFilter)
+ {
+ pDir->puszFilter = (PRTUNICP)pb;
+ int rc2 = RTStrToUniEx(pszFilter, RTSTR_MAX, &pDir->puszFilter, cucFilter0, &pDir->cucFilter);
+ AssertRC(rc2);
+ pb += cucFilter0 * sizeof(RTUNICP);
+ pDir->pszFilter = (char *)memcpy(pb, pszFilter, cbFilter);
+ pDir->cchFilter = cbFilter - 1;
+ pb += cbFilter;
+ }
+ else
+ {
+ pDir->puszFilter = NULL;
+ pDir->cucFilter = 0;
+ pDir->pszFilter = NULL;
+ pDir->cchFilter = 0;
+ }
+ pDir->enmFilter = enmFilter;
+ switch (enmFilter)
+ {
+ default:
+ case RTDIRFILTER_NONE:
+ pDir->pfnFilter = NULL;
+ break;
+ case RTDIRFILTER_WINNT:
+ pDir->pfnFilter = rtDirFilterWinNtInit(pDir);
+ break;
+ case RTDIRFILTER_UNIX:
+ pDir->pfnFilter = NULL;
+ break;
+ case RTDIRFILTER_UNIX_UPCASED:
+ pDir->pfnFilter = NULL;
+ break;
+ }
+ pDir->cchPath = cchAbsPath;
+ pDir->pszPath = (char *)memcpy(pb, pszAbsPath, cchAbsPath);
+ pb[cchAbsPath] = '\0';
+ Assert(pb - (uint8_t *)pDir + cchAbsPath + 1 <= cbAllocated);
+ pDir->pszName = NULL;
+ pDir->cchName = 0;
+ pDir->fFlags = fFlags;
+ pDir->fDirSlash = fDirSlash;
+ pDir->fDataUnread = false;
+
+ /*
+ * Hand it over to the native part.
+ */
+ int rc = rtDirNativeOpen(pDir, hRelativeDir, pvNativeRelative);
+ if (RT_SUCCESS(rc))
+ *phDir = pDir;
+ else
+ RTMemFree(pDir);
+ RTStrFree(pszAbsPath);
+ return rc;
+}
+
+
+RTDECL(int) RTDirOpen(RTDIR *phDir, const char *pszPath)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phDir, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+
+ /*
+ * Take common cause with RTDirOpenFiltered().
+ */
+ int rc = rtDirOpenCommon(phDir, pszPath, NULL, RTDIRFILTER_NONE, 0 /*fFlags*/, ~(uintptr_t)0, NULL);
+ LogFlow(("RTDirOpen(%p:{%p}, %p:{%s}): return %Rrc\n", phDir, *phDir, pszPath, pszPath, rc));
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtDirOpenRelativeOrHandle(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags,
+ uintptr_t hRelativeDir, void *pvNativeRelative)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phDir, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTDIR_F_VALID_MASK), VERR_INVALID_FLAGS);
+ switch (enmFilter)
+ {
+ case RTDIRFILTER_UNIX:
+ case RTDIRFILTER_UNIX_UPCASED:
+ AssertMsgFailed(("%d is not implemented!\n", enmFilter));
+ return VERR_NOT_IMPLEMENTED;
+ case RTDIRFILTER_NONE:
+ case RTDIRFILTER_WINNT:
+ break;
+ default:
+ AssertMsgFailedReturn(("%d\n", enmFilter), VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Find the last component, i.e. where the filter criteria starts and the dir name ends.
+ */
+ const char *pszFilter;
+ if (enmFilter == RTDIRFILTER_NONE)
+ pszFilter = NULL;
+ else
+ {
+ pszFilter = RTPathFilename(pszPath);
+ if (!pszFilter) /* trailing slash => directory to read => no filter. */
+ enmFilter = RTDIRFILTER_NONE;
+ }
+
+ /*
+ * Call worker common with RTDirOpen which will verify the path, allocate
+ * and initialize the handle, and finally call the backend.
+ */
+ int rc = rtDirOpenCommon(phDir, pszPath, pszFilter, enmFilter, fFlags, hRelativeDir, pvNativeRelative);
+
+ LogFlow(("RTDirOpenFiltered(%p:{%p}, %p:{%s}, %d, %#x, %p, %p): return %Rrc\n",
+ phDir,*phDir, pszPath, pszPath, enmFilter, fFlags, hRelativeDir, pvNativeRelative, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTDirOpenFiltered(RTDIR *phDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fFlags)
+{
+ return rtDirOpenRelativeOrHandle(phDir, pszPath, enmFilter, fFlags, ~(uintptr_t)0, NULL);
+}
+
+
+RTDECL(bool) RTDirIsValid(RTDIR hDir)
+{
+ return RT_VALID_PTR(hDir)
+ && hDir->u32Magic == RTDIR_MAGIC;
+}
+
+
+RTDECL(int) RTDirFlushParent(const char *pszChild)
+{
+ char *pszPath;
+ char *pszPathFree = NULL;
+ size_t const cchChild = strlen(pszChild);
+ if (cchChild < RTPATH_MAX)
+ pszPath = (char *)alloca(cchChild + 1);
+ else
+ {
+ pszPathFree = pszPath = (char *)RTMemTmpAlloc(cchChild + 1);
+ if (!pszPath)
+ return VERR_NO_TMP_MEMORY;
+ }
+ memcpy(pszPath, pszChild, cchChild);
+ pszPath[cchChild] = '\0';
+ RTPathStripFilename(pszPath);
+
+ int rc = RTDirFlush(pszPath);
+
+ if (pszPathFree)
+ RTMemTmpFree(pszPathFree);
+ return rc;
+}
+
+
+RTDECL(int) RTDirQueryUnknownTypeEx(const char *pszComposedName, bool fFollowSymlinks,
+ RTDIRENTRYTYPE *penmType, PRTFSOBJINFO pObjInfo)
+{
+ int rc = RTPathQueryInfoEx(pszComposedName, pObjInfo, RTFSOBJATTRADD_NOTHING,
+ fFollowSymlinks ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
+ *penmType = RTDIRENTRYTYPE_DIRECTORY;
+ else if (RTFS_IS_FILE(pObjInfo->Attr.fMode))
+ *penmType = RTDIRENTRYTYPE_FILE;
+ else if (RTFS_IS_SYMLINK(pObjInfo->Attr.fMode))
+ *penmType = RTDIRENTRYTYPE_SYMLINK;
+ else if (RTFS_IS_FIFO(pObjInfo->Attr.fMode))
+ *penmType = RTDIRENTRYTYPE_FIFO;
+ else if (RTFS_IS_DEV_CHAR(pObjInfo->Attr.fMode))
+ *penmType = RTDIRENTRYTYPE_DEV_CHAR;
+ else if (RTFS_IS_DEV_BLOCK(pObjInfo->Attr.fMode))
+ *penmType = RTDIRENTRYTYPE_DEV_BLOCK;
+ else if (RTFS_IS_SOCKET(pObjInfo->Attr.fMode))
+ *penmType = RTDIRENTRYTYPE_SOCKET;
+ else if (RTFS_IS_WHITEOUT(pObjInfo->Attr.fMode))
+ *penmType = RTDIRENTRYTYPE_WHITEOUT;
+ else
+ *penmType = RTDIRENTRYTYPE_UNKNOWN;
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTDirQueryUnknownType(const char *pszComposedName, bool fFollowSymlinks, RTDIRENTRYTYPE *penmType)
+{
+ if ( *penmType != RTDIRENTRYTYPE_UNKNOWN
+ && ( !fFollowSymlinks
+ || *penmType != RTDIRENTRYTYPE_SYMLINK))
+ return VINF_SUCCESS;
+
+ RTFSOBJINFO ObjInfo;
+ return RTDirQueryUnknownTypeEx(pszComposedName, fFollowSymlinks, penmType, &ObjInfo);
+}
+
+
+RTDECL(bool) RTDirEntryIsStdDotLink(PRTDIRENTRY pDirEntry)
+{
+ if (pDirEntry->szName[0] != '.')
+ return false;
+ if (pDirEntry->cbName == 1)
+ return true;
+ if (pDirEntry->cbName != 2)
+ return false;
+ return pDirEntry->szName[1] == '.';
+}
+
+
+RTDECL(bool) RTDirEntryExIsStdDotLink(PCRTDIRENTRYEX pDirEntryEx)
+{
+ if (pDirEntryEx->szName[0] != '.')
+ return false;
+ if (pDirEntryEx->cbName == 1)
+ return true;
+ if (pDirEntryEx->cbName != 2)
+ return false;
+ return pDirEntryEx->szName[1] == '.';
+}
+
+
+RTDECL(int) RTDirReadExA(RTDIR hDir, PRTDIRENTRYEX *ppDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAddAttr, uint32_t fFlags)
+{
+ PRTDIRENTRYEX pDirEntry = *ppDirEntry;
+ size_t cbDirEntry = *pcbDirEntry;
+ if (pDirEntry != NULL && cbDirEntry >= sizeof(RTDIRENTRYEX))
+ { /* likely */ }
+ else
+ {
+ Assert(pDirEntry == NULL);
+ Assert(cbDirEntry == 0);
+
+ cbDirEntry = RT_ALIGN_Z(sizeof(RTDIRENTRYEX), 16);
+ *ppDirEntry = pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntry);
+ if (pDirEntry)
+ *pcbDirEntry = cbDirEntry;
+ else
+ {
+ *pcbDirEntry = 0;
+ return VERR_NO_TMP_MEMORY;
+ }
+ }
+
+ for (;;)
+ {
+ int rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, enmAddAttr, fFlags);
+ if (rc != VERR_BUFFER_OVERFLOW)
+ return rc;
+
+ /* Grow the buffer. */
+ RTMemTmpFree(pDirEntry);
+ cbDirEntry = RT_MAX(RT_ALIGN_Z(cbDirEntry, 64), *pcbDirEntry + 64);
+ *ppDirEntry = pDirEntry = (PRTDIRENTRYEX)RTMemTmpAlloc(cbDirEntry);
+ if (pDirEntry)
+ *pcbDirEntry = cbDirEntry;
+ else
+ {
+ *pcbDirEntry = 0;
+ return VERR_NO_TMP_MEMORY;
+ }
+ }
+}
+
+
+RTDECL(void) RTDirReadExAFree(PRTDIRENTRYEX *ppDirEntry, size_t *pcbDirEntry)
+{
+ PRTDIRENTRYEX pDirEntry = *ppDirEntry;
+ if (pDirEntry != NULL && *pcbDirEntry >= sizeof(*pcbDirEntry))
+ RTMemTmpFree(pDirEntry);
+ else
+ {
+ Assert(pDirEntry == NULL);
+ Assert(*pcbDirEntry == 0);
+ }
+ *ppDirEntry = NULL;
+ *pcbDirEntry = 0;
+}
+
diff --git a/src/VBox/Runtime/r3/dir2.cpp b/src/VBox/Runtime/r3/dir2.cpp
new file mode 100644
index 00000000..1f47b3ed
--- /dev/null
+++ b/src/VBox/Runtime/r3/dir2.cpp
@@ -0,0 +1,242 @@
+/* $Id: dir2.cpp $ */
+/** @file
+ * IPRT - Directory Manipulation, Part 2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/dir.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include "internal/path.h"
+
+
+/**
+ * Recursion worker for RTDirRemoveRecursive.
+ *
+ * @returns IPRT status code.
+ * @param pszBuf The path buffer. Contains the abs path to the
+ * directory to recurse into. Trailing slash.
+ * @param cchDir The length of the directory we're recursing into,
+ * including the trailing slash.
+ * @param cbBuf Size of the buffer @a pszBuf points to.
+ * @param pDirEntry The dir entry buffer. (Shared to save stack.)
+ * @param pObjInfo The object info buffer. (ditto)
+ * @param fFlags RTDIRRMREC_F_XXX.
+ */
+static int rtDirRemoveRecursiveSub(char *pszBuf, size_t cchDir, size_t cbBuf, PRTDIRENTRY pDirEntry, PRTFSOBJINFO pObjInfo,
+ uint32_t fFlags)
+{
+ AssertReturn(RTPATH_IS_SLASH(pszBuf[cchDir - 1]), VERR_INTERNAL_ERROR_4);
+
+ /*
+ * Enumerate the directory content and dispose of it.
+ */
+ RTDIR hDir;
+ int rc = RTDirOpenFiltered(&hDir, pszBuf, RTDIRFILTER_NONE, fFlags & RTDIRRMREC_F_NO_ABS_PATH ? RTDIR_F_NO_ABS_PATH : 0);
+ if (RT_FAILURE(rc))
+ return rc;
+ while (RT_SUCCESS(rc = RTDirRead(hDir, pDirEntry, NULL)))
+ {
+ if (!RTDirEntryIsStdDotLink(pDirEntry))
+ {
+ /* Construct the full name of the entry. */
+ if (cchDir + pDirEntry->cbName + 1 /* dir slash */ >= cbBuf)
+ {
+ rc = VERR_FILENAME_TOO_LONG;
+ break;
+ }
+ memcpy(&pszBuf[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
+
+ /* Deal with the unknown type. */
+ if (pDirEntry->enmType == RTDIRENTRYTYPE_UNKNOWN)
+ {
+ rc = RTPathQueryInfoEx(pszBuf, pObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc) && RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
+ pDirEntry->enmType = RTDIRENTRYTYPE_DIRECTORY;
+ else if (RT_SUCCESS(rc) && RTFS_IS_FILE(pObjInfo->Attr.fMode))
+ pDirEntry->enmType = RTDIRENTRYTYPE_FILE;
+ else if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(pObjInfo->Attr.fMode))
+ pDirEntry->enmType = RTDIRENTRYTYPE_SYMLINK;
+ }
+
+ /* Try the delete the fs object. */
+ switch (pDirEntry->enmType)
+ {
+ case RTDIRENTRYTYPE_FILE:
+ rc = RTFileDelete(pszBuf);
+ break;
+
+ case RTDIRENTRYTYPE_DIRECTORY:
+ {
+ size_t cchSubDir = cchDir + pDirEntry->cbName;
+ pszBuf[cchSubDir++] = '/';
+ pszBuf[cchSubDir] = '\0';
+ rc = rtDirRemoveRecursiveSub(pszBuf, cchSubDir, cbBuf, pDirEntry, pObjInfo, fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ pszBuf[cchSubDir] = '\0';
+ rc = RTDirRemove(pszBuf);
+ }
+ break;
+ }
+
+ //case RTDIRENTRYTYPE_SYMLINK:
+ // rc = RTSymlinkDelete(pszBuf, 0);
+ // break;
+
+ default:
+ /** @todo not implemented yet. */
+ rc = VINF_SUCCESS;
+ break;
+ }
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+ if (rc == VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+ RTDirClose(hDir);
+ return rc;
+}
+
+
+RTDECL(int) RTDirRemoveRecursive(const char *pszPath, uint32_t fFlags)
+{
+ AssertReturn(!(fFlags & ~RTDIRRMREC_F_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate path buffer.
+ */
+ char *pszAbsPath;
+ size_t cbAbsPathBuf = RTPATH_BIG_MAX;
+ char *pszAbsPathFree = pszAbsPath = (char *)RTMemTmpAlloc(cbAbsPathBuf);
+ if (!pszAbsPath)
+ {
+ cbAbsPathBuf = RTPATH_MAX;
+ pszAbsPath = (char *)alloca(RTPATH_MAX);
+ }
+
+ /*
+ * Get an absolute path because this is easier to work with and
+ * eliminates any races with changing CWD.
+ */
+ int rc;
+ if (!(fFlags & RTDIRRMREC_F_NO_ABS_PATH))
+ rc = RTPathAbs(pszPath, pszAbsPath, cbAbsPathBuf);
+ else if (*pszPath != '\0')
+ rc = RTStrCopy(pszAbsPath, cbAbsPathBuf, pszPath);
+ else
+ rc = VERR_PATH_ZERO_LENGTH;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * This API is not permitted applied to the root of anything.
+ */
+ union
+ {
+ RTPATHPARSED Parsed;
+ uint8_t abParsed[RTPATHPARSED_MIN_SIZE];
+ } uBuf;
+ RTPathParse(pszPath, &uBuf.Parsed, sizeof(uBuf), RTPATH_STR_F_STYLE_HOST);
+ if ( uBuf.Parsed.cComps <= 1
+ && (uBuf.Parsed.fProps & RTPATH_PROP_ROOT_SLASH))
+ rc = VERR_ACCESS_DENIED;
+ else
+ {
+ /*
+ * Because of the above restriction, we never have to deal with the root
+ * slash problem and can safely strip any trailing slashes and add a
+ * definite one.
+ */
+ RTPathStripTrailingSlash(pszAbsPath);
+ size_t cchAbsPath = strlen(pszAbsPath);
+ if (cchAbsPath + 1 < cbAbsPathBuf)
+ {
+ pszAbsPath[cchAbsPath++] = RTPATH_SLASH;
+ pszAbsPath[cchAbsPath] = '\0';
+
+ /*
+ * Check if it exists so we can return quietly if it doesn't.
+ */
+ RTFSOBJINFO SharedObjInfoBuf;
+ rc = RTPathQueryInfoEx(pszAbsPath, &SharedObjInfoBuf, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if ( rc == VERR_PATH_NOT_FOUND
+ || rc == VERR_FILE_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ else if ( RT_SUCCESS(rc)
+ && RTFS_IS_DIRECTORY(SharedObjInfoBuf.Attr.fMode))
+ {
+ /*
+ * We're all set for the recursion now, so get going.
+ */
+ RTDIRENTRY SharedDirEntryBuf;
+ rc = rtDirRemoveRecursiveSub(pszAbsPath, cchAbsPath, cbAbsPathBuf,
+ &SharedDirEntryBuf, &SharedObjInfoBuf, fFlags);
+
+ /*
+ * Remove the specified directory if desired and removing the content was successful.
+ */
+ if ( RT_SUCCESS(rc)
+ && !(fFlags & RTDIRRMREC_F_CONTENT_ONLY))
+ {
+ pszAbsPath[cchAbsPath] = 0;
+ rc = RTDirRemove(pszAbsPath);
+ }
+ }
+ else if (RT_SUCCESS(rc))
+ rc = VERR_NOT_A_DIRECTORY;
+
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ }
+ if (pszAbsPathFree)
+ RTMemTmpFree(pszAbsPathFree);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/fileio.cpp b/src/VBox/Runtime/r3/fileio.cpp
new file mode 100644
index 00000000..cece884a
--- /dev/null
+++ b/src/VBox/Runtime/r3/fileio.cpp
@@ -0,0 +1,423 @@
+/* $Id: fileio.cpp $ */
+/** @file
+ * IPRT - File I/O.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/alloca.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include "internal/file.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Set of forced set open flags for files opened read-only. */
+static unsigned g_fOpenReadSet = 0;
+
+/** Set of forced cleared open flags for files opened read-only. */
+static unsigned g_fOpenReadMask = 0;
+
+/** Set of forced set open flags for files opened write-only. */
+static unsigned g_fOpenWriteSet = 0;
+
+/** Set of forced cleared open flags for files opened write-only. */
+static unsigned g_fOpenWriteMask = 0;
+
+/** Set of forced set open flags for files opened read-write. */
+static unsigned g_fOpenReadWriteSet = 0;
+
+/** Set of forced cleared open flags for files opened read-write. */
+static unsigned g_fOpenReadWriteMask = 0;
+
+
+/**
+ * Force the use of open flags for all files opened after the setting is
+ * changed. The caller is responsible for not causing races with RTFileOpen().
+ *
+ * @returns iprt status code.
+ * @param fOpenForAccess Access mode to which the set/mask settings apply.
+ * @param fSet Open flags to be forced set.
+ * @param fMask Open flags to be masked out.
+ */
+RTR3DECL(int) RTFileSetForceFlags(unsigned fOpenForAccess, unsigned fSet, unsigned fMask)
+{
+ /*
+ * For now allow only RTFILE_O_WRITE_THROUGH. The other flags either
+ * make no sense in this context or are not useful to apply to all files.
+ */
+ if ((fSet | fMask) & ~RTFILE_O_WRITE_THROUGH)
+ return VERR_INVALID_PARAMETER;
+ switch (fOpenForAccess)
+ {
+ case RTFILE_O_READ:
+ g_fOpenReadSet = fSet;
+ g_fOpenReadMask = fMask;
+ break;
+ case RTFILE_O_WRITE:
+ g_fOpenWriteSet = fSet;
+ g_fOpenWriteMask = fMask;
+ break;
+ case RTFILE_O_READWRITE:
+ g_fOpenReadWriteSet = fSet;
+ g_fOpenReadWriteMask = fMask;
+ break;
+ default:
+ AssertMsgFailed(("Invalid access mode %d\n", fOpenForAccess));
+ return VERR_INVALID_PARAMETER;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Adjusts and validates the flags.
+ *
+ * The adjustments are made according to the wishes specified using the RTFileSetForceFlags API.
+ *
+ * @returns IPRT status code.
+ * @param pfOpen Pointer to the user specified flags on input.
+ * Updated on successful return.
+ * @internal
+ */
+int rtFileRecalcAndValidateFlags(uint64_t *pfOpen)
+{
+ /*
+ * Recalc.
+ */
+ uint32_t fOpen = *pfOpen;
+ switch (fOpen & RTFILE_O_ACCESS_MASK)
+ {
+ case RTFILE_O_READ:
+ fOpen |= g_fOpenReadSet;
+ fOpen &= ~g_fOpenReadMask;
+ break;
+ case RTFILE_O_WRITE:
+ fOpen |= g_fOpenWriteSet;
+ fOpen &= ~g_fOpenWriteMask;
+ break;
+ case RTFILE_O_READWRITE:
+ fOpen |= g_fOpenReadWriteSet;
+ fOpen &= ~g_fOpenReadWriteMask;
+ break;
+#ifdef RT_OS_WINDOWS
+ case RTFILE_O_ATTR_ONLY:
+ if (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
+ break;
+#endif
+ default:
+ AssertMsgFailed(("Invalid access mode value, fOpen=%#llx\n", fOpen));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Validate .
+ */
+#ifdef RT_OS_WINDOWS
+ AssertMsgReturn((fOpen & RTFILE_O_ACCESS_MASK) || (fOpen & RTFILE_O_ACCESS_ATTR_MASK),
+ ("Missing RTFILE_O_READ/WRITE/ACCESS_ATTR: fOpen=%#llx\n", fOpen), VERR_INVALID_PARAMETER);
+#else
+ AssertMsgReturn(fOpen & RTFILE_O_ACCESS_MASK, ("Missing RTFILE_O_READ/WRITE: fOpen=%#llx\n", fOpen), VERR_INVALID_PARAMETER);
+#endif
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ AssertMsgReturn(!(fOpen & (~(uint64_t)RTFILE_O_VALID_MASK | RTFILE_O_NON_BLOCK)), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
+#else
+ AssertMsgReturn(!(fOpen & ~(uint64_t)RTFILE_O_VALID_MASK), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
+#endif
+ AssertMsgReturn((fOpen & (RTFILE_O_TRUNCATE | RTFILE_O_WRITE)) != RTFILE_O_TRUNCATE, ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
+
+ switch (fOpen & RTFILE_O_ACTION_MASK)
+ {
+ case 0: /* temporarily */
+ AssertMsgFailed(("Missing RTFILE_O_OPEN/CREATE*! (continuable assertion)\n"));
+ fOpen |= RTFILE_O_OPEN;
+ break;
+ case RTFILE_O_OPEN:
+ AssertMsgReturn(!(RTFILE_O_NOT_CONTENT_INDEXED & fOpen), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
+ case RTFILE_O_OPEN_CREATE:
+ case RTFILE_O_CREATE:
+ case RTFILE_O_CREATE_REPLACE:
+ break;
+ default:
+ AssertMsgFailed(("Invalid action value: fOpen=%#llx\n", fOpen));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ switch (fOpen & RTFILE_O_DENY_MASK)
+ {
+ case 0: /* temporarily */
+ AssertMsgFailed(("Missing RTFILE_O_DENY_*! (continuable assertion)\n"));
+ fOpen |= RTFILE_O_DENY_NONE;
+ break;
+ case RTFILE_O_DENY_NONE:
+ case RTFILE_O_DENY_READ:
+ case RTFILE_O_DENY_WRITE:
+ case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
+ case RTFILE_O_DENY_NOT_DELETE:
+ case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READ:
+ case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE:
+ case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
+ break;
+ default:
+ AssertMsgFailed(("Invalid deny value: fOpen=%#llx\n", fOpen));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* done */
+ *pfOpen = fOpen;
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(uint64_t) RTFileTell(RTFILE File)
+{
+ /*
+ * Call the seek api to query the stuff.
+ */
+ uint64_t off = 0;
+ int rc = RTFileSeek(File, 0, RTFILE_SEEK_CURRENT, &off);
+ if (RT_SUCCESS(rc))
+ return off;
+ AssertMsgFailed(("RTFileSeek(%d) -> %d\n", File, rc));
+ return ~0ULL;
+}
+
+
+RTR3DECL(RTFOFF) RTFileGetMaxSize(RTFILE File)
+{
+ RTFOFF cbMax;
+ int rc = RTFileQueryMaxSizeEx(File, &cbMax);
+ return RT_SUCCESS(rc) ? cbMax : -1;
+}
+
+
+RTDECL(int) RTFileCopyByHandles(RTFILE FileSrc, RTFILE FileDst)
+{
+ return RTFileCopyByHandlesEx(FileSrc, FileDst, NULL, NULL);
+}
+
+
+RTDECL(int) RTFileCompare(const char *pszFile1, const char *pszFile2)
+{
+ return RTFileCompareEx(pszFile1, pszFile2, 0 /*fFlags*/, NULL, NULL);
+}
+
+
+RTDECL(int) RTFileCompareByHandles(RTFILE hFile1, RTFILE hFile2)
+{
+ return RTFileCompareByHandlesEx(hFile1, hFile2, 0 /*fFlags*/, NULL, NULL);
+}
+
+
+RTDECL(int) RTFileCompareEx(const char *pszFile1, const char *pszFile2, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszFile1, VERR_INVALID_POINTER);
+ AssertReturn(*pszFile1, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszFile2, VERR_INVALID_POINTER);
+ AssertReturn(*pszFile2, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pfnProgress, VERR_INVALID_POINTER);
+ AssertMsgReturn(!(fFlags & ~RTFILECOMP_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Open the files.
+ */
+ RTFILE hFile1;
+ int rc = RTFileOpen(&hFile1, pszFile1,
+ RTFILE_O_READ | RTFILE_O_OPEN
+ | (fFlags & RTFILECOMP_FLAGS_NO_DENY_WRITE_FILE1 ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
+ if (RT_SUCCESS(rc))
+ {
+ RTFILE hFile2;
+ rc = RTFileOpen(&hFile2, pszFile2,
+ RTFILE_O_READ | RTFILE_O_OPEN
+ | (fFlags & RTFILECOMP_FLAGS_NO_DENY_WRITE_FILE2 ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Call the ByHandles version and let it do the job.
+ */
+ rc = RTFileCompareByHandlesEx(hFile1, hFile2, fFlags, pfnProgress, pvUser);
+
+ /* Clean up */
+ int rc2 = RTFileClose(hFile2);
+ AssertRC(rc2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ int rc2 = RTFileClose(hFile1);
+ AssertRC(rc2);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTFileCompareByHandlesEx(RTFILE hFile1, RTFILE hFile2, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(RTFileIsValid(hFile1), VERR_INVALID_HANDLE);
+ AssertReturn(RTFileIsValid(hFile1), VERR_INVALID_HANDLE);
+ AssertPtrNullReturn(pfnProgress, VERR_INVALID_POINTER);
+ AssertMsgReturn(!(fFlags & ~RTFILECOMP_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Compare the file sizes first.
+ */
+ uint64_t cbFile1;
+ int rc = RTFileQuerySize(hFile1, &cbFile1);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t cbFile2;
+ rc = RTFileQuerySize(hFile1, &cbFile2);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (cbFile1 != cbFile2)
+ return VERR_NOT_EQUAL;
+
+
+ /*
+ * Allocate buffer.
+ */
+ size_t cbBuf;
+ uint8_t *pbBuf1Free = NULL;
+ uint8_t *pbBuf1;
+ uint8_t *pbBuf2Free = NULL;
+ uint8_t *pbBuf2;
+ if (cbFile1 < _512K)
+ {
+ cbBuf = 8*_1K;
+ pbBuf1 = (uint8_t *)alloca(cbBuf);
+ pbBuf2 = (uint8_t *)alloca(cbBuf);
+ }
+ else
+ {
+ cbBuf = _128K;
+ pbBuf1 = pbBuf1Free = (uint8_t *)RTMemTmpAlloc(cbBuf);
+ pbBuf2 = pbBuf2Free = (uint8_t *)RTMemTmpAlloc(cbBuf);
+ }
+ if (pbBuf1 && pbBuf2)
+ {
+ /*
+ * Seek to the start of each file
+ * and set the size of the destination file.
+ */
+ rc = RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileSeek(hFile2, 0, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc) && pfnProgress)
+ rc = pfnProgress(0, pvUser);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Compare loop.
+ */
+ unsigned uPercentage = 0;
+ RTFOFF off = 0;
+ RTFOFF cbPercent = cbFile1 / 100;
+ RTFOFF offNextPercent = cbPercent;
+ while (off < (RTFOFF)cbFile1)
+ {
+ /* read the blocks */
+ RTFOFF cbLeft = cbFile1 - off;
+ size_t cbBlock = cbLeft >= (RTFOFF)cbBuf ? cbBuf : (size_t)cbLeft;
+ rc = RTFileRead(hFile1, pbBuf1, cbBlock, NULL);
+ if (RT_FAILURE(rc))
+ break;
+ rc = RTFileRead(hFile2, pbBuf2, cbBlock, NULL);
+ if (RT_FAILURE(rc))
+ break;
+
+ /* compare */
+ if (memcmp(pbBuf1, pbBuf2, cbBlock))
+ {
+ rc = VERR_NOT_EQUAL;
+ break;
+ }
+
+ /* advance */
+ off += cbBlock;
+ if (pfnProgress && offNextPercent < off)
+ {
+ while (offNextPercent < off)
+ {
+ uPercentage++;
+ offNextPercent += cbPercent;
+ }
+ rc = pfnProgress(uPercentage, pvUser);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+#if 0
+ /*
+ * Compare OS specific data (EAs and stuff).
+ */
+ if (RT_SUCCESS(rc))
+ rc = rtFileCompareOSStuff(hFile1, hFile2);
+#endif
+
+ /* 100% */
+ if (pfnProgress && uPercentage < 100 && RT_SUCCESS(rc))
+ rc = pfnProgress(100, pvUser);
+ }
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTMemTmpFree(pbBuf2Free);
+ RTMemTmpFree(pbBuf1Free);
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/freebsd/Makefile.kup b/src/VBox/Runtime/r3/freebsd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/freebsd/Makefile.kup
diff --git a/src/VBox/Runtime/r3/freebsd/RTFileQuerySectorSize-freebsd.cpp b/src/VBox/Runtime/r3/freebsd/RTFileQuerySectorSize-freebsd.cpp
new file mode 100644
index 00000000..15075d63
--- /dev/null
+++ b/src/VBox/Runtime/r3/freebsd/RTFileQuerySectorSize-freebsd.cpp
@@ -0,0 +1,88 @@
+/* $Id: RTFileQuerySectorSize-freebsd.cpp $ */
+/** @file
+ * IPRT - RTFileQuerySectorSize, FreeBSD.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#include <errno.h>
+#include <sys/disk.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+
+
+RTDECL(int) RTFileQuerySectorSize(RTFILE hFile, uint32_t *pcbSector)
+{
+ AssertPtrReturn(pcbSector, VERR_INVALID_PARAMETER);
+
+ int rc;
+ int const fd = (int)RTFileToNative(hFile);
+ struct stat DevStat = { 0 };
+ if (!fstat(fd, &DevStat))
+ {
+ if (S_ISCHR(DevStat.st_mode))
+ {
+ u_int cbSector = 0;
+ if (!ioctl(fd, DIOCGSECTORSIZE, &cbSector))
+ {
+ AssertReturn(cbSector > 0, VERR_INVALID_FUNCTION);
+ *pcbSector = cbSector;
+ return VINF_SUCCESS;
+ }
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("ioctl failed: errno=%d / %Rrc\n", errno, rc));
+ }
+ else
+ {
+ AssertMsgFailed(("not a character device.\n"));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("fstat failed: errno=%d / %Rrc\n", errno, rc));
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/freebsd/fileaio-freebsd.cpp b/src/VBox/Runtime/r3/freebsd/fileaio-freebsd.cpp
new file mode 100644
index 00000000..46190a5f
--- /dev/null
+++ b/src/VBox/Runtime/r3/freebsd/fileaio-freebsd.cpp
@@ -0,0 +1,682 @@
+/* $Id: fileaio-freebsd.cpp $ */
+/** @file
+ * IPRT - File async I/O, native implementation for the FreeBSD host platform.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include <iprt/asm.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/thread.h>
+#include "internal/fileaio.h"
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <aio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Async I/O completion context state.
+ */
+typedef struct RTFILEAIOCTXINTERNAL
+{
+ /** Handle to the kernel queue. */
+ int iKQueue;
+ /** Current number of requests active on this context. */
+ volatile int32_t cRequests;
+ /** The ID of the thread which is currently waiting for requests. */
+ volatile RTTHREAD hThreadWait;
+ /** Flag whether the thread was woken up. */
+ volatile bool fWokenUp;
+ /** Flag whether the thread is currently waiting in the syscall. */
+ volatile bool fWaiting;
+ /** Flags given during creation. */
+ uint32_t fFlags;
+ /** Magic value (RTFILEAIOCTX_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOCTXINTERNAL;
+/** Pointer to an internal context structure. */
+typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
+
+/**
+ * Async I/O request state.
+ */
+typedef struct RTFILEAIOREQINTERNAL
+{
+ /** The aio control block. Must be the FIRST
+ * element. */
+ struct aiocb AioCB;
+ /** Current state the request is in. */
+ RTFILEAIOREQSTATE enmState;
+ /** Flag whether this is a flush request. */
+ bool fFlush;
+ /** Opaque user data. */
+ void *pvUser;
+ /** Completion context we are assigned to. */
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ /** Number of bytes actually transferred. */
+ size_t cbTransfered;
+ /** Status code. */
+ int Rc;
+ /** Magic value (RTFILEAIOREQ_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOREQINTERNAL;
+/** Pointer to an internal request structure. */
+typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The max number of events to get in one call. */
+#define AIO_MAXIMUM_REQUESTS_PER_CONTEXT 64
+
+RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
+{
+ int rcBSD = 0;
+ AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
+
+ /*
+ * The AIO API is implemented in a kernel module which is not
+ * loaded by default.
+ * If it is loaded there are additional sysctl parameters.
+ */
+ int cReqsOutstandingMax = 0;
+ size_t cbParameter = sizeof(int);
+
+ rcBSD = sysctlbyname("vfs.aio.max_aio_per_proc", /* name */
+ &cReqsOutstandingMax, /* Where to store the old value. */
+ &cbParameter, /* Size of the memory pointed to. */
+ NULL, /* Where the new value is located. */
+ 0); /* Where the size of the new value is stored. */
+ if (rcBSD == -1)
+ {
+ /* ENOENT means the value is unknown thus the module is not loaded. */
+ if (errno == ENOENT)
+ return VERR_NOT_SUPPORTED;
+ else
+ return RTErrConvertFromErrno(errno);
+ }
+
+ pAioLimits->cReqsOutstandingMax = cReqsOutstandingMax;
+ pAioLimits->cbBufferAlignment = 0;
+
+ return VINF_SUCCESS;
+}
+
+RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
+{
+ AssertPtrReturn(phReq, VERR_INVALID_POINTER);
+
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL));
+ if (RT_UNLIKELY(!pReqInt))
+ return VERR_NO_MEMORY;
+
+ /* Ininitialize static parts. */
+ pReqInt->AioCB.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+ pReqInt->AioCB.aio_sigevent.sigev_value.sival_ptr = pReqInt;
+ pReqInt->pCtxInt = NULL;
+ pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ *phReq = (RTFILEAIOREQ)pReqInt;
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
+{
+ /*
+ * Validate the handle and ignore nil.
+ */
+ if (hReq == NIL_RTFILEAIOREQ)
+ return VINF_SUCCESS;
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+
+ /*
+ * Trash the magic and free it.
+ */
+ ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
+ RTMemFree(pReqInt);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Worker setting up the request.
+ */
+DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
+ unsigned uTransferDirection,
+ RTFOFF off, void *pvBuf, size_t cbTransfer,
+ void *pvUser)
+{
+ /*
+ * Validate the input.
+ */
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ Assert(hFile != NIL_RTFILE);
+ AssertPtr(pvBuf);
+ Assert(off >= 0);
+ Assert(cbTransfer > 0);
+
+ pReqInt->AioCB.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+ pReqInt->AioCB.aio_sigevent.sigev_value.sival_ptr = pReqInt;
+ pReqInt->AioCB.aio_lio_opcode = uTransferDirection;
+ pReqInt->AioCB.aio_fildes = RTFileToNative(hFile);
+ pReqInt->AioCB.aio_offset = off;
+ pReqInt->AioCB.aio_nbytes = cbTransfer;
+ pReqInt->AioCB.aio_buf = pvBuf;
+ pReqInt->fFlush = false;
+ pReqInt->pvUser = pvUser;
+ pReqInt->pCtxInt = NULL;
+ pReqInt->Rc = VERR_FILE_AIO_IN_PROGRESS;
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void *pvBuf, size_t cbRead, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, LIO_READ,
+ off, pvBuf, cbRead, pvUser);
+}
+
+RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void const *pvBuf, size_t cbWrite, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, LIO_WRITE,
+ off, (void *)pvBuf, cbWrite, pvUser);
+}
+
+RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)hReq;
+
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ Assert(hFile != NIL_RTFILE);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+
+ pReqInt->fFlush = true;
+ pReqInt->AioCB.aio_fildes = RTFileToNative(hFile);
+ pReqInt->AioCB.aio_offset = 0;
+ pReqInt->AioCB.aio_nbytes = 0;
+ pReqInt->AioCB.aio_buf = NULL;
+ pReqInt->pvUser = pvUser;
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
+
+ return pReqInt->pvUser;
+}
+
+RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+
+ int rcBSD = aio_cancel(pReqInt->AioCB.aio_fildes, &pReqInt->AioCB);
+
+ if (rcBSD == AIO_CANCELED)
+ {
+ /*
+ * Decrement request count because the request will never arrive at the
+ * completion port.
+ */
+ AssertMsg(RT_VALID_PTR(pReqInt->pCtxInt),
+ ("Invalid state. Request was canceled but wasn't submitted\n"));
+
+ ASMAtomicDecS32(&pReqInt->pCtxInt->cRequests);
+ pReqInt->Rc = VERR_FILE_AIO_CANCELED;
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ return VINF_SUCCESS;
+ }
+ else if (rcBSD == AIO_ALLDONE)
+ return VERR_FILE_AIO_COMPLETED;
+ else if (rcBSD == AIO_NOTCANCELED)
+ return VERR_FILE_AIO_IN_PROGRESS;
+ else
+ return RTErrConvertFromErrno(errno);
+}
+
+RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ AssertPtrNull(pcbTransfered);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ if ( (RT_SUCCESS(pReqInt->Rc))
+ && (pcbTransfered))
+ *pcbTransfered = pReqInt->cbTransfered;
+
+ return pReqInt->Rc;
+}
+
+RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax,
+ uint32_t fFlags)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
+ if (RT_UNLIKELY(!pCtxInt))
+ return VERR_NO_MEMORY;
+
+ /* Init the event handle. */
+ pCtxInt->iKQueue = kqueue();
+ if (RT_LIKELY(pCtxInt->iKQueue > 0))
+ {
+ pCtxInt->fFlags = fFlags;
+ pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
+ *phAioCtx = (RTFILEAIOCTX)pCtxInt;
+ }
+ else
+ {
+ RTMemFree(pCtxInt);
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
+{
+ /* Validate the handle and ignore nil. */
+ if (hAioCtx == NIL_RTFILEAIOCTX)
+ return VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ /* Cannot destroy a busy context. */
+ if (RT_UNLIKELY(pCtxInt->cRequests))
+ return VERR_FILE_AIO_BUSY;
+
+ close(pCtxInt->iKQueue);
+ ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
+ RTMemFree(pCtxInt);
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
+{
+ return RTFILEAIO_UNLIMITED_REQS;
+}
+
+RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
+{
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
+{
+ /*
+ * Parameter validation.
+ */
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+
+ do
+ {
+ int rcBSD = 0;
+ size_t cReqsSubmit = 0;
+ size_t i = 0;
+ PRTFILEAIOREQINTERNAL pReqInt;
+
+ while ( (i < cReqs)
+ && (i < AIO_LISTIO_MAX))
+ {
+ pReqInt = pahReqs[i];
+ if (RTFILEAIOREQ_IS_NOT_VALID(pReqInt))
+ {
+ /* Undo everything and stop submitting. */
+ for (size_t iUndo = 0; iUndo < i; iUndo++)
+ {
+ pReqInt = pahReqs[iUndo];
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+ pReqInt->pCtxInt = NULL;
+ pReqInt->AioCB.aio_sigevent.sigev_notify_kqueue = 0;
+ }
+ rc = VERR_INVALID_HANDLE;
+ break;
+ }
+
+ pReqInt->AioCB.aio_sigevent.sigev_notify_kqueue = pCtxInt->iKQueue;
+ pReqInt->pCtxInt = pCtxInt;
+ RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
+
+ if (pReqInt->fFlush)
+ break;
+
+ cReqsSubmit++;
+ i++;
+ }
+
+ if (cReqsSubmit)
+ {
+ rcBSD = lio_listio(LIO_NOWAIT, (struct aiocb **)pahReqs, cReqsSubmit, NULL);
+ if (RT_UNLIKELY(rcBSD < 0))
+ {
+ if (errno == EAGAIN)
+ rc = VERR_FILE_AIO_INSUFFICIENT_RESSOURCES;
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ /* Check which requests got actually submitted and which not. */
+ for (i = 0; i < cReqs; i++)
+ {
+ pReqInt = pahReqs[i];
+ rcBSD = aio_error(&pReqInt->AioCB);
+ if ( rcBSD == -1
+ && errno == EINVAL)
+ {
+ /* Was not submitted. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+ pReqInt->pCtxInt = NULL;
+ }
+ else if (rcBSD != EINPROGRESS)
+ {
+ /* The request encountered an error. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ pReqInt->Rc = RTErrConvertFromErrno(rcBSD);
+ pReqInt->pCtxInt = NULL;
+ pReqInt->cbTransfered = 0;
+ }
+ }
+ break;
+ }
+
+ ASMAtomicAddS32(&pCtxInt->cRequests, cReqsSubmit);
+ cReqs -= cReqsSubmit;
+ pahReqs += cReqsSubmit;
+ }
+
+ /* Check if we have a flush request now. */
+ if (cReqs && RT_SUCCESS_NP(rc))
+ {
+ pReqInt = pahReqs[0];
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+
+ if (pReqInt->fFlush)
+ {
+ /*
+ * lio_listio does not work with flush requests so
+ * we have to use aio_fsync directly.
+ */
+ rcBSD = aio_fsync(O_SYNC, &pReqInt->AioCB);
+ if (RT_UNLIKELY(rcBSD < 0))
+ {
+ if (rcBSD == EAGAIN)
+ {
+ /* Was not submitted. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+ pReqInt->pCtxInt = NULL;
+ return VERR_FILE_AIO_INSUFFICIENT_RESSOURCES;
+ }
+ else
+ {
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ pReqInt->Rc = RTErrConvertFromErrno(errno);
+ pReqInt->cbTransfered = 0;
+ return pReqInt->Rc;
+ }
+ }
+
+ ASMAtomicIncS32(&pCtxInt->cRequests);
+ cReqs--;
+ pahReqs++;
+ }
+ }
+ } while (cReqs);
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
+ PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
+{
+ int rc = VINF_SUCCESS;
+ int cRequestsCompleted = 0;
+
+ /*
+ * Validate the parameters, making sure to always set pcReqs.
+ */
+ AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
+ *pcReqs = 0; /* always set */
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
+
+ if ( RT_UNLIKELY(ASMAtomicReadS32(&pCtxInt->cRequests) == 0)
+ && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
+ return VERR_FILE_AIO_NO_REQUEST;
+
+ /*
+ * Convert the timeout if specified.
+ */
+ struct timespec *pTimeout = NULL;
+ struct timespec Timeout = {0,0};
+ uint64_t StartNanoTS = 0;
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ Timeout.tv_sec = cMillies / 1000;
+ Timeout.tv_nsec = cMillies % 1000 * 1000000;
+ pTimeout = &Timeout;
+ StartNanoTS = RTTimeNanoTS();
+ }
+
+ /* Wait for at least one. */
+ if (!cMinReqs)
+ cMinReqs = 1;
+
+ /* For the wakeup call. */
+ Assert(pCtxInt->hThreadWait == NIL_RTTHREAD);
+ ASMAtomicWriteHandle(&pCtxInt->hThreadWait, RTThreadSelf());
+
+ while ( cMinReqs
+ && RT_SUCCESS_NP(rc))
+ {
+ struct kevent aKEvents[AIO_MAXIMUM_REQUESTS_PER_CONTEXT];
+ int cRequestsToWait = cMinReqs < AIO_MAXIMUM_REQUESTS_PER_CONTEXT ? cReqs : AIO_MAXIMUM_REQUESTS_PER_CONTEXT;
+ int rcBSD;
+ uint64_t StartTime;
+
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, true);
+ rcBSD = kevent(pCtxInt->iKQueue, NULL, 0, aKEvents, cRequestsToWait, pTimeout);
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, false);
+
+ if (RT_UNLIKELY(rcBSD < 0))
+ {
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+
+ uint32_t const cDone = rcBSD;
+
+ /* Process received events. */
+ for (uint32_t i = 0; i < cDone; i++)
+ {
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)aKEvents[i].udata;
+ AssertPtr(pReqInt);
+ Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
+
+ /*
+ * Retrieve the status code here already because the
+ * user may omit the RTFileAioReqGetRC() call and
+ * we will leak kernel resources then.
+ * This will result in errors during submission
+ * of other requests as soon as the max_aio_queue_per_proc
+ * limit is reached.
+ */
+ int cbTransfered = aio_return(&pReqInt->AioCB);
+
+ if (cbTransfered < 0)
+ {
+ pReqInt->Rc = RTErrConvertFromErrno(cbTransfered);
+ pReqInt->cbTransfered = 0;
+ }
+ else
+ {
+ pReqInt->Rc = VINF_SUCCESS;
+ pReqInt->cbTransfered = cbTransfered;
+ }
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt;
+ }
+
+ /*
+ * Done Yet? If not advance and try again.
+ */
+ if (cDone >= cMinReqs)
+ break;
+ cMinReqs -= cDone;
+ cReqs -= cDone;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ /* The API doesn't return ETIMEDOUT, so we have to fix that ourselves. */
+ uint64_t NanoTS = RTTimeNanoTS();
+ uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
+ if (cMilliesElapsed >= cMillies)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+
+ /* The syscall supposedly updates it, but we're paranoid. :-) */
+ Timeout.tv_sec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) / 1000;
+ Timeout.tv_nsec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) % 1000 * 1000000;
+ }
+ }
+
+ /*
+ * Update the context state and set the return value.
+ */
+ *pcReqs = cRequestsCompleted;
+ ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
+ Assert(pCtxInt->hThreadWait == RTThreadSelf());
+ ASMAtomicWriteHandle(&pCtxInt->hThreadWait, NIL_RTTHREAD);
+
+ /*
+ * Clear the wakeup flag and set rc.
+ */
+ if ( pCtxInt->fWokenUp
+ && RT_SUCCESS(rc))
+ {
+ ASMAtomicXchgBool(&pCtxInt->fWokenUp, false);
+ rc = VERR_INTERRUPTED;
+ }
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
+{
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ /** @todo r=bird: Define the protocol for how to resume work after calling
+ * this function. */
+
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
+
+ /*
+ * Read the thread handle before the status flag.
+ * If we read the handle after the flag we might
+ * end up with an invalid handle because the thread
+ * waiting in RTFileAioCtxWakeup() might get scheduled
+ * before we read the flag and returns.
+ * We can ensure that the handle is valid if fWaiting is true
+ * when reading the handle before the status flag.
+ */
+ RTTHREAD hThread;
+ ASMAtomicReadHandle(&pCtxInt->hThreadWait, &hThread);
+ bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
+ if ( !fWokenUp
+ && fWaiting)
+ {
+ /*
+ * If a thread waits the handle must be valid.
+ * It is possible that the thread returns from
+ * kevent() before the signal is send.
+ * This is no problem because we already set fWokenUp
+ * to true which will let the thread return VERR_INTERRUPTED
+ * and the next call to RTFileAioCtxWait() will not
+ * return VERR_INTERRUPTED because signals are not saved
+ * and will simply vanish if the destination thread can't
+ * receive it.
+ */
+ Assert(hThread != NIL_RTTHREAD);
+ RTThreadPoke(hThread);
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/freebsd/mp-freebsd.cpp b/src/VBox/Runtime/r3/freebsd/mp-freebsd.cpp
new file mode 100644
index 00000000..8b2f5f83
--- /dev/null
+++ b/src/VBox/Runtime/r3/freebsd/mp-freebsd.cpp
@@ -0,0 +1,211 @@
+/* $Id: mp-freebsd.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, FreeBSD.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYSTEM
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/sysctl.h>
+
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/alloc.h>
+#include <iprt/log.h>
+#include <iprt/once.h>
+#include <iprt/critsect.h>
+
+
+/**
+ * Internal worker that determines the max possible CPU count.
+ *
+ * @returns Max cpus.
+ */
+static RTCPUID rtMpFreeBsdMaxCpus(void)
+{
+ int aiMib[2];
+ aiMib[0] = CTL_HW;
+ aiMib[1] = HW_NCPU;
+ int cCpus = -1;
+ size_t cb = sizeof(cCpus);
+ int rc = sysctl(aiMib, RT_ELEMENTS(aiMib), &cCpus, &cb, NULL, 0);
+ if (rc != -1 && cCpus >= 1)
+ return cCpus;
+ AssertFailed();
+ return 1;
+}
+
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS && idCpu < rtMpFreeBsdMaxCpus() ? idCpu : -1;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu < rtMpFreeBsdMaxCpus() ? iCpu : NIL_RTCPUID;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return rtMpFreeBsdMaxCpus() - 1;
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ /*
+ * FreeBSD doesn't support CPU hotplugging so every CPU which appears
+ * in the tree is also online.
+ */
+ char szName[40];
+ RTStrPrintf(szName, sizeof(szName), "dev.cpu.%d.%%driver", (int)idCpu);
+
+ char szDriver[10];
+ size_t cbDriver = sizeof(szDriver);
+ RT_ZERO(szDriver); /* this shouldn't be necessary. */
+ int rcBsd = sysctlbyname(szName, szDriver, &cbDriver, NULL, 0);
+ if (rcBsd == 0)
+ return true;
+
+ return false;
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ return idCpu != NIL_RTCPUID
+ && idCpu < rtMpFreeBsdMaxCpus();
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCpuSetEmpty(pSet);
+ RTCPUID cMax = rtMpFreeBsdMaxCpus();
+ for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
+ if (RTMpIsCpuPossible(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ return rtMpFreeBsdMaxCpus();
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ RTCpuSetEmpty(pSet);
+ RTCPUID cMax = rtMpFreeBsdMaxCpus();
+ for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
+ if (RTMpIsCpuOnline(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ /*
+ * FreeBSD has sysconf.
+ */
+ return sysconf(_SC_NPROCESSORS_ONLN);
+}
+
+
+RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
+{
+ int uFreqCurr = 0;
+ size_t cbParameter = sizeof(uFreqCurr);
+
+ if (!RTMpIsCpuOnline(idCpu))
+ return 0;
+
+ /* CPU's have a common frequency. */
+ int rc = sysctlbyname("dev.cpu.0.freq", &uFreqCurr, &cbParameter, NULL, 0);
+ if (rc)
+ return 0;
+
+ return (uint32_t)uFreqCurr;
+}
+
+
+RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
+{
+ char szFreqLevels[20]; /* Should be enough to get the highest level which is always the first. */
+ size_t cbFreqLevels = sizeof(szFreqLevels);
+
+ if (!RTMpIsCpuOnline(idCpu))
+ return 0;
+
+ memset(szFreqLevels, 0, sizeof(szFreqLevels));
+
+ /*
+ * CPU 0 has the freq levels entry. ENOMEM is ok as we don't need all supported
+ * levels but only the first one.
+ */
+ int rc = sysctlbyname("dev.cpu.0.freq_levels", szFreqLevels, &cbFreqLevels, NULL, 0);
+ if ( (rc && (errno != ENOMEM))
+ || (cbFreqLevels == 0))
+ return 0;
+
+ /* Clear everything starting from the '/' */
+ unsigned i = 0;
+
+ do
+ {
+ if (szFreqLevels[i] == '/')
+ {
+ memset(&szFreqLevels[i], 0, sizeof(szFreqLevels) - i);
+ break;
+ }
+ i++;
+ } while (i < sizeof(szFreqLevels));
+
+ /* Returns 0 on failure. */
+ return RTStrToUInt32(szFreqLevels);
+}
+
diff --git a/src/VBox/Runtime/r3/freebsd/rtProcInitExePath-freebsd.cpp b/src/VBox/Runtime/r3/freebsd/rtProcInitExePath-freebsd.cpp
new file mode 100644
index 00000000..5994f994
--- /dev/null
+++ b/src/VBox/Runtime/r3/freebsd/rtProcInitExePath-freebsd.cpp
@@ -0,0 +1,139 @@
+/* $Id: rtProcInitExePath-freebsd.cpp $ */
+/** @file
+ * IPRT - rtProcInitName, FreeBSD.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <link.h>
+
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include "internal/process.h"
+#include "internal/path.h"
+
+
+DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath)
+{
+#ifdef KERN_PROC_PATHNAME
+ int aiName[4];
+ aiName[0] = CTL_KERN;
+ aiName[1] = KERN_PROC;
+ aiName[2] = KERN_PROC_PATHNAME; /* This was introduced in FreeBSD 6.0, thus the #ifdef above. */
+ aiName[3] = -1; /* Shorthand for the current process. */
+
+ size_t cchExePath = cchPath;
+ if (sysctl(aiName, RT_ELEMENTS(aiName), pszPath, &cchExePath, NULL, 0) == 0)
+ {
+ const char *pszTmp;
+ int rc = rtPathFromNative(&pszTmp, pszPath, NULL);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszPath=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, cchExePath, pszPath), rc);
+ if (pszTmp != pszPath)
+ {
+ rc = RTStrCopy(pszPath, cchPath, pszTmp);
+ rtPathFreeIprt(pszTmp, pszPath);
+ }
+ return rc;
+ }
+
+ int rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("rc=%Rrc errno=%d cchExePath=%d\n", rc, errno, cchExePath));
+ return rc;
+
+#else
+
+ /*
+ * Read the /proc/curproc/file link, convert to native and return it.
+ */
+ int cchLink = readlink("/proc/curproc/file", pszPath, cchPath - 1);
+ if (cchLink > 0 && (size_t)cchLink <= cchPath - 1)
+ {
+ pszPath[cchLink] = '\0';
+
+ char const *pszTmp;
+ int rc = rtPathFromNative(&pszTmp, pszPath);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, cchLink, pszPath), rc);
+ if (pszTmp != pszPath)
+ {
+ rc = RTStrCopy(pszPath, cchPath, pszTmp);
+ rtPathFreeIprt(pszTmp);
+ }
+ return rc;
+ }
+
+ int err = errno;
+
+ /*
+ * Fall back on the dynamic linker since /proc is optional.
+ */
+ void *hExe = dlopen(NULL, 0);
+ if (hExe)
+ {
+ struct link_map const *pLinkMap = 0;
+ if (dlinfo(hExe, RTLD_DI_LINKMAP, &pLinkMap) == 0)
+ {
+ const char *pszImageName = pLinkMap->l_name;
+ if (*pszImageName == '/') /* this may not always be absolute, despite the docs. :-( */
+ {
+ char const *pszTmp;
+ int rc = rtPathFromNative(&pszTmp, pszImageName, NULL);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszImageName=\"%s\"\n", rc, pszImageName), rc);
+ if (pszTmp != pszPath)
+ {
+ rc = RTStrCopy(pszPath, cchPath, pszTmp);
+ rtPathFreeIprt(pszTmp, pszPath);
+ }
+ return rc;
+ }
+ /** @todo Try search the PATH for the file name or append the current
+ * directory, which ever makes sense... */
+ }
+ }
+
+ int rc = RTErrConvertFromErrno(err);
+ AssertMsgFailed(("rc=%Rrc err=%d cchLink=%d hExe=%p\n", rc, err, cchLink, hExe));
+ return rc;
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/freebsd/systemmem-freebsd.cpp b/src/VBox/Runtime/r3/freebsd/systemmem-freebsd.cpp
new file mode 100644
index 00000000..b662fc67
--- /dev/null
+++ b/src/VBox/Runtime/r3/freebsd/systemmem-freebsd.cpp
@@ -0,0 +1,108 @@
+/* $Id: systemmem-freebsd.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryTotalRam, Linux ring-3.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <errno.h>
+
+
+RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb)
+{
+ int rc = VINF_SUCCESS;
+ u_long cbMemPhys = 0;
+ size_t cbParameter = sizeof(cbMemPhys);
+
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ if (!sysctlbyname("hw.physmem", &cbMemPhys, &cbParameter, NULL, 0))
+ {
+ *pcb = cbMemPhys;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ u_int cPagesMemFree = 0;
+ u_int cPagesMemInactive = 0;
+ u_int cPagesMemCached = 0;
+ u_int cPagesMemUsed = 0;
+ int cbPage = 0;
+ size_t cbParameter;
+ int cProcessed = 0;
+
+ cbParameter = sizeof(cPagesMemFree);
+ if (sysctlbyname("vm.stats.vm.v_free_count", &cPagesMemFree, &cbParameter, NULL, 0))
+ rc = RTErrConvertFromErrno(errno);
+ cbParameter = sizeof(cPagesMemUsed);
+ if ( RT_SUCCESS(rc)
+ && sysctlbyname("vm.stats.vm.v_active_count", &cPagesMemUsed, &cbParameter, NULL, 0))
+ rc = RTErrConvertFromErrno(errno);
+ cbParameter = sizeof(cPagesMemInactive);
+ if ( RT_SUCCESS(rc)
+ && sysctlbyname("vm.stats.vm.v_inactive_count", &cPagesMemInactive, &cbParameter, NULL, 0))
+ rc = RTErrConvertFromErrno(errno);
+ cbParameter = sizeof(cPagesMemCached);
+ if ( RT_SUCCESS(rc)
+ && sysctlbyname("vm.stats.vm.v_cache_count", &cPagesMemCached, &cbParameter, NULL, 0))
+ rc = RTErrConvertFromErrno(errno);
+ cbParameter = sizeof(cbPage);
+ if ( RT_SUCCESS(rc)
+ && sysctlbyname("hw.pagesize", &cbPage, &cbParameter, NULL, 0))
+ rc = RTErrConvertFromErrno(errno);
+
+ if (RT_SUCCESS(rc))
+ *pcb = (cPagesMemFree + cPagesMemInactive + cPagesMemCached ) * cbPage;
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/fs.cpp b/src/VBox/Runtime/r3/fs.cpp
new file mode 100644
index 00000000..41f7de8d
--- /dev/null
+++ b/src/VBox/Runtime/r3/fs.cpp
@@ -0,0 +1,274 @@
+/* $Id: fs.cpp $ */
+/** @file
+ * IPRT - File System.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/fs.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include "internal/fs.h"
+
+
+/**
+ * Converts dos-style attributes to Unix attributes.
+ *
+ * @returns Normalized mode mask.
+ * @param fMode The mode mask containing dos-style attributes only.
+ * @param pszName The filename which this applies to (exe check).
+ * @param cbName The length of that filename. (optional, set 0)
+ * @param uReparseTag The reparse tag if RTFS_DOS_NT_REPARSE_POINT is set.
+ * @param fType RTFS_TYPE_XXX to normalize against, 0 if not known.
+ */
+RTFMODE rtFsModeFromDos(RTFMODE fMode, const char *pszName, size_t cbName, uint32_t uReparseTag, RTFMODE fType)
+{
+ Assert(!(fType & ~RTFS_TYPE_MASK));
+
+ fMode &= ~((1 << RTFS_DOS_SHIFT) - 1);
+
+ /* Forcibly set the directory attribute if caller desires it. */
+ if (fType == RTFS_TYPE_DIRECTORY)
+ fMode |= RTFS_DOS_DIRECTORY;
+
+ /* Everything is readable. */
+ fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
+ if (fMode & RTFS_DOS_DIRECTORY)
+ /* Directories are executable. */
+ fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
+ else
+ {
+ fMode |= RTFS_TYPE_FILE;
+ if (!cbName && pszName)
+ cbName = strlen(pszName);
+ if (cbName >= 4 && pszName[cbName - 4] == '.')
+ {
+ /* check for executable extension. */
+ const char *pszExt = &pszName[cbName - 3];
+ char szExt[4];
+ szExt[0] = RT_C_TO_LOWER(pszExt[0]);
+ szExt[1] = RT_C_TO_LOWER(pszExt[1]);
+ szExt[2] = RT_C_TO_LOWER(pszExt[2]);
+ szExt[3] = '\0';
+ if ( !memcmp(szExt, "exe", 4)
+ || !memcmp(szExt, "bat", 4)
+ || !memcmp(szExt, "com", 4)
+ || !memcmp(szExt, "cmd", 4)
+ || !memcmp(szExt, "btm", 4)
+ )
+ fMode |= RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
+ }
+ }
+
+ /* Is it really a symbolic link? */
+ if ((fMode & RTFS_DOS_NT_REPARSE_POINT) && uReparseTag == RTFSMODE_SYMLINK_REPARSE_TAG)
+ fMode = (fMode & ~RTFS_TYPE_MASK) | RTFS_TYPE_SYMLINK;
+
+ /*
+ * Writable?
+ *
+ * Note! We ignore the read-only flag on directories as windows seems to
+ * use it for purposes other than writability (@ticketref{18345}):
+ * https://support.microsoft.com/en-gb/help/326549/you-cannot-view-or-change-the-read-only-or-the-system-attributes-of-fo
+ *
+ */
+ if ((fMode & (RTFS_DOS_DIRECTORY | RTFS_DOS_READONLY)) != RTFS_DOS_READONLY)
+ fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
+ return fMode;
+}
+
+
+/**
+ * Converts Unix attributes to Dos-style attributes.
+ *
+ * @returns File mode mask.
+ * @param fMode The mode mask containing dos-style attributes only.
+ * @param pszName The filename which this applies to (hidden check).
+ * @param cbName The length of that filename. (optional, set 0)
+ * @param fType RTFS_TYPE_XXX to normalize against, 0 if not known.
+ */
+RTFMODE rtFsModeFromUnix(RTFMODE fMode, const char *pszName, size_t cbName, RTFMODE fType)
+{
+ Assert(!(fType & ~RTFS_TYPE_MASK));
+ NOREF(cbName);
+
+ fMode &= RTFS_UNIX_MASK;
+
+ if (!(fType & RTFS_TYPE_MASK) && fType)
+ fMode |= fType;
+
+ if (!(fMode & (RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH)))
+ fMode |= RTFS_DOS_READONLY;
+ if (RTFS_IS_DIRECTORY(fMode))
+ fMode |= RTFS_DOS_DIRECTORY;
+ if (!(fMode & RTFS_DOS_MASK))
+ fMode |= RTFS_DOS_NT_NORMAL;
+ if (!(fMode & RTFS_DOS_HIDDEN) && pszName)
+ {
+ pszName = RTPathFilename(pszName);
+ if ( pszName
+ && pszName[0] == '.'
+ && pszName[1] != '\0' /* exclude "." */
+ && (pszName[1] != '.' || pszName[2] != '\0')) /* exclude ".." */
+ fMode |= RTFS_DOS_HIDDEN;
+ }
+ return fMode;
+}
+
+
+/**
+ * Normalizes the give mode mask.
+ *
+ * It will create the missing unix or dos mask from the other (one
+ * of them is required by all APIs), and guess the file type if that's
+ * missing.
+ *
+ * @returns Normalized file mode.
+ * @param fMode The mode mask that may contain a partial/incomplete mask.
+ * @param pszName The filename which this applies to (exe check).
+ * @param cbName The length of that filename. (optional, set 0)
+ * @param fType RTFS_TYPE_XXX to normalize against, 0 if not known.
+ */
+RTFMODE rtFsModeNormalize(RTFMODE fMode, const char *pszName, size_t cbName, RTFMODE fType)
+{
+ Assert(!(fType & ~RTFS_TYPE_MASK));
+
+ if (!(fMode & RTFS_UNIX_MASK))
+ fMode = rtFsModeFromDos(fMode, pszName, cbName, RTFSMODE_SYMLINK_REPARSE_TAG, fType);
+ else if (!(fMode & RTFS_DOS_MASK))
+ fMode = rtFsModeFromUnix(fMode, pszName, cbName, fType);
+ else if (!(fMode & RTFS_TYPE_MASK))
+ fMode |= fMode & RTFS_DOS_DIRECTORY ? RTFS_TYPE_DIRECTORY : RTFS_TYPE_FILE;
+ else if (RTFS_IS_DIRECTORY(fMode))
+ fMode |= RTFS_DOS_DIRECTORY;
+ return fMode;
+}
+
+
+/**
+ * Checks if the file mode is valid or not.
+ *
+ * @return true if valid.
+ * @return false if invalid, done bitching.
+ * @param fMode The file mode.
+ */
+bool rtFsModeIsValid(RTFMODE fMode)
+{
+ AssertMsgReturn( (!RTFS_IS_DIRECTORY(fMode) && !(fMode & RTFS_DOS_DIRECTORY))
+ || (RTFS_IS_DIRECTORY(fMode) && (fMode & RTFS_DOS_DIRECTORY)),
+ ("%RTfmode\n", fMode), false);
+ AssertMsgReturn(RTFS_TYPE_MASK & fMode,
+ ("%RTfmode\n", fMode), false);
+ /** @todo more checks! */
+ return true;
+}
+
+
+/**
+ * Checks if the file mode is valid as a permission mask or not.
+ *
+ * @return true if valid.
+ * @return false if invalid, done bitching.
+ * @param fMode The file mode.
+ */
+bool rtFsModeIsValidPermissions(RTFMODE fMode)
+{
+ AssertMsgReturn( (!RTFS_IS_DIRECTORY(fMode) && !(fMode & RTFS_DOS_DIRECTORY))
+ || (RTFS_IS_DIRECTORY(fMode) && (fMode & RTFS_DOS_DIRECTORY)),
+ ("%RTfmode\n", fMode), false);
+ /** @todo more checks! */
+ return true;
+}
+
+
+RTDECL(const char *) RTFsTypeName(RTFSTYPE enmType)
+{
+ switch (enmType)
+ {
+ case RTFSTYPE_UNKNOWN: return "unknown";
+ case RTFSTYPE_UDF: return "udf";
+ case RTFSTYPE_ISO9660: return "iso9660";
+ case RTFSTYPE_FUSE: return "fuse";
+ case RTFSTYPE_VBOXSHF: return "vboxshf";
+
+ case RTFSTYPE_EXT: return "ext";
+ case RTFSTYPE_EXT2: return "ext2";
+ case RTFSTYPE_EXT3: return "ext3";
+ case RTFSTYPE_EXT4: return "ext4";
+ case RTFSTYPE_XFS: return "xfs";
+ case RTFSTYPE_CIFS: return "cifs";
+ case RTFSTYPE_SMBFS: return "smbfs";
+ case RTFSTYPE_TMPFS: return "tmpfs";
+ case RTFSTYPE_SYSFS: return "sysfs";
+ case RTFSTYPE_PROC: return "proc";
+ case RTFSTYPE_OCFS2: return "ocfs2";
+ case RTFSTYPE_BTRFS: return "btrfs";
+
+ case RTFSTYPE_NTFS: return "ntfs";
+ case RTFSTYPE_FAT: return "fat";
+ case RTFSTYPE_EXFAT: return "exfat";
+ case RTFSTYPE_REFS: return "refs";
+
+ case RTFSTYPE_ZFS: return "zfs";
+ case RTFSTYPE_UFS: return "ufs";
+ case RTFSTYPE_NFS: return "nfs";
+
+ case RTFSTYPE_HFS: return "hfs";
+ case RTFSTYPE_APFS: return "apfs";
+ case RTFSTYPE_AUTOFS: return "autofs";
+ case RTFSTYPE_DEVFS: return "devfs";
+
+ case RTFSTYPE_HPFS: return "hpfs";
+ case RTFSTYPE_JFS: return "jfs";
+
+ case RTFSTYPE_END: return "end";
+ case RTFSTYPE_32BIT_HACK: break;
+ }
+
+ /* Don't put this in as 'default:', we wish GCC to warn about missing cases. */
+ static char s_asz[4][64];
+ static uint32_t volatile s_i = 0;
+ uint32_t i = ASMAtomicIncU32(&s_i) % RT_ELEMENTS(s_asz);
+ RTStrPrintf(s_asz[i], sizeof(s_asz[i]), "type=%d", enmType);
+ return s_asz[i];
+}
+
diff --git a/src/VBox/Runtime/r3/ftp-server.cpp b/src/VBox/Runtime/r3/ftp-server.cpp
new file mode 100644
index 00000000..425aad03
--- /dev/null
+++ b/src/VBox/Runtime/r3/ftp-server.cpp
@@ -0,0 +1,2589 @@
+/* $Id: ftp-server.cpp $ */
+/** @file
+ * Generic FTP server (RFC 959) implementation.
+ *
+ * Partly also implements RFC 3659 (Extensions to FTP, for "SIZE", ++).
+ *
+ * Known limitations so far:
+ * - UTF-8 support only.
+ * - Only supports ASCII + binary (image type) file streams for now.
+ * - No directory / file caching yet.
+ * - No support for writing / modifying ("DELE", "MKD", "RMD", "STOR", ++).
+ * - No FTPS / SFTP support.
+ * - No passive mode ("PASV") support.
+ * - No IPv6 support.
+ * - No proxy support.
+ * - No FXP support.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FTP
+#include <iprt/ftp.h>
+#include "internal/iprt.h"
+#include "internal/magics.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/circbuf.h>
+#include <iprt/err.h>
+#include <iprt/file.h> /* For file mode flags. */
+#include <iprt/getopt.h>
+#include <iprt/mem.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+#include <iprt/poll.h>
+#include <iprt/socket.h>
+#include <iprt/sort.h>
+#include <iprt/string.h>
+#include <iprt/system.h>
+#include <iprt/tcp.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Internal FTP server instance.
+ */
+typedef struct RTFTPSERVERINTERNAL
+{
+ /** Magic value. */
+ uint32_t u32Magic;
+ /** Callback table. */
+ RTFTPSERVERCALLBACKS Callbacks;
+ /** Pointer to TCP server instance. */
+ PRTTCPSERVER pTCPServer;
+ /** Number of currently connected clients. */
+ uint32_t cClients;
+ /** Pointer to user-specific data. Optional. */
+ void *pvUser;
+ /** Size of user-specific data. Optional. */
+ size_t cbUser;
+} RTFTPSERVERINTERNAL;
+/** Pointer to an internal FTP server instance. */
+typedef RTFTPSERVERINTERNAL *PRTFTPSERVERINTERNAL;
+
+/**
+ * FTP directory entry.
+ */
+typedef struct RTFTPDIRENTRY
+{
+ /** The information about the entry. */
+ RTFSOBJINFO Info;
+ /** Symbolic link target (allocated after the name). */
+ const char *pszTarget;
+ /** Owner if applicable (allocated after the name). */
+ const char *pszOwner;
+ /** Group if applicable (allocated after the name). */
+ const char *pszGroup;
+ /** The length of szName. */
+ size_t cchName;
+ /** The entry name. */
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ char szName[RT_FLEXIBLE_ARRAY];
+} RTFTPDIRENTRY;
+/** Pointer to a FTP directory entry. */
+typedef RTFTPDIRENTRY *PRTFTPDIRENTRY;
+/** Pointer to a FTP directory entry pointer. */
+typedef PRTFTPDIRENTRY *PPRTFTPDIRENTRY;
+
+/**
+ * Collection of directory entries.
+ * Used for also caching stuff.
+ */
+typedef struct RTFTPDIRCOLLECTION
+{
+ /** Current size of papEntries. */
+ size_t cEntries;
+ /** Memory allocated for papEntries. */
+ size_t cEntriesAllocated;
+ /** Current entries pending sorting and display. */
+ PPRTFTPDIRENTRY papEntries;
+
+ /** Total number of bytes allocated for the above entries. */
+ uint64_t cbTotalAllocated;
+ /** Total number of file content bytes. */
+ uint64_t cbTotalFiles;
+
+} RTFTPDIRCOLLECTION;
+/** Pointer to a directory collection. */
+typedef RTFTPDIRCOLLECTION *PRTFTPDIRCOLLECTION;
+/** Pointer to a directory entry collection pointer. */
+typedef PRTFTPDIRCOLLECTION *PPRTFTPDIRCOLLECTION;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
+#define RTFTPSERVER_VALID_RETURN_RC(hFTPServer, a_rc) \
+ do { \
+ AssertPtrReturn((hFTPServer), (a_rc)); \
+ AssertReturn((hFTPServer)->u32Magic == RTFTPSERVER_MAGIC, (a_rc)); \
+ } while (0)
+
+/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
+#define RTFTPSERVER_VALID_RETURN(hFTPServer) RTFTPSERVER_VALID_RETURN_RC((hFTPServer), VERR_INVALID_HANDLE)
+
+/** Validates a handle and returns (void) if not valid. */
+#define RTFTPSERVER_VALID_RETURN_VOID(hFTPServer) \
+ do { \
+ AssertPtrReturnVoid(hFTPServer); \
+ AssertReturnVoid((hFTPServer)->u32Magic == RTFTPSERVER_MAGIC); \
+ } while (0)
+
+
+/** Handles a FTP server callback with no arguments and returns. */
+#define RTFTPSERVER_HANDLE_CALLBACK_RET(a_Name) \
+ do \
+ { \
+ PRTFTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
+ if (pCallbacks->a_Name) \
+ { \
+ RTFTPCALLBACKDATA Data = { &pClient->State }; \
+ return pCallbacks->a_Name(&Data); \
+ } \
+ return VERR_NOT_IMPLEMENTED; \
+ } while (0)
+
+/** Handles a FTP server callback with no arguments and sets rc accordingly. */
+#define RTFTPSERVER_HANDLE_CALLBACK(a_Name) \
+ do \
+ { \
+ PRTFTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
+ if (pCallbacks->a_Name) \
+ { \
+ RTFTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
+ rc = pCallbacks->a_Name(&Data); \
+ } \
+ else \
+ rc = VERR_NOT_IMPLEMENTED; \
+ } while (0)
+
+/** Handles a FTP server callback with arguments and sets rc accordingly. */
+#define RTFTPSERVER_HANDLE_CALLBACK_VA(a_Name, ...) \
+ do \
+ { \
+ PRTFTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
+ if (pCallbacks->a_Name) \
+ { \
+ RTFTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
+ rc = pCallbacks->a_Name(&Data, __VA_ARGS__); \
+ } \
+ else \
+ rc = VERR_NOT_IMPLEMENTED; \
+ } while (0)
+
+/** Handles a FTP server callback with arguments and returns. */
+#define RTFTPSERVER_HANDLE_CALLBACK_VA_RET(a_Name, ...) \
+ do \
+ { \
+ PRTFTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
+ if (pCallbacks->a_Name) \
+ { \
+ RTFTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
+ return pCallbacks->a_Name(&Data, __VA_ARGS__); \
+ } \
+ return VERR_NOT_IMPLEMENTED; \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Supported FTP server command IDs.
+ * Alphabetically, named after their official command names. */
+typedef enum RTFTPSERVERCMD
+{
+ /** Invalid command, do not use. Always must come first. */
+ RTFTPSERVERCMD_INVALID = 0,
+ /** Aborts the current command on the server. */
+ RTFTPSERVERCMD_ABOR,
+ /** Changes the current working directory. */
+ RTFTPSERVERCMD_CDUP,
+ /** Changes the current working directory. */
+ RTFTPSERVERCMD_CWD,
+ /** Reports features supported by the server. */
+ RTFTPSERVERCMD_FEAT,
+ /** Lists a directory. */
+ RTFTPSERVERCMD_LIST,
+ /** Sets the transfer mode. */
+ RTFTPSERVERCMD_MODE,
+ /** Sends a nop ("no operation") to the server. */
+ RTFTPSERVERCMD_NOOP,
+ /** Sets the password for authentication. */
+ RTFTPSERVERCMD_PASS,
+ /** Sets the port to use for the data connection. */
+ RTFTPSERVERCMD_PORT,
+ /** Gets the current working directory. */
+ RTFTPSERVERCMD_PWD,
+ /** Get options. Needed in conjunction with the FEAT command. */
+ RTFTPSERVERCMD_OPTS,
+ /** Terminates the session (connection). */
+ RTFTPSERVERCMD_QUIT,
+ /** Retrieves a specific file. */
+ RTFTPSERVERCMD_RETR,
+ /** Retrieves the size of a file. */
+ RTFTPSERVERCMD_SIZE,
+ /** Retrieves the current status of a transfer. */
+ RTFTPSERVERCMD_STAT,
+ /** Sets the structure type to use. */
+ RTFTPSERVERCMD_STRU,
+ /** Gets the server's OS info. */
+ RTFTPSERVERCMD_SYST,
+ /** Sets the (data) representation type. */
+ RTFTPSERVERCMD_TYPE,
+ /** Sets the user name for authentication. */
+ RTFTPSERVERCMD_USER,
+ /** End marker. */
+ RTFTPSERVERCMD_END,
+ /** The usual 32-bit hack. */
+ RTFTPSERVERCMD_32BIT_HACK = 0x7fffffff
+} RTFTPSERVERCMD;
+
+struct RTFTPSERVERCLIENT;
+
+/**
+ * Structure for maintaining a single data connection.
+ */
+typedef struct RTFTPSERVERDATACONN
+{
+ /** Pointer to associated client of this data connection. */
+ RTFTPSERVERCLIENT *pClient;
+ /** Data connection IP. */
+ RTNETADDRIPV4 Addr;
+ /** Data connection port number. */
+ uint16_t uPort;
+ /** The current data socket to use.
+ * Can be NIL_RTSOCKET if no data port has been specified (yet) or has been closed. */
+ RTSOCKET hSocket;
+ /** Thread serving the data connection. */
+ RTTHREAD hThread;
+ /** Thread started indicator. */
+ volatile bool fStarted;
+ /** Thread stop indicator. */
+ volatile bool fStop;
+ /** Thread stopped indicator. */
+ volatile bool fStopped;
+ /** Overall result when closing the data connection. */
+ int rc;
+ /** Number of command arguments. */
+ uint8_t cArgs;
+ /** Command arguments array. Optional and can be NULL.
+ * Will be free'd by the data connection thread. */
+ char** papszArgs;
+ /** Circular buffer for caching data before writing. */
+ PRTCIRCBUF pCircBuf;
+} RTFTPSERVERDATACONN;
+/** Pointer to a data connection struct. */
+typedef RTFTPSERVERDATACONN *PRTFTPSERVERDATACONN;
+
+/**
+ * Structure for maintaining an internal FTP server client.
+ */
+typedef struct RTFTPSERVERCLIENT
+{
+ /** Pointer to internal server state. */
+ PRTFTPSERVERINTERNAL pServer;
+ /** Socket handle the client is bound to. */
+ RTSOCKET hSocket;
+ /** Actual client state. */
+ RTFTPSERVERCLIENTSTATE State;
+ /** The last set data connection IP. */
+ RTNETADDRIPV4 DataConnAddr;
+ /** The last set data connection port number. */
+ uint16_t uDataConnPort;
+ /** Data connection information.
+ * At the moment we only allow one data connection per client at a time. */
+ PRTFTPSERVERDATACONN pDataConn;
+} RTFTPSERVERCLIENT;
+/** Pointer to an internal FTP server client state. */
+typedef RTFTPSERVERCLIENT *PRTFTPSERVERCLIENT;
+
+/** Function pointer declaration for a specific FTP server command handler. */
+typedef DECLCALLBACKTYPE(int, FNRTFTPSERVERCMD,(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs));
+/** Pointer to a FNRTFTPSERVERCMD(). */
+typedef FNRTFTPSERVERCMD *PFNRTFTPSERVERCMD;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int rtFtpServerDataConnOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort);
+static int rtFtpServerDataConnClose(PRTFTPSERVERDATACONN pDataConn);
+static void rtFtpServerDataConnReset(PRTFTPSERVERDATACONN pDataConn);
+static int rtFtpServerDataConnStart(PRTFTPSERVERDATACONN pDataConn, PFNRTTHREAD pfnThread, uint8_t cArgs, const char * const *apszArgs);
+static int rtFtpServerDataConnStop(PRTFTPSERVERDATACONN pDataConn);
+static void rtFtpServerDataConnDestroy(PRTFTPSERVERDATACONN pDataConn);
+static int rtFtpServerDataConnFlush(PRTFTPSERVERDATACONN pDataConn);
+
+static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState);
+
+/** @name Command handlers.
+ * @{
+ */
+static FNRTFTPSERVERCMD rtFtpServerHandleABOR;
+static FNRTFTPSERVERCMD rtFtpServerHandleCDUP;
+static FNRTFTPSERVERCMD rtFtpServerHandleCWD;
+static FNRTFTPSERVERCMD rtFtpServerHandleFEAT;
+static FNRTFTPSERVERCMD rtFtpServerHandleLIST;
+static FNRTFTPSERVERCMD rtFtpServerHandleMODE;
+static FNRTFTPSERVERCMD rtFtpServerHandleNOOP;
+static FNRTFTPSERVERCMD rtFtpServerHandlePASS;
+static FNRTFTPSERVERCMD rtFtpServerHandlePORT;
+static FNRTFTPSERVERCMD rtFtpServerHandlePWD;
+static FNRTFTPSERVERCMD rtFtpServerHandleOPTS;
+static FNRTFTPSERVERCMD rtFtpServerHandleQUIT;
+static FNRTFTPSERVERCMD rtFtpServerHandleRETR;
+static FNRTFTPSERVERCMD rtFtpServerHandleSIZE;
+static FNRTFTPSERVERCMD rtFtpServerHandleSTAT;
+static FNRTFTPSERVERCMD rtFtpServerHandleSTRU;
+static FNRTFTPSERVERCMD rtFtpServerHandleSYST;
+static FNRTFTPSERVERCMD rtFtpServerHandleTYPE;
+static FNRTFTPSERVERCMD rtFtpServerHandleUSER;
+/** @} */
+
+/**
+ * Structure for maintaining a single command entry for the command table.
+ */
+typedef struct RTFTPSERVERCMD_ENTRY
+{
+ /** Command ID. */
+ RTFTPSERVERCMD enmCmd;
+ /** Command represented as ASCII string. */
+ char szCmd[RTFTPSERVER_MAX_CMD_LEN];
+ /** Whether the commands needs a logged in (valid) user. */
+ bool fNeedsUser;
+ /** Function pointer invoked to handle the command. */
+ PFNRTFTPSERVERCMD pfnCmd;
+} RTFTPSERVERCMD_ENTRY;
+/** Pointer to a command entry. */
+typedef RTFTPSERVERCMD_ENTRY *PRTFTPSERVERCMD_ENTRY;
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Table of handled commands.
+ */
+static const RTFTPSERVERCMD_ENTRY g_aCmdMap[] =
+{
+ { RTFTPSERVERCMD_ABOR, "ABOR", true, rtFtpServerHandleABOR },
+ { RTFTPSERVERCMD_CDUP, "CDUP", true, rtFtpServerHandleCDUP },
+ { RTFTPSERVERCMD_CWD, "CWD", true, rtFtpServerHandleCWD },
+ { RTFTPSERVERCMD_FEAT, "FEAT", false, rtFtpServerHandleFEAT },
+ { RTFTPSERVERCMD_LIST, "LIST", true, rtFtpServerHandleLIST },
+ { RTFTPSERVERCMD_MODE, "MODE", true, rtFtpServerHandleMODE },
+ { RTFTPSERVERCMD_NOOP, "NOOP", true, rtFtpServerHandleNOOP },
+ { RTFTPSERVERCMD_PASS, "PASS", false, rtFtpServerHandlePASS },
+ { RTFTPSERVERCMD_PORT, "PORT", true, rtFtpServerHandlePORT },
+ { RTFTPSERVERCMD_PWD, "PWD", true, rtFtpServerHandlePWD },
+ { RTFTPSERVERCMD_OPTS, "OPTS", false, rtFtpServerHandleOPTS },
+ { RTFTPSERVERCMD_QUIT, "QUIT", false, rtFtpServerHandleQUIT },
+ { RTFTPSERVERCMD_RETR, "RETR", true, rtFtpServerHandleRETR },
+ { RTFTPSERVERCMD_SIZE, "SIZE", true, rtFtpServerHandleSIZE },
+ { RTFTPSERVERCMD_STAT, "STAT", true, rtFtpServerHandleSTAT },
+ { RTFTPSERVERCMD_STRU, "STRU", true, rtFtpServerHandleSTRU },
+ { RTFTPSERVERCMD_SYST, "SYST", false, rtFtpServerHandleSYST },
+ { RTFTPSERVERCMD_TYPE, "TYPE", true, rtFtpServerHandleTYPE },
+ { RTFTPSERVERCMD_USER, "USER", false, rtFtpServerHandleUSER },
+ { RTFTPSERVERCMD_END, "", false, NULL }
+};
+
+/** RFC-1123 month of the year names. */
+static const char * const g_apszMonths[1+12] =
+{
+ "000", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/** Feature string which represents all commands we support in addition to RFC 959 (see RFC 2398).
+ * Must match the command table above.
+ *
+ * Don't forget the beginning space (" ") at each feature. */
+#define RTFTPSERVER_FEATURES_STRING \
+ " SIZE\r\n" \
+ " UTF8"
+
+/** Maximum length in characters a FTP server path can have (excluding termination). */
+#define RTFTPSERVER_MAX_PATH RTPATH_MAX
+
+
+/*********************************************************************************************************************************
+* Protocol Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Replies a (three digit) reply code back to the client.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to reply to.
+ * @param enmReply Reply code to send.
+ */
+static int rtFtpServerSendReplyRc(PRTFTPSERVERCLIENT pClient, RTFTPSERVER_REPLY enmReply)
+{
+ /* Note: If we don't supply any additional text, make sure to include an empty stub, as
+ * some clients expect this as part of their parsing code. */
+ char szReply[32];
+ RTStrPrintf2(szReply, sizeof(szReply), "%RU32 -\r\n", enmReply);
+
+ LogFlowFunc(("Sending reply code %RU32\n", enmReply));
+
+ return RTTcpWrite(pClient->hSocket, szReply, strlen(szReply));
+}
+
+/**
+ * Replies a (three digit) reply code with a custom message back to the client.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to reply to.
+ * @param enmReply Reply code to send.
+ * @param pszFormat Format string of message to send with the reply code.
+ */
+static int rtFtpServerSendReplyRcEx(PRTFTPSERVERCLIENT pClient, RTFTPSERVER_REPLY enmReply,
+ const char *pszFormat, ...)
+{
+ char *pszMsg = NULL;
+
+ va_list args;
+ va_start(args, pszFormat);
+ char *pszFmt = NULL;
+ const int cch = RTStrAPrintfV(&pszFmt, pszFormat, args);
+ va_end(args);
+ AssertReturn(cch > 0, VERR_NO_MEMORY);
+
+ int rc = RTStrAPrintf(&pszMsg, "%RU32 -", enmReply);
+ AssertRCReturn(rc, rc);
+
+ /** @todo Support multi-line replies (see 4.2ff). */
+
+ if (pszFmt)
+ {
+ rc = RTStrAAppend(&pszMsg, " ");
+ AssertRCReturn(rc, rc);
+
+ rc = RTStrAAppend(&pszMsg, pszFmt);
+ AssertRCReturn(rc, rc);
+ }
+
+
+ rc = RTStrAAppend(&pszMsg, "\r\n");
+ AssertRCReturn(rc, rc);
+
+ RTStrFree(pszFmt);
+
+ rc = RTTcpWrite(pClient->hSocket, pszMsg, strlen(pszMsg));
+
+ RTStrFree(pszMsg);
+
+ return rc;
+}
+
+/**
+ * Replies a string back to the client.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to reply to.
+ * @param pszFormat Format to reply.
+ * @param ... Format arguments.
+ */
+static int rtFtpServerSendReplyStr(PRTFTPSERVERCLIENT pClient, const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ char *psz = NULL;
+ const int cch = RTStrAPrintfV(&psz, pszFormat, args);
+ va_end(args);
+ AssertReturn(cch > 0, VERR_NO_MEMORY);
+
+ int rc = RTStrAAppend(&psz, "\r\n");
+ AssertRCReturn(rc, rc);
+
+ LogFlowFunc(("Sending reply '%s'\n", psz));
+
+ rc = RTTcpWrite(pClient->hSocket, psz, strlen(psz));
+
+ RTStrFree(psz);
+
+ return rc;
+}
+
+/**
+ * Validates if a given absolute path is valid or not.
+ *
+ * @returns \c true if path is valid, or \c false if not.
+ * @param pszPath Path to check.
+ * @param fIsAbsolute Whether the path to check is an absolute path or not.
+ */
+static bool rtFtpServerPathIsValid(const char *pszPath, bool fIsAbsolute)
+{
+ if (!pszPath)
+ return false;
+
+ bool fIsValid = strlen(pszPath)
+ && RTStrIsValidEncoding(pszPath)
+ && RTStrStr(pszPath, "..") == NULL; /** @todo Very crude for now -- improve this. */
+ if ( fIsValid
+ && fIsAbsolute)
+ {
+ RTFSOBJINFO objInfo;
+ int rc2 = RTPathQueryInfo(pszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc2))
+ {
+ fIsValid = RTFS_IS_DIRECTORY(objInfo.Attr.fMode)
+ || RTFS_IS_FILE(objInfo.Attr.fMode);
+
+ /* No symlinks and other stuff not allowed. */
+ }
+ else
+ fIsValid = false;
+ }
+
+ LogFlowFunc(("pszPath=%s -> %RTbool\n", pszPath, fIsValid));
+ return fIsValid;
+}
+
+/**
+ * Sets the current working directory for a client.
+ *
+ * @returns VBox status code.
+ * @param pState Client state to set current working directory for.
+ * @param pszPath Working directory to set.
+ */
+static int rtFtpSetCWD(PRTFTPSERVERCLIENTSTATE pState, const char *pszPath)
+{
+ RTStrFree(pState->pszCWD);
+
+ if (!rtFtpServerPathIsValid(pszPath, false /* fIsAbsolute */))
+ return VERR_INVALID_PARAMETER;
+
+ pState->pszCWD = RTStrDup(pszPath);
+
+ LogFlowFunc(("Current CWD is now '%s'\n", pState->pszCWD));
+
+ int rc = pState->pszCWD ? VINF_SUCCESS : VERR_NO_MEMORY;
+ AssertRC(rc);
+ return rc;
+}
+
+/**
+ * Looks up an user account.
+ *
+ * @returns VBox status code, or VERR_NOT_FOUND if user has not been found.
+ * @param pClient Client to look up user for.
+ * @param pszUser User name to look up.
+ */
+static int rtFtpServerLookupUser(PRTFTPSERVERCLIENT pClient, const char *pszUser)
+{
+ RTFTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnUserConnect, pszUser);
+}
+
+/**
+ * Handles the actual client authentication.
+ *
+ * @returns VBox status code, or VERR_ACCESS_DENIED if authentication failed.
+ * @param pClient Client to authenticate.
+ * @param pszUser User name to authenticate with.
+ * @param pszPassword Password to authenticate with.
+ */
+static int rtFtpServerAuthenticate(PRTFTPSERVERCLIENT pClient, const char *pszUser, const char *pszPassword)
+{
+ RTFTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnUserAuthenticate, pszUser, pszPassword);
+}
+
+/**
+ * Converts a RTFSOBJINFO struct to a string.
+ *
+ * @returns VBox status code.
+ * @param pObjInfo RTFSOBJINFO object to convert.
+ * @param pszFsObjInfo Where to store the output string.
+ * @param cbFsObjInfo Size of the output string in bytes.
+ */
+static int rtFtpServerFsObjInfoToStr(PRTFSOBJINFO pObjInfo, char *pszFsObjInfo, size_t cbFsObjInfo)
+{
+ RTFMODE fMode = pObjInfo->Attr.fMode;
+ char chFileType;
+ switch (fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_FIFO: chFileType = 'f'; break;
+ case RTFS_TYPE_DEV_CHAR: chFileType = 'c'; break;
+ case RTFS_TYPE_DIRECTORY: chFileType = 'd'; break;
+ case RTFS_TYPE_DEV_BLOCK: chFileType = 'b'; break;
+ case RTFS_TYPE_FILE: chFileType = '-'; break;
+ case RTFS_TYPE_SYMLINK: chFileType = 'l'; break;
+ case RTFS_TYPE_SOCKET: chFileType = 's'; break;
+ case RTFS_TYPE_WHITEOUT: chFileType = 'w'; break;
+ default: chFileType = '?'; break;
+ }
+
+ char szTimeBirth[RTTIME_STR_LEN];
+ char szTimeChange[RTTIME_STR_LEN];
+ char szTimeModification[RTTIME_STR_LEN];
+ char szTimeAccess[RTTIME_STR_LEN];
+
+#define INFO_TO_STR(a_Format, ...) \
+ do \
+ { \
+ const ssize_t cchSize = RTStrPrintf2(szTemp, sizeof(szTemp), a_Format, __VA_ARGS__); \
+ AssertReturn(cchSize > 0, VERR_BUFFER_OVERFLOW); \
+ const int rc2 = RTStrCat(pszFsObjInfo, cbFsObjInfo, szTemp); \
+ AssertRCReturn(rc2, rc2); \
+ } while (0);
+
+ char szTemp[128];
+
+ INFO_TO_STR("%c", chFileType);
+ INFO_TO_STR("%c%c%c",
+ fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
+ fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
+ fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
+ INFO_TO_STR("%c%c%c",
+ fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
+ fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
+ fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
+ INFO_TO_STR("%c%c%c",
+ fMode & RTFS_UNIX_IROTH ? 'r' : '-',
+ fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
+ fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
+
+ INFO_TO_STR( " %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
+ fMode & RTFS_DOS_READONLY ? 'R' : '-',
+ fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
+ fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
+ fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
+ fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
+ fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
+ fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
+ fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
+ fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
+ fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
+ fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
+ fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
+ fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
+ fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-');
+
+ INFO_TO_STR( " %d %4d %4d %10lld %10lld",
+ pObjInfo->Attr.u.Unix.cHardlinks,
+ pObjInfo->Attr.u.Unix.uid,
+ pObjInfo->Attr.u.Unix.gid,
+ pObjInfo->cbObject,
+ pObjInfo->cbAllocated);
+
+ INFO_TO_STR( " %s %s %s %s",
+ RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth)),
+ RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange)),
+ RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification)),
+ RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess)) );
+
+#undef INFO_TO_STR
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Parses a string which consists of an IPv4 (ww,xx,yy,zz) and a port number (hi,lo), all separated by comma delimiters.
+ * See RFC 959, 4.1.2.
+ *
+ * @returns VBox status code.
+ * @param pszStr String to parse.
+ * @param pAddr Where to store the IPv4 address on success.
+ * @param puPort Where to store the port number on success.
+ */
+static int rtFtpParseHostAndPort(const char *pszStr, PRTNETADDRIPV4 pAddr, uint16_t *puPort)
+{
+ AssertPtrReturn(pszStr, VERR_INVALID_POINTER);
+ AssertPtrReturn(pAddr, VERR_INVALID_POINTER);
+ AssertPtrReturn(puPort, VERR_INVALID_POINTER);
+
+ char *pszNext;
+ int rc;
+
+ /* Parse IP (v4). */
+ /** @todo I don't think IPv6 ever will be a thing here, or will it? */
+ rc = RTStrToUInt8Ex(pszStr, &pszNext, 10, &pAddr->au8[0]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+ if (*pszNext++ != ',')
+ return VERR_INVALID_PARAMETER;
+
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[1]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+ if (*pszNext++ != ',')
+ return VERR_INVALID_PARAMETER;
+
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[2]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+ if (*pszNext++ != ',')
+ return VERR_INVALID_PARAMETER;
+
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[3]);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+ if (*pszNext++ != ',')
+ return VERR_INVALID_PARAMETER;
+
+ /* Parse port. */
+ uint8_t uPortHi;
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &uPortHi);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+ if (*pszNext++ != ',')
+ return VERR_INVALID_PARAMETER;
+ uint8_t uPortLo;
+ rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &uPortLo);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES && rc != VWRN_TRAILING_CHARS)
+ return VERR_INVALID_PARAMETER;
+
+ *puPort = RT_MAKE_U16(uPortLo, uPortHi);
+
+ return rc;
+}
+
+/**
+ * Duplicates a command argument vector.
+ *
+ * @returns Duplicated argument vector or NULL if failed or no arguments given. Needs to be free'd with rtFtpCmdArgsFree().
+ * @param cArgs Number of arguments in argument vector.
+ * @param apszArgs Pointer to argument vector to duplicate.
+ */
+static char** rtFtpCmdArgsDup(uint8_t cArgs, const char * const *apszArgs)
+{
+ if (!cArgs)
+ return NULL;
+
+ char **apszArgsDup = (char **)RTMemAlloc(cArgs * sizeof(char *));
+ if (!apszArgsDup)
+ {
+ AssertFailed();
+ return NULL;
+ }
+
+ int rc2 = VINF_SUCCESS;
+
+ uint8_t i;
+ for (i = 0; i < cArgs; i++)
+ {
+ apszArgsDup[i] = RTStrDup(apszArgs[i]);
+ if (!apszArgsDup[i])
+ rc2 = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(rc2))
+ {
+ while (i--)
+ RTStrFree(apszArgsDup[i]);
+
+ RTMemFree(apszArgsDup);
+ return NULL;
+ }
+
+ return apszArgsDup;
+}
+
+/**
+ * Frees a command argument vector.
+ *
+ * @param cArgs Number of arguments in argument vector.
+ * @param papszArgs Pointer to argument vector to free.
+ */
+static void rtFtpCmdArgsFree(uint8_t cArgs, char **papszArgs)
+{
+ while (cArgs--)
+ RTStrFree(papszArgs[cArgs]);
+
+ RTMemFree(papszArgs);
+}
+
+/**
+ * Opens a data connection to the client.
+ *
+ * @returns VBox status code.
+ * @param pDataConn Data connection to open.
+ * @param pAddr Address for the data connection.
+ * @param uPort Port for the data connection.
+ */
+static int rtFtpServerDataConnOpen(PRTFTPSERVERDATACONN pDataConn, PRTNETADDRIPV4 pAddr, uint16_t uPort)
+{
+ LogFlowFuncEnter();
+
+ /** @todo Implement IPv6 handling here. */
+ char szAddress[32];
+ const ssize_t cchAdddress = RTStrPrintf2(szAddress, sizeof(szAddress), "%RU8.%RU8.%RU8.%RU8",
+ pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3]);
+ AssertReturn(cchAdddress > 0, VERR_NO_MEMORY);
+
+ int rc = VINF_SUCCESS; /* Shut up MSVC. */
+
+ /* Try a bit harder if the data connection is not ready (yet). */
+ for (int i = 0; i < 10; i++)
+ {
+ rc = RTTcpClientConnect(szAddress, uPort, &pDataConn->hSocket);
+ if (RT_SUCCESS(rc))
+ break;
+ RTThreadSleep(100);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Closes a data connection to the client.
+ *
+ * @returns VBox status code.
+ * @param pDataConn Data connection to close.
+ */
+static int rtFtpServerDataConnClose(PRTFTPSERVERDATACONN pDataConn)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pDataConn->hSocket != NIL_RTSOCKET)
+ {
+ LogFlowFuncEnter();
+
+ rtFtpServerDataConnFlush(pDataConn);
+
+ rc = RTTcpClientClose(pDataConn->hSocket);
+ pDataConn->hSocket = NIL_RTSOCKET;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Writes data to the data connection.
+ *
+ * @returns VBox status code.
+ * @param pDataConn Data connection to write to.
+ * @param pvData Data to write.
+ * @param cbData Size (in bytes) of data to write.
+ * @param pcbWritten How many bytes were written. Optional.
+ */
+static int rtFtpServerDataConnWrite(PRTFTPSERVERDATACONN pDataConn, const void *pvData, size_t cbData, size_t *pcbWritten)
+{
+ int rc = RTTcpWrite(pDataConn->hSocket, pvData, cbData);
+ if (RT_SUCCESS(rc))
+ {
+ if (pcbWritten)
+ *pcbWritten = cbData;
+ }
+
+ return rc;
+}
+
+/**
+ * Flushes a data connection.
+ *
+ * @returns VBox status code.
+ * @param pDataConn Data connection to flush.
+ */
+static int rtFtpServerDataConnFlush(PRTFTPSERVERDATACONN pDataConn)
+{
+ int rc = VINF_SUCCESS;
+
+ size_t cbUsed = RTCircBufUsed(pDataConn->pCircBuf);
+ while (cbUsed)
+ {
+ void *pvBlock;
+ size_t cbBlock;
+ RTCircBufAcquireReadBlock(pDataConn->pCircBuf, cbUsed, &pvBlock, &cbBlock);
+ if (cbBlock)
+ {
+ size_t cbWritten = 0;
+ rc = rtFtpServerDataConnWrite(pDataConn, pvBlock, cbBlock, &cbWritten);
+ if (RT_SUCCESS(rc))
+ {
+ AssertBreak(cbUsed >= cbWritten);
+ cbUsed -= cbWritten;
+ }
+
+ RTCircBufReleaseReadBlock(pDataConn->pCircBuf, cbWritten);
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Checks if flushing a data connection is necessary, and if so, flush it.
+ *
+ * @returns VBox status code.
+ * @param pDataConn Data connection to check / do flushing for.
+ */
+static int rtFtpServerDataCheckFlush(PRTFTPSERVERDATACONN pDataConn)
+{
+ int rc = VINF_SUCCESS;
+
+ size_t cbUsed = RTCircBufUsed(pDataConn->pCircBuf);
+ if (cbUsed >= _4K) /** @todo Make this more dynamic. */
+ {
+ rc = rtFtpServerDataConnFlush(pDataConn);
+ }
+
+ return rc;
+}
+
+/**
+ * Adds new data for a data connection to be sent.
+ *
+ * @returns VBox status code.
+ * @param pDataConn Data connection to add new data to.
+ * @param pvData Pointer to data to add.
+ * @param cbData Size (in bytes) of data to add.
+ */
+static int rtFtpServerDataConnAddData(PRTFTPSERVERDATACONN pDataConn, const void *pvData, size_t cbData)
+{
+ AssertReturn(cbData <= RTCircBufFree(pDataConn->pCircBuf), VERR_BUFFER_OVERFLOW);
+
+ int rc = VINF_SUCCESS;
+
+ size_t cbToWrite = cbData;
+ do
+ {
+ void *pvBlock;
+ size_t cbBlock;
+ RTCircBufAcquireWriteBlock(pDataConn->pCircBuf, cbToWrite, &pvBlock, &cbBlock);
+ if (cbBlock)
+ {
+ AssertBreak(cbData >= cbBlock);
+ memcpy(pvBlock, pvData, cbBlock);
+
+ AssertBreak(cbToWrite >= cbBlock);
+ cbToWrite -= cbBlock;
+
+ RTCircBufReleaseWriteBlock(pDataConn->pCircBuf, cbBlock);
+ }
+
+ } while (cbToWrite);
+
+ if (RT_SUCCESS(rc))
+ rc = rtFtpServerDataCheckFlush(pDataConn);
+
+ return rc;
+}
+
+/**
+ * Does a printf-style write on a data connection.
+ *
+ * @returns VBox status code.
+ * @param pDataConn Data connection to write to.
+ * @param pszFormat Format string to send. No (terminal) termination added.
+ */
+static int rtFtpServerDataConnPrintf(PRTFTPSERVERDATACONN pDataConn, const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ char *pszFmt = NULL;
+ const int cch = RTStrAPrintfV(&pszFmt, pszFormat, args);
+ va_end(args);
+ AssertReturn(cch > 0, VERR_NO_MEMORY);
+
+ char *pszMsg = NULL;
+ int rc = RTStrAAppend(&pszMsg, pszFmt);
+ AssertRCReturn(rc, rc);
+
+ RTStrFree(pszFmt);
+
+ rc = rtFtpServerDataConnAddData(pDataConn, pszMsg, strlen(pszMsg));
+
+ RTStrFree(pszMsg);
+
+ return rc;
+}
+
+/**
+ * Data connection thread for writing (sending) a file to the client.
+ *
+ * @returns VBox status code.
+ * @param ThreadSelf Thread handle. Unused at the moment.
+ * @param pvUser Pointer to user-provided data. Of type PRTFTPSERVERCLIENT.
+ */
+static DECLCALLBACK(int) rtFtpServerDataConnFileWriteThread(RTTHREAD ThreadSelf, void *pvUser)
+{
+ RT_NOREF(ThreadSelf);
+
+ PRTFTPSERVERCLIENT pClient = (PRTFTPSERVERCLIENT)pvUser;
+ AssertPtr(pClient);
+
+ PRTFTPSERVERDATACONN pDataConn = pClient->pDataConn;
+ AssertPtr(pDataConn);
+
+ LogFlowFuncEnter();
+
+ uint32_t cbBuf = _64K; /** @todo Improve this. */
+ void *pvBuf = RTMemAlloc(cbBuf);
+ if (!pvBuf)
+ return VERR_NO_MEMORY;
+
+ int rc;
+
+ /* Set start indicator. */
+ pDataConn->fStarted = true;
+
+ RTThreadUserSignal(RTThreadSelf());
+
+ AssertPtr(pDataConn->papszArgs);
+ const char *pszFile = pDataConn->papszArgs[0];
+ AssertPtr(pszFile);
+
+ void *pvHandle = NULL; /* Opaque handle known to the actual implementation. */
+
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileOpen, pszFile,
+ RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &pvHandle);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("Transfer started\n"));
+
+ do
+ {
+ size_t cbRead = 0;
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileRead, pvHandle, pvBuf, cbBuf, &cbRead);
+ if ( RT_SUCCESS(rc)
+ && cbRead)
+ {
+ rc = rtFtpServerDataConnWrite(pDataConn, pvBuf, cbRead, NULL /* pcbWritten */);
+ }
+
+ if ( !cbRead
+ || ASMAtomicReadBool(&pDataConn->fStop))
+ {
+ break;
+ }
+ }
+ while (RT_SUCCESS(rc));
+
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileClose, pvHandle);
+
+ LogFlowFunc(("Transfer done\n"));
+ }
+
+ RTMemFree(pvBuf);
+ pvBuf = NULL;
+
+ pDataConn->fStopped = true;
+ pDataConn->rc = rc;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Creates a data connection.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to create data connection for.
+ * @param ppDataConn Where to return the (allocated) data connection.
+ */
+static int rtFtpServerDataConnCreate(PRTFTPSERVERCLIENT pClient, PRTFTPSERVERDATACONN *ppDataConn)
+{
+ if (pClient->pDataConn)
+ return VERR_FTP_DATA_CONN_LIMIT_REACHED;
+
+ PRTFTPSERVERDATACONN pDataConn = (PRTFTPSERVERDATACONN)RTMemAllocZ(sizeof(RTFTPSERVERDATACONN));
+ if (!pDataConn)
+ return VERR_NO_MEMORY;
+
+ rtFtpServerDataConnReset(pDataConn);
+
+ pDataConn->pClient = pClient;
+
+ /* Use the last configured addr + port. */
+ pDataConn->Addr = pClient->DataConnAddr;
+ pDataConn->uPort = pClient->uDataConnPort;
+
+ int rc = RTCircBufCreate(&pDataConn->pCircBuf, _16K); /** @todo Some random value; improve. */
+ if (RT_SUCCESS(rc))
+ {
+ *ppDataConn = pDataConn;
+ }
+
+ LogFlowFuncLeaveRC(VINF_SUCCESS);
+ return rc;
+}
+
+/**
+ * Starts a data connection.
+ *
+ * @returns VBox status code.
+ * @param pDataConn Data connection to start.
+ * @param pfnThread Thread function for the data connection to use.
+ * @param cArgs Number of arguments.
+ * @param apszArgs Array of arguments.
+ */
+static int rtFtpServerDataConnStart(PRTFTPSERVERDATACONN pDataConn, PFNRTTHREAD pfnThread,
+ uint8_t cArgs, const char * const *apszArgs)
+{
+ AssertPtrReturn(pDataConn, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnThread, VERR_INVALID_POINTER);
+
+ AssertReturn(!pDataConn->fStarted, VERR_WRONG_ORDER);
+ AssertReturn(!pDataConn->fStop, VERR_WRONG_ORDER);
+ AssertReturn(!pDataConn->fStopped, VERR_WRONG_ORDER);
+
+ int rc = VINF_SUCCESS;
+
+ if (cArgs)
+ {
+ pDataConn->papszArgs = rtFtpCmdArgsDup(cArgs, apszArgs);
+ if (!pDataConn->papszArgs)
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pDataConn->cArgs = cArgs;
+
+ rc = rtFtpServerDataConnOpen(pDataConn, &pDataConn->Addr, pDataConn->uPort);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&pDataConn->hThread, pfnThread,
+ pDataConn->pClient, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
+ "ftpdata");
+ if (RT_SUCCESS(rc))
+ {
+ int rc2 = RTThreadUserWait(pDataConn->hThread, 30 * 1000 /* Timeout in ms */);
+ AssertRC(rc2);
+
+ if (!pDataConn->fStarted) /* Did the thread indicate that it started correctly? */
+ rc = VERR_FTP_DATA_CONN_INIT_FAILED;
+ }
+
+ if (RT_FAILURE(rc))
+ rtFtpServerDataConnClose(pDataConn);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ rtFtpCmdArgsFree(pDataConn->cArgs, pDataConn->papszArgs);
+
+ pDataConn->cArgs = 0;
+ pDataConn->papszArgs = NULL;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Stops a data connection.
+ *
+ * @returns VBox status code.
+ * @param pDataConn Data connection to stop.
+ */
+static int rtFtpServerDataConnStop(PRTFTPSERVERDATACONN pDataConn)
+{
+ if (!pDataConn)
+ return VINF_SUCCESS;
+
+ LogFlowFuncEnter();
+
+ int rc = VINF_SUCCESS;
+
+ if (pDataConn->hThread != NIL_RTTHREAD)
+ {
+ /* Set stop indicator. */
+ pDataConn->fStop = true;
+
+ int rcThread = VERR_WRONG_ORDER;
+ rc = RTThreadWait(pDataConn->hThread, 30 * 1000 /* Timeout in ms */, &rcThread);
+ }
+
+ if (RT_SUCCESS(rc))
+ rtFtpServerDataConnClose(pDataConn);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Destroys a data connection.
+ *
+ * @param pDataConn Data connection to destroy. The pointer is not valid anymore after successful return.
+ */
+static void rtFtpServerDataConnDestroy(PRTFTPSERVERDATACONN pDataConn)
+{
+ if (!pDataConn)
+ return;
+
+ LogFlowFuncEnter();
+
+ rtFtpServerDataConnClose(pDataConn);
+ rtFtpCmdArgsFree(pDataConn->cArgs, pDataConn->papszArgs);
+
+ RTCircBufDestroy(pDataConn->pCircBuf);
+
+ RTMemFree(pDataConn);
+ pDataConn = NULL;
+
+ LogFlowFuncLeave();
+ return;
+}
+
+/**
+ * Resets a data connection structure.
+ *
+ * @param pDataConn Data connection structure to reset.
+ */
+static void rtFtpServerDataConnReset(PRTFTPSERVERDATACONN pDataConn)
+{
+ LogFlowFuncEnter();
+
+ pDataConn->hSocket = NIL_RTSOCKET;
+ pDataConn->uPort = 20; /* Default port to use. */
+ pDataConn->hThread = NIL_RTTHREAD;
+ pDataConn->fStarted = false;
+ pDataConn->fStop = false;
+ pDataConn->fStopped = false;
+ pDataConn->rc = VERR_IPE_UNINITIALIZED_STATUS;
+}
+
+
+/*********************************************************************************************************************************
+* Command Protocol Handlers *
+*********************************************************************************************************************************/
+
+static DECLCALLBACK(int) rtFtpServerHandleABOR(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ RT_NOREF(cArgs, apszArgs);
+
+ int rc = rtFtpServerDataConnClose(pClient->pDataConn);
+ if (RT_SUCCESS(rc))
+ {
+ rtFtpServerDataConnDestroy(pClient->pDataConn);
+ pClient->pDataConn = NULL;
+
+ rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
+ }
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleCDUP(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ RT_NOREF(cArgs, apszArgs);
+
+ int rc;
+
+ RTFTPSERVER_HANDLE_CALLBACK(pfnOnPathUp);
+
+ if (RT_SUCCESS(rc))
+ {
+ const size_t cbPath = sizeof(char) * RTFTPSERVER_MAX_PATH;
+ char *pszPath = RTStrAlloc(cbPath);
+ if (pszPath)
+ {
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnPathGetCurrent, pszPath, cbPath);
+
+ if (RT_SUCCESS(rc))
+ rc = rtFtpSetCWD(&pClient->State, pszPath);
+
+ RTStrFree(pszPath);
+
+ rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
+ AssertRC(rc2);
+ }
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleCWD(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ if (cArgs != 1)
+ return VERR_INVALID_PARAMETER;
+
+ int rc;
+
+ const char *pszPath = apszArgs[0];
+
+ if (!rtFtpServerPathIsValid(pszPath, false /* fIsAbsolute */))
+ return VERR_INVALID_PARAMETER;
+
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnPathSetCurrent, pszPath);
+
+ if (RT_SUCCESS(rc))
+ rc = rtFtpSetCWD(&pClient->State, pszPath);
+
+ return rtFtpServerSendReplyRc(pClient,
+ RT_SUCCESS(rc)
+ ? RTFTPSERVER_REPLY_OKAY : RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleFEAT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ RT_NOREF(cArgs, apszArgs);
+
+ int rc = rtFtpServerSendReplyStr(pClient, "211-BEGIN Features:");
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtFtpServerSendReplyStr(pClient, RTFTPSERVER_FEATURES_STRING);
+ if (RT_SUCCESS(rc))
+ rc = rtFtpServerSendReplyStr(pClient, "211 END Features");
+ }
+
+ return rc;
+}
+
+/**
+ * Formats the given user ID according to the specified options.
+ *
+ * @returns pszDst
+ * @param uid The UID to format.
+ * @param pszOwner The owner returned by the FS.
+ * @param pszDst The output buffer.
+ * @param cbDst The output buffer size.
+ */
+static const char *rtFtpServerDecimalFormatOwner(RTUID uid, const char *pszOwner, char *pszDst, size_t cbDst)
+{
+ if (pszOwner)
+ {
+ RTStrCopy(pszDst, cbDst, pszOwner);
+ return pszDst;
+ }
+ if (uid == NIL_RTUID)
+ return "<Nil>";
+
+ RTStrFormatU64(pszDst, cbDst, uid, 10, 0, 0, 0);
+ return pszDst;
+}
+
+/**
+ * Formats the given group ID according to the specified options.
+ *
+ * @returns pszDst
+ * @param gid The GID to format.
+ * @param pszGroup The group returned by the FS.
+ * @param pszDst The output buffer.
+ * @param cbDst The output buffer size.
+ */
+static const char *rtFtpServerDecimalFormatGroup(RTGID gid, const char *pszGroup, char *pszDst, size_t cbDst)
+{
+ if (pszGroup)
+ {
+ RTStrCopy(pszDst, cbDst, pszGroup);
+ return pszDst;
+ }
+ if (gid == NIL_RTGID)
+ return "<Nil>";
+
+ RTStrFormatU64(pszDst, cbDst, gid, 10, 0, 0, 0);
+ return pszDst;
+}
+
+/**
+ * Format file size.
+ */
+static const char *rtFtpServerFormatSize(uint64_t cb, char *pszDst, size_t cbDst)
+{
+ RTStrFormatU64(pszDst, cbDst, cb, 10, 0, 0, 0);
+ return pszDst;
+}
+
+/**
+ * Formats the given timestamp according to (non-standardized) FTP LIST command.
+ *
+ * @returns pszDst
+ * @param pTimestamp The timestamp to format.
+ * @param pszDst The output buffer.
+ * @param cbDst The output buffer size.
+ */
+static const char *rtFtpServerFormatTimestamp(PCRTTIMESPEC pTimestamp, char *pszDst, size_t cbDst)
+{
+ RTTIME Time;
+ RTTimeExplode(&Time, pTimestamp);
+
+ /* Calc the UTC offset part. */
+ int32_t offUtc = Time.offUTC;
+ Assert(offUtc <= 840 && offUtc >= -840);
+ char chSign;
+ if (offUtc >= 0)
+ chSign = '+';
+ else
+ {
+ chSign = '-';
+ offUtc = -offUtc;
+ }
+ uint32_t offUtcHour = (uint32_t)offUtc / 60;
+ uint32_t offUtcMinute = (uint32_t)offUtc % 60;
+
+ /** @todo Cache this. */
+ RTTIMESPEC TimeSpecNow;
+ RTTimeNow(&TimeSpecNow);
+ RTTIME TimeNow;
+ RTTimeExplode(&TimeNow, &TimeSpecNow);
+
+ /* Only include the year if it's not the same year as today. */
+ if (TimeNow.i32Year != Time.i32Year)
+ {
+ RTStrPrintf(pszDst, cbDst, "%s %02RU8 %5RU32",
+ g_apszMonths[Time.u8Month], Time.u8MonthDay, Time.i32Year);
+ }
+ else /* ... otherwise include the (rough) time (as GMT). */
+ {
+ RTStrPrintf(pszDst, cbDst, "%s %02RU8 %02RU32:%02RU32",
+ g_apszMonths[Time.u8Month], Time.u8MonthDay, offUtcHour, offUtcMinute);
+ }
+
+ return pszDst;
+}
+
+/**
+ * Format name, i.e. escape, hide, quote stuff.
+ */
+static const char *rtFtpServerFormatName(const char *pszName, char *pszDst, size_t cbDst)
+{
+ /** @todo implement name formatting. */
+ RT_NOREF(pszDst, cbDst);
+ return pszName;
+}
+
+/**
+ * Figures out the length for a 32-bit number when formatted as decimal.
+ * @returns Number of digits.
+ * @param uValue The number.
+ */
+DECLINLINE(size_t) rtFtpServerDecimalFormatLengthU32(uint32_t uValue)
+{
+ if (uValue < 10)
+ return 1;
+ if (uValue < 100)
+ return 2;
+ if (uValue < 1000)
+ return 3;
+ if (uValue < 10000)
+ return 4;
+ if (uValue < 100000)
+ return 5;
+ if (uValue < 1000000)
+ return 6;
+ if (uValue < 10000000)
+ return 7;
+ if (uValue < 100000000)
+ return 8;
+ if (uValue < 1000000000)
+ return 9;
+ return 10;
+}
+
+/**
+ * Allocates a new directory collection.
+ *
+ * @returns The collection allocated.
+ */
+static PRTFTPDIRCOLLECTION rtFtpServerDataConnDirCollAlloc(void)
+{
+ return (PRTFTPDIRCOLLECTION)RTMemAllocZ(sizeof(RTFTPDIRCOLLECTION));
+}
+
+/**
+ * Frees a directory collection and its entries.
+ *
+ * @param pCollection The collection to free.
+ */
+static void rtFtpServerDataConnDirCollFree(PRTFTPDIRCOLLECTION pCollection)
+{
+ PPRTFTPDIRENTRY papEntries = pCollection->papEntries;
+ size_t j = pCollection->cEntries;
+ while (j-- > 0)
+ {
+ RTMemFree(papEntries[j]);
+ papEntries[j] = NULL;
+ }
+ RTMemFree(papEntries);
+ pCollection->papEntries = NULL;
+ pCollection->cEntries = 0;
+ pCollection->cEntriesAllocated = 0;
+ RTMemFree(pCollection);
+}
+
+/**
+ * Adds one entry to a collection.
+ *
+ * @returns VBox status code.
+ * @param pCollection The collection to add entry to.
+ * @param pszEntry The entry name.
+ * @param pInfo The entry info.
+ * @param pszOwner The owner name if available, otherwise NULL.
+ * @param pszGroup The group anme if available, otherwise NULL.
+ * @param pszTarget The symbolic link target if applicable and
+ * available, otherwise NULL.
+ */
+static int rtFtpServerDataConnDirCollAddEntry(PRTFTPDIRCOLLECTION pCollection, const char *pszEntry, PRTFSOBJINFO pInfo,
+ const char *pszOwner, const char *pszGroup, const char *pszTarget)
+{
+ /* Filter out entries we don't want to report to the client, even if they were reported by the actual implementation. */
+ if ( !RTStrCmp(pszEntry, ".")
+ || !RTStrCmp(pszEntry, ".."))
+ {
+ return VINF_SUCCESS;
+ }
+
+ /* Anything else besides files and directores is not allowed; just don't show them at all for the moment. */
+ switch (pInfo->Attr.fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_DIRECTORY:
+ RT_FALL_THROUGH();
+ case RTFS_TYPE_FILE:
+ break;
+
+ default:
+ return VINF_SUCCESS;
+ }
+
+ /* Make sure there is space in the collection for the new entry. */
+ if (pCollection->cEntries >= pCollection->cEntriesAllocated)
+ {
+ size_t cNew = pCollection->cEntriesAllocated ? pCollection->cEntriesAllocated * 2 : 16;
+ void *pvNew = RTMemRealloc(pCollection->papEntries, cNew * sizeof(pCollection->papEntries[0]));
+ if (!pvNew)
+ return VERR_NO_MEMORY;
+ pCollection->papEntries = (PPRTFTPDIRENTRY)pvNew;
+ pCollection->cEntriesAllocated = cNew;
+ }
+
+ /* Create and insert a new entry. */
+ size_t const cchEntry = strlen(pszEntry);
+ size_t const cbOwner = pszOwner ? strlen(pszOwner) + 1 : 0;
+ size_t const cbGroup = pszGroup ? strlen(pszGroup) + 1 : 0;
+ size_t const cbTarget = pszTarget ? strlen(pszTarget) + 1 : 0;
+ size_t const cbEntry = RT_UOFFSETOF_DYN(RTFTPDIRENTRY, szName[cchEntry + 1 + cbOwner + cbGroup + cbTarget]);
+ PRTFTPDIRENTRY pEntry = (PRTFTPDIRENTRY)RTMemAlloc(cbEntry);
+ if (pEntry)
+ {
+ pEntry->Info = *pInfo;
+ pEntry->pszTarget = NULL; /** @todo symbolic links. */
+ pEntry->pszOwner = NULL;
+ pEntry->pszGroup = NULL;
+ pEntry->cchName = cchEntry;
+ memcpy(pEntry->szName, pszEntry, cchEntry);
+ pEntry->szName[cchEntry] = '\0';
+
+ char *psz = &pEntry->szName[cchEntry + 1];
+ if (pszTarget)
+ {
+ pEntry->pszTarget = psz;
+ memcpy(psz, pszTarget, cbTarget);
+ psz += cbTarget;
+ }
+ if (pszOwner)
+ {
+ pEntry->pszOwner = psz;
+ memcpy(psz, pszOwner, cbOwner);
+ psz += cbOwner;
+ }
+ if (pszGroup)
+ {
+ pEntry->pszGroup = psz;
+ memcpy(psz, pszGroup, cbGroup);
+ }
+
+ pCollection->papEntries[pCollection->cEntries++] = pEntry;
+ pCollection->cbTotalAllocated += pEntry->Info.cbAllocated;
+ pCollection->cbTotalFiles += pEntry->Info.cbObject;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+/** @callback_method_impl{FNRTSORTCMP, Name} */
+static DECLCALLBACK(int) rtFtpServerCollEntryCmpName(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ RT_NOREF(pvUser);
+ PRTFTPDIRENTRY pEntry1 = (PRTFTPDIRENTRY)pvElement1;
+ PRTFTPDIRENTRY pEntry2 = (PRTFTPDIRENTRY)pvElement2;
+ return RTStrCmp(pEntry1->szName, pEntry2->szName);
+}
+
+/** @callback_method_impl{FNRTSORTCMP, Dirs first + Name} */
+static DECLCALLBACK(int) rtFtpServerCollEntryCmpDirFirstName(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ RT_NOREF(pvUser);
+ PRTFTPDIRENTRY pEntry1 = (PRTFTPDIRENTRY)pvElement1;
+ PRTFTPDIRENTRY pEntry2 = (PRTFTPDIRENTRY)pvElement2;
+ int iDiff = !RTFS_IS_DIRECTORY(pEntry1->Info.Attr.fMode) - !RTFS_IS_DIRECTORY(pEntry2->Info.Attr.fMode);
+ if (!iDiff)
+ iDiff = rtFtpServerCollEntryCmpName(pEntry1, pEntry2, pvUser);
+ return iDiff;
+}
+
+/**
+ * Sorts a given directory collection according to the FTP server's LIST style.
+ *
+ * @param pCollection Collection to sort.
+ */
+static void rtFtpServerCollSort(PRTFTPDIRCOLLECTION pCollection)
+{
+ PFNRTSORTCMP pfnCmp = rtFtpServerCollEntryCmpDirFirstName;
+ if (pfnCmp)
+ RTSortApvShell((void **)pCollection->papEntries, pCollection->cEntries, pfnCmp, NULL);
+}
+
+/**
+ * Writes a directory collection to a specific data connection.
+ *
+ * @returns VBox status code.
+ * @param pDataConn Data connection to write directory collection to.
+ * @param pCollection Collection to write.
+ * @param pszTmp Temporary buffer used for writing.
+ * @param cbTmp Size (in bytes) of temporary buffer used for writing.
+ */
+static int rtFtpServerDataConnDirCollWrite(PRTFTPSERVERDATACONN pDataConn, PRTFTPDIRCOLLECTION pCollection,
+ char *pszTmp, size_t cbTmp)
+{
+ size_t cchSizeCol = 4;
+ size_t cchLinkCol = 1;
+ size_t cchUidCol = 1;
+ size_t cchGidCol = 1;
+
+ size_t i = pCollection->cEntries;
+ while (i-- > 0)
+ {
+ PRTFTPDIRENTRY pEntry = pCollection->papEntries[i];
+
+ rtFtpServerFormatSize(pEntry->Info.cbObject, pszTmp, cbTmp);
+ size_t cchTmp = strlen(pszTmp);
+ if (cchTmp > cchSizeCol)
+ cchSizeCol = cchTmp;
+
+ cchTmp = rtFtpServerDecimalFormatLengthU32(pEntry->Info.Attr.u.Unix.cHardlinks) + 1;
+ if (cchTmp > cchLinkCol)
+ cchLinkCol = cchTmp;
+
+ rtFtpServerDecimalFormatOwner(pEntry->Info.Attr.u.Unix.uid, pEntry->pszOwner, pszTmp, cbTmp);
+ cchTmp = strlen(pszTmp);
+ if (cchTmp > cchUidCol)
+ cchUidCol = cchTmp;
+
+ rtFtpServerDecimalFormatGroup(pEntry->Info.Attr.u.Unix.gid, pEntry->pszGroup, pszTmp, cbTmp);
+ cchTmp = strlen(pszTmp);
+ if (cchTmp > cchGidCol)
+ cchGidCol = cchTmp;
+ }
+
+ size_t offTime = RT_UOFFSETOF(RTFTPDIRENTRY, Info.ModificationTime);
+
+ /*
+ * Display the entries.
+ */
+ for (i = 0; i < pCollection->cEntries; i++)
+ {
+ PRTFTPDIRENTRY pEntry = pCollection->papEntries[i];
+
+ RTFMODE fMode = pEntry->Info.Attr.fMode;
+ switch (fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_FIFO: rtFtpServerDataConnPrintf(pDataConn, "f"); break;
+ case RTFS_TYPE_DEV_CHAR: rtFtpServerDataConnPrintf(pDataConn, "c"); break;
+ case RTFS_TYPE_DIRECTORY: rtFtpServerDataConnPrintf(pDataConn, "d"); break;
+ case RTFS_TYPE_DEV_BLOCK: rtFtpServerDataConnPrintf(pDataConn, "b"); break;
+ case RTFS_TYPE_FILE: rtFtpServerDataConnPrintf(pDataConn, "-"); break;
+ case RTFS_TYPE_SYMLINK: rtFtpServerDataConnPrintf(pDataConn, "l"); break;
+ case RTFS_TYPE_SOCKET: rtFtpServerDataConnPrintf(pDataConn, "s"); break;
+ case RTFS_TYPE_WHITEOUT: rtFtpServerDataConnPrintf(pDataConn, "w"); break;
+ default: rtFtpServerDataConnPrintf(pDataConn, "?"); AssertFailed(); break;
+ }
+
+ rtFtpServerDataConnPrintf(pDataConn, "%c%c%c",
+ fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
+ fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
+ fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
+ rtFtpServerDataConnPrintf(pDataConn, "%c%c%c",
+ fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
+ fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
+ fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
+ rtFtpServerDataConnPrintf(pDataConn, "%c%c%c",
+ fMode & RTFS_UNIX_IROTH ? 'r' : '-',
+ fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
+ fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
+
+ rtFtpServerDataConnPrintf(pDataConn, " %*u",
+ cchLinkCol, pEntry->Info.Attr.u.Unix.cHardlinks);
+
+ if (cchUidCol)
+ rtFtpServerDataConnPrintf(pDataConn, " %*s", cchUidCol,
+ rtFtpServerDecimalFormatOwner(pEntry->Info.Attr.u.Unix.uid, pEntry->pszOwner, pszTmp, cbTmp));
+ if (cchGidCol)
+ rtFtpServerDataConnPrintf(pDataConn, " %*s", cchGidCol,
+ rtFtpServerDecimalFormatGroup(pEntry->Info.Attr.u.Unix.gid, pEntry->pszGroup, pszTmp, cbTmp));
+
+ rtFtpServerDataConnPrintf(pDataConn, "%*s", cchSizeCol, rtFtpServerFormatSize(pEntry->Info.cbObject, pszTmp, cbTmp));
+
+ PCRTTIMESPEC pTime = (PCRTTIMESPEC)((uintptr_t)pEntry + offTime);
+ rtFtpServerDataConnPrintf(pDataConn," %s", rtFtpServerFormatTimestamp(pTime, pszTmp, cbTmp));
+
+ rtFtpServerDataConnPrintf(pDataConn," %s\r\n", rtFtpServerFormatName(pEntry->szName, pszTmp, cbTmp));
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Thread for handling the LIST command's output in a separate data connection.
+ *
+ * @returns VBox status code.
+ * @param ThreadSelf Thread handle. Unused.
+ * @param pvUser User-provided arguments. Of type PRTFTPSERVERCLIENT.
+ */
+static DECLCALLBACK(int) rtFtpServerDataConnListThread(RTTHREAD ThreadSelf, void *pvUser)
+{
+ RT_NOREF(ThreadSelf);
+
+ PRTFTPSERVERCLIENT pClient = (PRTFTPSERVERCLIENT)pvUser;
+ AssertPtr(pClient);
+
+ PRTFTPSERVERDATACONN pDataConn = pClient->pDataConn;
+ AssertPtr(pDataConn);
+
+ LogFlowFuncEnter();
+
+ int rc;
+
+ char szTmp[RTPATH_MAX * 2];
+ PRTFTPDIRCOLLECTION pColl = rtFtpServerDataConnDirCollAlloc();
+ AssertPtrReturn(pColl, VERR_NO_MEMORY);
+
+ /* Set start indicator. */
+ pDataConn->fStarted = true;
+
+ RTThreadUserSignal(RTThreadSelf());
+
+ /* The first argument might indicate a directory to list.
+ * If no argument is given, the implementation must use the last directory set. */
+ char *pszPath = RTStrDup( pDataConn->cArgs == 1
+ ? pDataConn->papszArgs[0] : pDataConn->pClient->State.pszCWD); /** @todo Needs locking. */
+ AssertPtrReturn(pszPath, VERR_NO_MEMORY);
+ /* The paths already have been validated in the actual command handlers. */
+
+ void *pvHandle = NULL; /* Shut up MSVC. */
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnDirOpen, pszPath, &pvHandle);
+
+ for (;;)
+ {
+ RTFSOBJINFO objInfo;
+ RT_ZERO(objInfo);
+
+ char *pszEntry = NULL;
+ char *pszOwner = NULL;
+ char *pszGroup = NULL;
+ char *pszTarget = NULL;
+
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnDirRead, pvHandle, &pszEntry,
+ &objInfo, &pszOwner, &pszGroup, &pszTarget);
+ if (RT_SUCCESS(rc))
+ {
+ int rc2 = rtFtpServerDataConnDirCollAddEntry(pColl, pszEntry,
+ &objInfo, pszOwner, pszGroup, pszTarget);
+
+ RTStrFree(pszEntry);
+ pszEntry = NULL;
+
+ RTStrFree(pszOwner);
+ pszOwner = NULL;
+
+ RTStrFree(pszGroup);
+ pszGroup = NULL;
+
+ RTStrFree(pszTarget);
+ pszTarget = NULL;
+
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ else
+ {
+ if (rc == VERR_NO_MORE_FILES)
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+
+ if (ASMAtomicReadBool(&pDataConn->fStop))
+ break;
+ }
+
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnDirClose, pvHandle);
+ pvHandle = NULL;
+
+ rtFtpServerCollSort(pColl);
+
+ if (RT_SUCCESS(rc))
+ {
+ int rc2 = rtFtpServerDataConnDirCollWrite(pDataConn, pColl, szTmp, sizeof(szTmp));
+ AssertRC(rc2);
+ }
+
+ rtFtpServerDataConnDirCollFree(pColl);
+
+ RTStrFree(pszPath);
+
+ pDataConn->fStopped = true;
+ pDataConn->rc = rc;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleLIST(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ /* If no argument is given, use the server's CWD as the path. */
+ const char *pszPath = cArgs ? apszArgs[0] : pClient->State.pszCWD;
+ AssertPtr(pszPath);
+
+ int rc = VINF_SUCCESS;
+
+ if (!rtFtpServerPathIsValid(pszPath, false /* fIsAbsolute */))
+ {
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
+ AssertRC(rc2);
+ }
+ else
+ {
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileStat, pszPath, NULL /* PRTFSOBJINFO */);
+
+ if (RT_SUCCESS(rc))
+ {
+ if (pClient->pDataConn == NULL)
+ {
+ rc = rtFtpServerDataConnCreate(pClient, &pClient->pDataConn);
+ if (RT_SUCCESS(rc))
+ rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnListThread, cArgs, apszArgs);
+
+ int rc2 = rtFtpServerSendReplyRc( pClient, RT_SUCCESS(rc)
+ ? RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN
+ : RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN);
+ AssertRC(rc2);
+ }
+ else
+ {
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN);
+ AssertRC(rc2);
+ }
+ }
+ else
+ {
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
+ AssertRC(rc2);
+ }
+ }
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleMODE(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ RT_NOREF(pClient, cArgs, apszArgs);
+
+ /** @todo Anything to do here? */
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleNOOP(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ RT_NOREF(cArgs, apszArgs);
+
+ /* Save timestamp of last command sent. */
+ pClient->State.tsLastCmdMs = RTTimeMilliTS();
+
+ return rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
+}
+
+static DECLCALLBACK(int) rtFtpServerHandlePASS(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ if (cArgs != 1)
+ return rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
+
+ const char *pszPassword = apszArgs[0];
+ AssertPtrReturn(pszPassword, VERR_INVALID_PARAMETER);
+
+ int rc = rtFtpServerAuthenticate(pClient, pClient->State.pszUser, pszPassword);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_LOGGED_IN_PROCEED);
+ }
+ else
+ {
+ pClient->State.cFailedLoginAttempts++;
+
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_NOT_LOGGED_IN);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandlePORT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ if (cArgs != 1)
+ return rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS);
+
+ RTFTPSERVER_REPLY rcClient;
+
+ int rc = rtFtpParseHostAndPort(apszArgs[0], &pClient->DataConnAddr, &pClient->uDataConnPort);
+ if (RT_SUCCESS(rc))
+ rcClient = RTFTPSERVER_REPLY_OKAY;
+ else
+ rcClient = RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS;
+
+ int rc2 = rtFtpServerSendReplyRc(pClient, rcClient);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandlePWD(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ RT_NOREF(cArgs, apszArgs);
+
+ int rc;
+
+ char szPWD[RTPATH_MAX];
+
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnPathGetCurrent, szPWD, sizeof(szPWD));
+
+ if (RT_SUCCESS(rc))
+ rc = rtFtpServerSendReplyRcEx(pClient, RTFTPSERVER_REPLY_PATHNAME_OK, "\"%s\"", szPWD); /* See RFC 959, APPENDIX II. */
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleOPTS(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ RT_NOREF(cArgs, apszArgs);
+
+ int rc = VINF_SUCCESS;
+
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleQUIT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ RT_NOREF(cArgs, apszArgs);
+
+ int rc = VINF_SUCCESS;
+
+ if (pClient->pDataConn)
+ {
+ rc = rtFtpServerDataConnClose(pClient->pDataConn);
+ if (RT_SUCCESS(rc))
+ {
+ rtFtpServerDataConnDestroy(pClient->pDataConn);
+ pClient->pDataConn = NULL;
+ }
+ }
+
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleRETR(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ if (cArgs != 1) /* File name needs to be present. */
+ return VERR_INVALID_PARAMETER;
+
+ int rc;
+
+ const char *pszPath = apszArgs[0];
+
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileStat, pszPath, NULL /* PRTFSOBJINFO */);
+
+ if (RT_SUCCESS(rc))
+ {
+ if (RT_SUCCESS(rc))
+ {
+ if (pClient->pDataConn == NULL)
+ {
+ rc = rtFtpServerDataConnCreate(pClient, &pClient->pDataConn);
+ if (RT_SUCCESS(rc))
+ rc = rtFtpServerDataConnStart(pClient->pDataConn, rtFtpServerDataConnFileWriteThread, cArgs, apszArgs);
+
+ int rc2 = rtFtpServerSendReplyRc( pClient, RT_SUCCESS(rc)
+ ? RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN
+ : RTFTPSERVER_REPLY_CANT_OPEN_DATA_CONN);
+ AssertRC(rc2);
+ }
+ else
+ {
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_DATACONN_ALREADY_OPEN);
+ AssertRC(rc2);
+ }
+ }
+ else
+ {
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
+ AssertRC(rc2);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_REQ_ACTION_NOT_TAKEN);
+ AssertRC(rc2);
+ }
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleSIZE(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ if (cArgs != 1)
+ return VERR_INVALID_PARAMETER;
+
+ int rc;
+
+ const char *pszPath = apszArgs[0];
+ uint64_t uSize = 0;
+
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileGetSize, pszPath, &uSize);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtFtpServerSendReplyStr(pClient, "213 %RU64\r\n", uSize);
+ }
+ else
+ {
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_REQ_ACTION_NOT_TAKEN);
+ AssertRC(rc2);
+ }
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleSTAT(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ if (cArgs != 1)
+ return VERR_INVALID_PARAMETER;
+
+ int rc;
+
+ RTFSOBJINFO objInfo;
+ RT_ZERO(objInfo);
+
+ const char *pszPath = apszArgs[0];
+
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnFileStat, pszPath, &objInfo);
+
+ if (RT_SUCCESS(rc))
+ {
+ char szFsObjInfo[_4K]; /** @todo Check this size. */
+ rc = rtFtpServerFsObjInfoToStr(&objInfo, szFsObjInfo, sizeof(szFsObjInfo));
+ if (RT_SUCCESS(rc))
+ {
+ char szFsPathInfo[RTPATH_MAX + 16];
+ const ssize_t cchPathInfo = RTStrPrintf2(szFsPathInfo, sizeof(szFsPathInfo), " %2zu %s\n", strlen(pszPath), pszPath);
+ if (cchPathInfo > 0)
+ {
+ rc = RTStrCat(szFsObjInfo, sizeof(szFsObjInfo), szFsPathInfo);
+ if (RT_SUCCESS(rc))
+ rc = rtFtpServerSendReplyStr(pClient, szFsObjInfo);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_REQ_ACTION_NOT_TAKEN);
+ AssertRC(rc2);
+ }
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleSTRU(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ if (cArgs != 1)
+ return VERR_INVALID_PARAMETER;
+
+ const char *pszType = apszArgs[0];
+
+ int rc;
+
+ if (!RTStrICmp(pszType, "F"))
+ {
+ pClient->State.enmStructType = RTFTPSERVER_STRUCT_TYPE_FILE;
+
+ rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
+ }
+ else
+ rc = VERR_NOT_IMPLEMENTED;
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleSYST(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ RT_NOREF(cArgs, apszArgs);
+
+ char szOSInfo[64];
+ int rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szOSInfo, sizeof(szOSInfo));
+ if (RT_SUCCESS(rc))
+ rc = rtFtpServerSendReplyStr(pClient, "215 %s", szOSInfo);
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleTYPE(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ if (cArgs != 1)
+ return VERR_INVALID_PARAMETER;
+
+ const char *pszType = apszArgs[0];
+
+ int rc = VINF_SUCCESS;
+
+ if (!RTStrICmp(pszType, "A"))
+ {
+ pClient->State.enmDataType = RTFTPSERVER_DATA_TYPE_ASCII;
+ }
+ else if (!RTStrICmp(pszType, "I")) /* Image (binary). */
+ {
+ pClient->State.enmDataType = RTFTPSERVER_DATA_TYPE_IMAGE;
+ }
+ else /** @todo Support "E" (EBCDIC) and/or "L <size>" (custom)? */
+ rc = VERR_NOT_IMPLEMENTED;
+
+ if (RT_SUCCESS(rc))
+ rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_OKAY);
+
+ return rc;
+}
+
+static DECLCALLBACK(int) rtFtpServerHandleUSER(PRTFTPSERVERCLIENT pClient, uint8_t cArgs, const char * const *apszArgs)
+{
+ if (cArgs != 1)
+ return VERR_INVALID_PARAMETER;
+
+ const char *pszUser = apszArgs[0];
+ AssertPtrReturn(pszUser, VERR_INVALID_PARAMETER);
+
+ rtFtpServerClientStateReset(&pClient->State);
+
+ int rc = rtFtpServerLookupUser(pClient, pszUser);
+ if (RT_SUCCESS(rc))
+ {
+ pClient->State.pszUser = RTStrDup(pszUser);
+ AssertPtrReturn(pClient->State.pszUser, VERR_NO_MEMORY);
+
+ rc = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_USERNAME_OKAY_NEED_PASSWORD);
+ }
+ else
+ {
+ pClient->State.cFailedLoginAttempts++;
+
+ int rc2 = rtFtpServerSendReplyRc(pClient, RTFTPSERVER_REPLY_NOT_LOGGED_IN);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ return rc;
+}
+
+
+/*********************************************************************************************************************************
+* Internal server functions *
+*********************************************************************************************************************************/
+
+/**
+ * Parses FTP command arguments handed in by the client.
+ *
+ * @returns VBox status code.
+ * @param pszCmdParms Pointer to command arguments, if any. Can be NULL if no arguments are given.
+ * @param pcArgs Returns the number of parsed arguments, separated by a space (hex 0x20).
+ * @param ppapszArgs Returns the string array of parsed arguments. Needs to be free'd with rtFtpServerCmdArgsFree().
+ */
+static int rtFtpServerCmdArgsParse(const char *pszCmdParms, uint8_t *pcArgs, char ***ppapszArgs)
+{
+ *pcArgs = 0;
+ *ppapszArgs = NULL;
+
+ if (!pszCmdParms) /* No parms given? Bail out early. */
+ return VINF_SUCCESS;
+
+ /** @todo Anything else to do here? */
+ /** @todo Check if quoting is correct. */
+
+ int cArgs = 0;
+ int rc = RTGetOptArgvFromString(ppapszArgs, &cArgs, pszCmdParms, RTGETOPTARGV_CNV_QUOTE_MS_CRT, " " /* Separators */);
+ if (RT_SUCCESS(rc))
+ {
+ if (cArgs <= UINT8_MAX)
+ {
+ *pcArgs = (uint8_t)cArgs;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ return rc;
+}
+
+/**
+ * Frees a formerly argument string array parsed by rtFtpServerCmdArgsParse().
+ *
+ * @param ppapszArgs Argument string array to free.
+ */
+static void rtFtpServerCmdArgsFree(char **ppapszArgs)
+{
+ RTGetOptArgvFree(ppapszArgs);
+}
+
+/**
+ * Main function for processing client commands for the control connection.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to process commands for.
+ * @param pszCmd Command string to parse and handle.
+ * @param cbCmd Size (in bytes) of command string.
+ */
+static int rtFtpServerProcessCommands(PRTFTPSERVERCLIENT pClient, char *pszCmd, size_t cbCmd)
+{
+ /* Make sure to terminate the string in any case. */
+ pszCmd[RT_MIN(RTFTPSERVER_MAX_CMD_LEN, cbCmd)] = '\0';
+
+ /* A tiny bit of sanitation. */
+ RTStrStripL(pszCmd);
+
+ /* First, terminate string by finding the command end marker (telnet style). */
+ /** @todo Not sure if this is entirely correct and/or needs tweaking; good enough for now as it seems. */
+ char *pszCmdEnd = RTStrStr(pszCmd, "\r\n");
+ if (pszCmdEnd)
+ *pszCmdEnd = '\0';
+
+ /* Reply which gets sent back to the client. */
+ RTFTPSERVER_REPLY rcClient = RTFTPSERVER_REPLY_INVALID;
+
+ int rcCmd = VINF_SUCCESS;
+
+ uint8_t cArgs = 0;
+ char **papszArgs = NULL;
+ int rc = rtFtpServerCmdArgsParse(pszCmd, &cArgs, &papszArgs);
+ if ( RT_SUCCESS(rc)
+ && cArgs) /* At least the actual command (without args) must be present. */
+ {
+ LogFlowFunc(("Handling command '%s'\n", papszArgs[0]));
+ for (uint8_t a = 0; a < cArgs; a++)
+ LogFlowFunc(("\targ[%RU8] = '%s'\n", a, papszArgs[a]));
+
+ unsigned i = 0;
+ for (; i < RT_ELEMENTS(g_aCmdMap); i++)
+ {
+ const RTFTPSERVERCMD_ENTRY *pCmdEntry = &g_aCmdMap[i];
+
+ if (!RTStrICmp(papszArgs[0], pCmdEntry->szCmd))
+ {
+ /* Some commands need a valid user before they can be executed. */
+ if ( pCmdEntry->fNeedsUser
+ && pClient->State.pszUser == NULL)
+ {
+ rcClient = RTFTPSERVER_REPLY_NOT_LOGGED_IN;
+ break;
+ }
+
+ /* Save timestamp of last command sent. */
+ pClient->State.tsLastCmdMs = RTTimeMilliTS();
+
+ /* Hand in arguments only without the actual command. */
+ rcCmd = pCmdEntry->pfnCmd(pClient, cArgs - 1, cArgs > 1 ? &papszArgs[1] : NULL);
+ if (RT_FAILURE(rcCmd))
+ {
+ LogFunc(("Handling command '%s' failed with %Rrc\n", papszArgs[0], rcCmd));
+
+ switch (rcCmd)
+ {
+ case VERR_INVALID_PARAMETER:
+ RT_FALL_THROUGH();
+ case VERR_INVALID_POINTER:
+ rcClient = RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS;
+ break;
+
+ case VERR_NOT_IMPLEMENTED:
+ rcClient = RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL;
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ rtFtpServerCmdArgsFree(papszArgs);
+
+ if (i == RT_ELEMENTS(g_aCmdMap))
+ {
+ LogFlowFunc(("Command not implemented\n"));
+ Assert(rcClient == RTFTPSERVER_REPLY_INVALID);
+ rcClient = RTFTPSERVER_REPLY_ERROR_CMD_NOT_IMPL;
+ }
+
+ const bool fDisconnect = g_aCmdMap[i].enmCmd == RTFTPSERVERCMD_QUIT
+ || pClient->State.cFailedLoginAttempts >= 3; /** @todo Make this dynamic. */
+ if (fDisconnect)
+ {
+ RTFTPSERVER_HANDLE_CALLBACK_VA(pfnOnUserDisconnect, pClient->State.pszUser);
+
+ rtFtpServerClientStateReset(&pClient->State);
+
+ Assert(rcClient == RTFTPSERVER_REPLY_INVALID);
+ rcClient = RTFTPSERVER_REPLY_CLOSING_CTRL_CONN;
+ }
+ }
+ else
+ rcClient = RTFTPSERVER_REPLY_ERROR_INVALID_PARAMETERS;
+
+ if (rcClient != RTFTPSERVER_REPLY_INVALID)
+ {
+ int rc2 = rtFtpServerSendReplyRc(pClient, rcClient);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Main loop for processing client commands.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to process commands for.
+ */
+static int rtFtpServerClientMain(PRTFTPSERVERCLIENT pClient)
+{
+ int rc;
+
+ size_t cbRead;
+ char szCmd[RTFTPSERVER_MAX_CMD_LEN + 1];
+
+ for (;;)
+ {
+ rc = RTTcpSelectOne(pClient->hSocket, 200 /* ms */); /** @todo Can we improve here? Using some poll events or so? */
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTTcpReadNB(pClient->hSocket, szCmd, sizeof(szCmd), &cbRead);
+ if ( RT_SUCCESS(rc)
+ && cbRead)
+ {
+ AssertBreakStmt(cbRead <= sizeof(szCmd), rc = VERR_BUFFER_OVERFLOW);
+ rc = rtFtpServerProcessCommands(pClient, szCmd, cbRead);
+ }
+ }
+ else if (rc == VERR_TIMEOUT)
+ rc = VINF_SUCCESS;
+ else
+ break;
+
+ /*
+ * Handle data connection replies.
+ */
+ if (pClient->pDataConn)
+ {
+ if ( ASMAtomicReadBool(&pClient->pDataConn->fStarted)
+ && ASMAtomicReadBool(&pClient->pDataConn->fStopped))
+ {
+ Assert(pClient->pDataConn->rc != VERR_IPE_UNINITIALIZED_STATUS);
+
+ int rc2 = rtFtpServerSendReplyRc(pClient,
+ RT_SUCCESS(pClient->pDataConn->rc)
+ ? RTFTPSERVER_REPLY_CLOSING_DATA_CONN : RTFTPSERVER_REPLY_CONN_REQ_FILE_ACTION_NOT_TAKEN);
+ AssertRC(rc2);
+
+ rc = rtFtpServerDataConnStop(pClient->pDataConn);
+ if (RT_SUCCESS(rc))
+ {
+ rtFtpServerDataConnDestroy(pClient->pDataConn);
+ pClient->pDataConn = NULL;
+ }
+ }
+ }
+ }
+
+ /* Make sure to destroy all data connections. */
+ rtFtpServerDataConnDestroy(pClient->pDataConn);
+ pClient->pDataConn = NULL;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Resets the client's state.
+ *
+ * @param pState Client state to reset.
+ */
+static void rtFtpServerClientStateReset(PRTFTPSERVERCLIENTSTATE pState)
+{
+ LogFlowFuncEnter();
+
+ RTStrFree(pState->pszUser);
+ pState->pszUser = NULL;
+
+ int rc2 = rtFtpSetCWD(pState, "/");
+ AssertRC(rc2);
+
+ pState->cFailedLoginAttempts = 0;
+ pState->tsLastCmdMs = RTTimeMilliTS();
+ pState->enmDataType = RTFTPSERVER_DATA_TYPE_ASCII;
+ pState->enmStructType = RTFTPSERVER_STRUCT_TYPE_FILE;
+}
+
+/**
+ * Per-client thread for serving the server's control connection.
+ *
+ * @returns VBox status code.
+ * @param hSocket Socket handle to use for the control connection.
+ * @param pvUser User-provided arguments. Of type PRTFTPSERVERINTERNAL.
+ */
+static DECLCALLBACK(int) rtFtpServerClientThread(RTSOCKET hSocket, void *pvUser)
+{
+ PRTFTPSERVERINTERNAL pThis = (PRTFTPSERVERINTERNAL)pvUser;
+ RTFTPSERVER_VALID_RETURN(pThis);
+
+ RTFTPSERVERCLIENT Client;
+ RT_ZERO(Client);
+
+ Client.pServer = pThis;
+ Client.hSocket = hSocket;
+
+ LogFlowFunc(("New client connected\n"));
+
+ rtFtpServerClientStateReset(&Client.State);
+
+ /*
+ * Send welcome message.
+ * Note: Some clients (like FileZilla / Firefox) expect a message together with the reply code,
+ * so make sure to include at least *something*.
+ */
+ int rc = rtFtpServerSendReplyRcEx(&Client, RTFTPSERVER_REPLY_READY_FOR_NEW_USER,
+ "Welcome!");
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicIncU32(&pThis->cClients);
+
+ rc = rtFtpServerClientMain(&Client);
+
+ ASMAtomicDecU32(&pThis->cClients);
+ }
+
+ rtFtpServerClientStateReset(&Client.State);
+
+ return rc;
+}
+
+RTR3DECL(int) RTFtpServerCreate(PRTFTPSERVER phFTPServer, const char *pszAddress, uint16_t uPort,
+ PRTFTPSERVERCALLBACKS pCallbacks, void *pvUser, size_t cbUser)
+{
+ AssertPtrReturn(phFTPServer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
+ AssertReturn (uPort, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pCallbacks, VERR_INVALID_POINTER);
+ /* pvUser is optional. */
+
+ int rc;
+
+ PRTFTPSERVERINTERNAL pThis = (PRTFTPSERVERINTERNAL)RTMemAllocZ(sizeof(RTFTPSERVERINTERNAL));
+ if (pThis)
+ {
+ pThis->u32Magic = RTFTPSERVER_MAGIC;
+ pThis->Callbacks = *pCallbacks;
+ pThis->pvUser = pvUser;
+ pThis->cbUser = cbUser;
+
+ rc = RTTcpServerCreate(pszAddress, uPort, RTTHREADTYPE_DEFAULT, "ftpsrv",
+ rtFtpServerClientThread, pThis /* pvUser */, &pThis->pTCPServer);
+ if (RT_SUCCESS(rc))
+ {
+ *phFTPServer = (RTFTPSERVER)pThis;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+RTR3DECL(int) RTFtpServerDestroy(RTFTPSERVER hFTPServer)
+{
+ if (hFTPServer == NIL_RTFTPSERVER)
+ return VINF_SUCCESS;
+
+ PRTFTPSERVERINTERNAL pThis = hFTPServer;
+ RTFTPSERVER_VALID_RETURN(pThis);
+
+ AssertPtr(pThis->pTCPServer);
+
+ int rc = RTTcpServerDestroy(pThis->pTCPServer);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32Magic = RTFTPSERVER_MAGIC_DEAD;
+
+ RTMemFree(pThis);
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/generic/Makefile.kup b/src/VBox/Runtime/r3/generic/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/generic/Makefile.kup
diff --git a/src/VBox/Runtime/r3/generic/RTLocaleQueryLocaleName-r3-generic.cpp b/src/VBox/Runtime/r3/generic/RTLocaleQueryLocaleName-r3-generic.cpp
new file mode 100644
index 00000000..1618d593
--- /dev/null
+++ b/src/VBox/Runtime/r3/generic/RTLocaleQueryLocaleName-r3-generic.cpp
@@ -0,0 +1,67 @@
+/* $Id: RTLocaleQueryLocaleName-r3-generic.cpp $ */
+/** @file
+ * IPRT - RTLocaleQueryLocaleName, ring-3 generic.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <locale.h>
+
+#include <iprt/locale.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#ifdef RT_OS_SOLARIS
+#include <iprt/path.h>
+#endif /* RT_OS_SOLARIS */
+
+
+
+RTDECL(int) RTLocaleQueryLocaleName(char *pszName, size_t cbName)
+{
+ const char *pszLocale = setlocale(LC_ALL, NULL);
+ if (pszLocale)
+ {
+#ifdef RT_OS_SOLARIS /* Solaris can return a locale starting with a slash ('/'), e.g. /en_GB.UTF-8/C/C/C/C/C */
+ if (RTPATH_IS_SLASH(*pszLocale))
+ pszLocale++;
+#endif /* RT_OS_SOLARIS */
+ return RTStrCopy(pszName, cbName, pszLocale);
+ }
+ return VERR_NOT_AVAILABLE;
+}
+
diff --git a/src/VBox/Runtime/r3/generic/RTLocaleQueryNormalizedBaseLocaleName-r3-generic.cpp b/src/VBox/Runtime/r3/generic/RTLocaleQueryNormalizedBaseLocaleName-r3-generic.cpp
new file mode 100644
index 00000000..6ae8ef83
--- /dev/null
+++ b/src/VBox/Runtime/r3/generic/RTLocaleQueryNormalizedBaseLocaleName-r3-generic.cpp
@@ -0,0 +1,102 @@
+/* $Id: RTLocaleQueryNormalizedBaseLocaleName-r3-generic.cpp $ */
+/** @file
+ * IPRT - RTLocaleQueryNormalizedBaseLocaleName, ring-3 generic.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/locale.h>
+#include "internal/iprt.h"
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+/*
+ * Note! Code duplicated in r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp (adds fallback).
+ */
+RTDECL(int) RTLocaleQueryNormalizedBaseLocaleName(char *pszName, size_t cbName)
+{
+ char szLocale[_1K];
+ int rc = RTLocaleQueryLocaleName(szLocale, sizeof(szLocale));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * May return some complicated "LC_XXX=yyy;LC.." sequence if
+ * partially set (like IPRT does). Try get xx_YY sequence first
+ * because 'C' or 'POSIX' may be LC_xxx variants that haven't been
+ * set yet.
+ *
+ * ASSUMES complicated locale mangling is done in a certain way...
+ */
+ const char *pszLocale = strchr(szLocale, '=');
+ if (!pszLocale)
+ pszLocale = szLocale;
+ else
+ pszLocale++;
+ bool fSeenC = false;
+ bool fSeenPOSIX = false;
+ do
+ {
+ const char *pszEnd = strchr(pszLocale, ';');
+
+ if ( RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(pszLocale)
+ && ( pszLocale[5] == '\0'
+ || RT_C_IS_PUNCT(pszLocale[5])) )
+ return RTStrCopyEx(pszName, cbName, pszLocale, 5);
+
+ if ( pszLocale[0] == 'C'
+ && ( pszLocale[1] == '\0'
+ || RT_C_IS_PUNCT(pszLocale[1])) )
+ fSeenC = true;
+ else if ( strncmp(pszLocale, "POSIX", 5) == 0
+ && ( pszLocale[5] == '\0'
+ || RT_C_IS_PUNCT(pszLocale[5])) )
+ fSeenPOSIX = true;
+
+ /* advance */
+ pszLocale = pszEnd ? strchr(pszEnd + 1, '=') : NULL;
+ } while (pszLocale++);
+
+ if (fSeenC || fSeenPOSIX)
+ return RTStrCopy(pszName, cbName, "C"); /* C and POSIX should be identical IIRC, so keep it simple. */
+
+ rc = VERR_NOT_AVAILABLE;
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/generic/RTLocaleQueryUserCountryCode-r3-generic.cpp b/src/VBox/Runtime/r3/generic/RTLocaleQueryUserCountryCode-r3-generic.cpp
new file mode 100644
index 00000000..64d675e2
--- /dev/null
+++ b/src/VBox/Runtime/r3/generic/RTLocaleQueryUserCountryCode-r3-generic.cpp
@@ -0,0 +1,87 @@
+/* $Id: RTLocaleQueryUserCountryCode-r3-generic.cpp $ */
+/** @file
+ * IPRT - RTLocaleQueryLocaleName, ring-3 generic.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <locale.h>
+
+#include <iprt/locale.h>
+#include "internal/iprt.h"
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+
+RTDECL(int) RTLocaleQueryUserCountryCode(char pszCountryCode[3])
+{
+ static const int s_aiLocales[] =
+ {
+ LC_ALL,
+ LC_CTYPE,
+ LC_COLLATE,
+ LC_MONETARY,
+ LC_NUMERIC,
+ LC_TIME
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aiLocales); i++)
+ {
+ const char *pszLocale = setlocale(s_aiLocales[i], NULL);
+ if ( pszLocale
+ && strlen(pszLocale) >= 5
+ && RT_C_IS_ALPHA(pszLocale[0])
+ && RT_C_IS_ALPHA(pszLocale[1])
+ && pszLocale[2] == '_'
+ && RT_C_IS_ALPHA(pszLocale[3])
+ && RT_C_IS_ALPHA(pszLocale[4]))
+ {
+ pszCountryCode[0] = RT_C_TO_UPPER(pszLocale[3]);
+ pszCountryCode[1] = RT_C_TO_UPPER(pszLocale[4]);
+ pszCountryCode[2] = '\0';
+ return VINF_SUCCESS;
+ }
+ }
+
+ pszCountryCode[0] = 'Z';
+ pszCountryCode[1] = 'Z';
+ pszCountryCode[2] = '\0';
+ return VERR_NOT_AVAILABLE;
+}
+
diff --git a/src/VBox/Runtime/r3/generic/RTTimeZoneGetCurrent-generic.cpp b/src/VBox/Runtime/r3/generic/RTTimeZoneGetCurrent-generic.cpp
new file mode 100644
index 00000000..5cc6b39a
--- /dev/null
+++ b/src/VBox/Runtime/r3/generic/RTTimeZoneGetCurrent-generic.cpp
@@ -0,0 +1,51 @@
+/* $Id: RTTimeZoneGetCurrent-generic.cpp $ */
+/** @file
+ * IPRT - RTTimeZoneGetCurrent, generic.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/env.h>
+
+
+RTDECL(int) RTTimeZoneGetCurrent(char *pszName, size_t cbName)
+{
+ return RTEnvGetEx(RTENV_DEFAULT, "TZ", pszName, cbName, NULL);
+}
+
diff --git a/src/VBox/Runtime/r3/generic/allocex-r3-generic.cpp b/src/VBox/Runtime/r3/generic/allocex-r3-generic.cpp
new file mode 100644
index 00000000..2e7f0528
--- /dev/null
+++ b/src/VBox/Runtime/r3/generic/allocex-r3-generic.cpp
@@ -0,0 +1,70 @@
+/* $Id: allocex-r3-generic.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Extended Alloc Workers, generic.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTMEM_NO_WRAP_TO_EF_APIS
+#include <iprt/mem.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "../allocex.h"
+
+
+
+DECLHIDDEN(int) rtMemAllocEx16BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv)
+{
+ RT_NOREF(cbAlloc, fFlags, ppv);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtMemAllocEx32BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv)
+{
+ RT_NOREF(cbAlloc, fFlags, ppv);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(void) rtMemFreeExYyBitReach(void *pv, size_t cb, uint32_t fFlags)
+{
+ RT_NOREF(pv, cb, fFlags);
+ AssertFailed();
+}
+
diff --git a/src/VBox/Runtime/r3/generic/dirrel-r3-generic.cpp b/src/VBox/Runtime/r3/generic/dirrel-r3-generic.cpp
new file mode 100644
index 00000000..da34638e
--- /dev/null
+++ b/src/VBox/Runtime/r3/generic/dirrel-r3-generic.cpp
@@ -0,0 +1,369 @@
+/* $Id: dirrel-r3-generic.cpp $ */
+/** @file
+ * IPRT - Directory relative base APIs, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/dir.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/symlink.h>
+#define RTDIR_AGNOSTIC
+#include "internal/dir.h"
+
+
+
+/**
+ * Helper that builds a full path for a directory relative path.
+ *
+ * @returns IPRT status code.
+ * @param pThis The directory.
+ * @param pszPathDst The destination buffer.
+ * @param cbPathDst The size of the destination buffer.
+ * @param pszRelPath The relative path.
+ */
+static int rtDirRelBuildFullPath(PRTDIRINTERNAL pThis, char *pszPathDst, size_t cbPathDst, const char *pszRelPath)
+{
+ AssertMsgReturn(!RTPathStartsWithRoot(pszRelPath), ("pszRelPath='%s'\n", pszRelPath), VERR_PATH_IS_NOT_RELATIVE);
+
+ /*
+ * Let's hope we can avoid checking for ascension.
+ *
+ * Note! We don't take symbolic links into account here. That can be
+ * done later if desired.
+ */
+ if ( !(pThis->fFlags & RTDIR_F_DENY_ASCENT)
+ || strstr(pszRelPath, "..") == NULL)
+ {
+ size_t const cchRelPath = strlen(pszRelPath);
+ size_t const cchDirPath = pThis->cchPath;
+ if (cchDirPath + cchRelPath < cbPathDst)
+ {
+ memcpy(pszPathDst, pThis->pszPath, cchDirPath);
+ memcpy(&pszPathDst[cchDirPath], pszRelPath, cchRelPath);
+ pszPathDst[cchDirPath + cchRelPath] = '\0';
+ return VINF_SUCCESS;
+ }
+ return VERR_FILENAME_TOO_LONG;
+ }
+
+ /*
+ * Calc the absolute path using the directory as a base, then check if the result
+ * still starts with the full directory path.
+ *
+ * This ASSUMES that pThis->pszPath is an absolute path.
+ */
+ int rc = RTPathAbsEx(pThis->pszPath, pszRelPath, RTPATH_STR_F_STYLE_HOST, pszPathDst, &cbPathDst);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTPathStartsWith(pszPathDst, pThis->pszPath))
+ return VINF_SUCCESS;
+ return VERR_PATH_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/*
+ *
+ *
+ * RTFile stuff.
+ * RTFile stuff.
+ * RTFile stuff.
+ *
+ *
+ */
+
+
+
+
+RTDECL(int) RTDirRelFileOpen(RTDIR hDir, const char *pszRelFilename, uint64_t fOpen, PRTFILE phFile)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelFilename);
+ if (RT_SUCCESS(rc))
+ rc = RTFileOpen(phFile, szPath, fOpen);
+ return rc;
+}
+
+
+
+/*
+ *
+ *
+ * RTDir stuff.
+ * RTDir stuff.
+ * RTDir stuff.
+ *
+ *
+ */
+
+
+
+RTDECL(int) RTDirRelDirOpen(RTDIR hDir, const char *pszDir, RTDIR *phDir)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszDir);
+ if (RT_SUCCESS(rc))
+ rc = RTDirOpen(phDir, szPath);
+ return rc;
+
+}
+
+
+RTDECL(int) RTDirRelDirOpenFiltered(RTDIR hDir, const char *pszDirAndFilter, RTDIRFILTER enmFilter,
+ uint32_t fFlags, RTDIR *phDir)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszDirAndFilter);
+ if (RT_SUCCESS(rc))
+ rc = RTDirOpenFiltered(phDir, szPath, enmFilter, fFlags);
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelDirCreate(RTDIR hDir, const char *pszRelPath, RTFMODE fMode, uint32_t fCreate, RTDIR *phSubDir)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDirCreate(szPath, fMode, fCreate);
+ if (RT_SUCCESS(rc) && phSubDir)
+ rc = RTDirOpen(phSubDir, szPath);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelDirRemove(RTDIR hDir, const char *pszRelPath)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ rc = RTDirRemove(szPath);
+ return rc;
+}
+
+
+/*
+ *
+ * RTPath stuff.
+ * RTPath stuff.
+ * RTPath stuff.
+ *
+ *
+ */
+
+
+RTDECL(int) RTDirRelPathQueryInfo(RTDIR hDir, const char *pszRelPath, PRTFSOBJINFO pObjInfo,
+ RTFSOBJATTRADD enmAddAttr, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ rc = RTPathQueryInfoEx(szPath, pObjInfo, enmAddAttr, fFlags);
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathSetMode(RTDIR hDir, const char *pszRelPath, RTFMODE fMode, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_FLAGS);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef RT_OS_WINDOWS
+ rc = RTPathSetMode(szPath, fMode); /** @todo fFlags is currently ignored. */
+#else
+ rc = VERR_NOT_IMPLEMENTED; /** @todo implement RTPathSetMode on windows. */
+ RT_NOREF(fMode);
+#endif
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathSetTimes(RTDIR hDir, const char *pszRelPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ rc = RTPathSetTimesEx(szPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, fFlags);
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathSetOwner(RTDIR hDir, const char *pszRelPath, uint32_t uid, uint32_t gid, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef RT_OS_WINDOWS
+ rc = RTPathSetOwnerEx(szPath, uid, gid, fFlags);
+#else
+ rc = VERR_NOT_IMPLEMENTED;
+ RT_NOREF(uid, gid, fFlags);
+#endif
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathRename(RTDIR hDirSrc, const char *pszSrc, RTDIR hDirDst, const char *pszDst, unsigned fRename)
+{
+ PRTDIRINTERNAL pThis = hDirSrc;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ PRTDIRINTERNAL pThat = hDirDst;
+ if (pThat != pThis)
+ {
+ AssertPtrReturn(pThat, VERR_INVALID_HANDLE);
+ AssertReturn(pThat->u32Magic != RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ }
+
+ char szSrcPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szSrcPath, sizeof(szSrcPath), pszSrc);
+ if (RT_SUCCESS(rc))
+ {
+ char szDstPath[RTPATH_MAX];
+ rc = rtDirRelBuildFullPath(pThis, szDstPath, sizeof(szDstPath), pszDst);
+ if (RT_SUCCESS(rc))
+ rc = RTPathRename(szSrcPath, szDstPath, fRename);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathUnlink(RTDIR hDir, const char *pszRelPath, uint32_t fUnlink)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ rc = RTPathUnlink(szPath, fUnlink);
+ return rc;
+}
+
+
+/*
+ *
+ * RTSymlink stuff.
+ * RTSymlink stuff.
+ * RTSymlink stuff.
+ *
+ *
+ */
+
+
+RTDECL(int) RTDirRelSymlinkCreate(RTDIR hDir, const char *pszSymlink, const char *pszTarget,
+ RTSYMLINKTYPE enmType, uint32_t fCreate)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszSymlink);
+ if (RT_SUCCESS(rc))
+ rc = RTSymlinkCreate(szPath, pszTarget, enmType, fCreate);
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelSymlinkRead(RTDIR hDir, const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszSymlink);
+ if (RT_SUCCESS(rc))
+ rc = RTSymlinkRead(szPath, pszTarget, cbTarget, fRead);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/generic/semspinmutex-r3-generic.cpp b/src/VBox/Runtime/r3/generic/semspinmutex-r3-generic.cpp
new file mode 100644
index 00000000..ae6d7c5e
--- /dev/null
+++ b/src/VBox/Runtime/r3/generic/semspinmutex-r3-generic.cpp
@@ -0,0 +1,103 @@
+/* $Id: semspinmutex-r3-generic.cpp $ */
+/** @file
+ * IPRT - Spinning Mutex Semaphores, Ring-3, Generic.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+
+
+
+RTDECL(int) RTSemSpinMutexCreate(PRTSEMSPINMUTEX phSpinMtx, uint32_t fFlags)
+{
+ AssertReturn(!(fFlags & ~RTSEMSPINMUTEX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertPtr(phSpinMtx);
+
+ PRTCRITSECT pCritSect = (PRTCRITSECT)RTMemAlloc(sizeof(RTCRITSECT));
+ if (!pCritSect)
+ return VERR_NO_MEMORY;
+ int rc = RTCritSectInitEx(pCritSect, RTCRITSECT_FLAGS_NO_NESTING | RTCRITSECT_FLAGS_NO_LOCK_VAL,
+ NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTSemSpinMutex");
+ if (RT_SUCCESS(rc))
+ *phSpinMtx = (RTSEMSPINMUTEX)pCritSect;
+ else
+ RTMemFree(pCritSect);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemSpinMutexCreate);
+
+
+RTDECL(int) RTSemSpinMutexDestroy(RTSEMSPINMUTEX hSpinMtx)
+{
+ if (hSpinMtx == NIL_RTSEMSPINMUTEX)
+ return VERR_INVALID_PARAMETER;
+ PRTCRITSECT pCritSect = (PRTCRITSECT)hSpinMtx;
+ int rc = RTCritSectDelete(pCritSect);
+ if (RT_SUCCESS(rc))
+ RTMemFree(pCritSect);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemSpinMutexDestroy);
+
+
+RTDECL(int) RTSemSpinMutexTryRequest(RTSEMSPINMUTEX hSpinMtx)
+{
+ return RTCritSectTryEnter((PRTCRITSECT)hSpinMtx);
+
+}
+RT_EXPORT_SYMBOL(RTSemSpinMutexTryRequest);
+
+
+RTDECL(int) RTSemSpinMutexRequest(RTSEMSPINMUTEX hSpinMtx)
+{
+ return RTCritSectEnter((PRTCRITSECT)hSpinMtx);
+}
+RT_EXPORT_SYMBOL(RTSemSpinMutexRequest);
+
+
+RTDECL(int) RTSemSpinMutexRelease(RTSEMSPINMUTEX hSpinMtx)
+{
+ return RTCritSectLeave((PRTCRITSECT)hSpinMtx);
+}
+RT_EXPORT_SYMBOL(RTSemSpinMutexRelease);
+
diff --git a/src/VBox/Runtime/r3/haiku/Makefile.kup b/src/VBox/Runtime/r3/haiku/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/haiku/Makefile.kup
diff --git a/src/VBox/Runtime/r3/haiku/rtProcInitExePath-haiku.cpp b/src/VBox/Runtime/r3/haiku/rtProcInitExePath-haiku.cpp
new file mode 100644
index 00000000..51905b73
--- /dev/null
+++ b/src/VBox/Runtime/r3/haiku/rtProcInitExePath-haiku.cpp
@@ -0,0 +1,71 @@
+/* $Id: rtProcInitExePath-haiku.cpp $ */
+/** @file
+ * IPRT - rtProcInitName, Haiku.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#ifdef RT_OS_HAIKU
+# include <image.h>
+#endif
+
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include "internal/process.h"
+#include "internal/path.h"
+
+
+DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath)
+{
+ image_info ImageInfo;
+ int32 Cookie = 0;
+ status_t status;
+
+ /*
+ * Query the image name from the OS, convert and return it.
+ */
+ status = get_next_image_info(0, &Cookie, &ImageInfo);
+ AssertReturn((status == B_OK), VERR_INTERNAL_ERROR);
+
+ int rc = rtPathFromNativeCopy(pszPath, MIN(cchPath, MAXPATHLEN), ImageInfo.name, NULL);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, MIN(cchPath, MAXPATHLEN), pszPath), rc);
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/haiku/time-haiku.cpp b/src/VBox/Runtime/r3/haiku/time-haiku.cpp
new file mode 100644
index 00000000..a6419b83
--- /dev/null
+++ b/src/VBox/Runtime/r3/haiku/time-haiku.cpp
@@ -0,0 +1,95 @@
+/* $Id: time-haiku.cpp $ */
+/** @file
+ * IPRT - Time, Haiku.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#define RTTIME_INCL_TIMEVAL
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <OS.h>
+
+#include <iprt/time.h>
+#include <iprt/errcore.h>
+#include "internal/time.h"
+
+
+DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void)
+{
+ return (uint64_t)system_time() * RT_NS_1US;
+}
+
+
+/**
+ * Gets the current nanosecond timestamp.
+ *
+ * This differs from RTTimeNanoTS in that it will use system APIs and not do any
+ * resolution or performance optimizations.
+ *
+ * @returns nanosecond timestamp.
+ */
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+/**
+ * Gets the current millisecond timestamp.
+ *
+ * This differs from RTTimeNanoTS in that it will use system APIs and not do any
+ * resolution or performance optimizations.
+ *
+ * @returns millisecond timestamp.
+ */
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(int) RTTimeSet(PCRTTIMESPEC pTime)
+{
+ struct timeval tv;
+ RTTimeSpecGetTimeval(pTime, &tv);
+ set_real_time_clock(tv.tv_sec);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/http-server.cpp b/src/VBox/Runtime/r3/http-server.cpp
new file mode 100644
index 00000000..93c3f3fa
--- /dev/null
+++ b/src/VBox/Runtime/r3/http-server.cpp
@@ -0,0 +1,1455 @@
+/* $Id: http-server.cpp $ */
+/** @file
+ * Simple HTTP server (RFC 7231) implementation.
+ *
+ * Known limitations so far:
+ * - Only HTTP 1.1.
+ * - Only supports GET + HEAD methods so far.
+ * - Only supports UTF-8 charset.
+ * - Only supports plain text and octet stream MIME types.
+ * - No content compression ("gzip", "x-gzip", ++).
+ * - No caching.
+ * - No redirections (via 302).
+ * - No encryption (TLS).
+ * - No IPv6 support.
+ * - No multi-threading.
+ *
+ * For WebDAV (optional via IPRT_HTTP_WITH_WEBDAV):
+ * - Only OPTIONS + PROPLIST methods are implemented (e.g. simple read-only support).
+ * - No pagination support for directory listings.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_HTTP
+#include <iprt/http.h>
+#include <iprt/http-server.h>
+#include "internal/iprt.h"
+#include "internal/magics.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/circbuf.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/file.h> /* For file mode flags. */
+#include <iprt/getopt.h>
+#include <iprt/mem.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+#include <iprt/poll.h>
+#include <iprt/socket.h>
+#include <iprt/sort.h>
+#include <iprt/string.h>
+#include <iprt/system.h>
+#include <iprt/tcp.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Internal HTTP server instance.
+ */
+typedef struct RTHTTPSERVERINTERNAL
+{
+ /** Magic value. */
+ uint32_t u32Magic;
+ /** Callback table. */
+ RTHTTPSERVERCALLBACKS Callbacks;
+ /** Pointer to TCP server instance. */
+ PRTTCPSERVER pTCPServer;
+ /** Pointer to user-specific data. Optional. */
+ void *pvUser;
+ /** Size of user-specific data. Optional. */
+ size_t cbUser;
+} RTHTTPSERVERINTERNAL;
+/** Pointer to an internal HTTP server instance. */
+typedef RTHTTPSERVERINTERNAL *PRTHTTPSERVERINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
+#define RTHTTPSERVER_VALID_RETURN_RC(hHttpServer, a_rc) \
+ do { \
+ AssertPtrReturn((hHttpServer), (a_rc)); \
+ AssertReturn((hHttpServer)->u32Magic == RTHTTPSERVER_MAGIC, (a_rc)); \
+ } while (0)
+
+/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
+#define RTHTTPSERVER_VALID_RETURN(hHttpServer) RTHTTPSERVER_VALID_RETURN_RC((hHttpServer), VERR_INVALID_HANDLE)
+
+/** Validates a handle and returns (void) if not valid. */
+#define RTHTTPSERVER_VALID_RETURN_VOID(hHttpServer) \
+ do { \
+ AssertPtrReturnVoid(hHttpServer); \
+ AssertReturnVoid((hHttpServer)->u32Magic == RTHTTPSERVER_MAGIC); \
+ } while (0)
+
+
+/** Handles a HTTP server callback with no arguments and returns. */
+#define RTHTTPSERVER_HANDLE_CALLBACK_RET(a_Name) \
+ do \
+ { \
+ PRTHTTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
+ if (pCallbacks->a_Name) \
+ { \
+ RTHTTPCALLBACKDATA Data = { &pClient->State }; \
+ return pCallbacks->a_Name(&Data); \
+ } \
+ return VERR_NOT_IMPLEMENTED; \
+ } while (0)
+
+/** Handles a HTTP server callback with no arguments and sets rc accordingly. */
+#define RTHTTPSERVER_HANDLE_CALLBACK(a_Name) \
+ do \
+ { \
+ PRTHTTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
+ if (pCallbacks->a_Name) \
+ { \
+ RTHTTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
+ rc = pCallbacks->a_Name(&Data); \
+ } \
+ } while (0)
+
+/** Handles a HTTP server callback with arguments and sets rc accordingly. */
+#define RTHTTPSERVER_HANDLE_CALLBACK_VA(a_Name, ...) \
+ do \
+ { \
+ PRTHTTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
+ if (pCallbacks->a_Name) \
+ { \
+ RTHTTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
+ rc = pCallbacks->a_Name(&Data, __VA_ARGS__); \
+ } \
+ } while (0)
+
+/** Handles a HTTP server callback with arguments and returns. */
+#define RTHTTPSERVER_HANDLE_CALLBACK_VA_RET(a_Name, ...) \
+ do \
+ { \
+ PRTHTTPSERVERCALLBACKS pCallbacks = &pClient->pServer->Callbacks; \
+ if (pCallbacks->a_Name) \
+ { \
+ RTHTTPCALLBACKDATA Data = { &pClient->State, pClient->pServer->pvUser, pClient->pServer->cbUser }; \
+ return pCallbacks->a_Name(&Data, __VA_ARGS__); \
+ } \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Structure for maintaining an internal HTTP server client.
+ */
+typedef struct RTHTTPSERVERCLIENT
+{
+ /** Pointer to internal server state. */
+ PRTHTTPSERVERINTERNAL pServer;
+ /** Socket handle the client is bound to. */
+ RTSOCKET hSocket;
+ /** Actual client state. */
+ RTHTTPSERVERCLIENTSTATE State;
+} RTHTTPSERVERCLIENT;
+/** Pointer to an internal HTTP server client state. */
+typedef RTHTTPSERVERCLIENT *PRTHTTPSERVERCLIENT;
+
+/** Function pointer declaration for a specific HTTP server method handler. */
+typedef DECLCALLBACKTYPE(int, FNRTHTTPSERVERMETHOD,(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq));
+/** Pointer to a FNRTHTTPSERVERMETHOD(). */
+typedef FNRTHTTPSERVERMETHOD *PFNRTHTTPSERVERMETHOD;
+
+/**
+ * Static lookup table for some file extensions <-> MIME type. Add more as needed.
+ * Keep this alphabetical (file extension).
+ */
+static const struct
+{
+ /** File extension. */
+ const char *pszExt;
+ /** MIME type. */
+ const char *pszMIMEType;
+} s_aFileExtMIMEType[] = {
+ { ".arj", "application/x-arj-compressed" },
+ { ".asf", "video/x-ms-asf" },
+ { ".avi", "video/x-msvideo" },
+ { ".bmp", "image/bmp" },
+ { ".css", "text/css" },
+ { ".doc", "application/msword" },
+ { ".exe", "application/octet-stream" },
+ { ".gif", "image/gif" },
+ { ".gz", "application/x-gunzip" },
+ { ".htm", "text/html" },
+ { ".html", "text/html" },
+ { ".ico", "image/x-icon" },
+ { ".js", "application/x-javascript" },
+ { ".json", "text/json" },
+ { ".jpg", "image/jpeg" },
+ { ".jpeg", "image/jpeg" },
+ { ".ogg", "application/ogg" },
+ { ".m3u", "audio/x-mpegurl" },
+ { ".m4v", "video/x-m4v" },
+ { ".mid", "audio/mid" },
+ { ".mov", "video/quicktime" },
+ { ".mp3", "audio/x-mp3" },
+ { ".mp4", "video/mp4" },
+ { ".mpg", "video/mpeg" },
+ { ".mpeg", "video/mpeg" },
+ { ".pdf", "application/pdf" },
+ { ".png", "image/png" },
+ { ".ra", "audio/x-pn-realaudio" },
+ { ".ram", "audio/x-pn-realaudio" },
+ { ".rar", "application/x-arj-compressed" },
+ { ".rtf", "application/rtf" },
+ { ".shtm", "text/html" },
+ { ".shtml", "text/html" },
+ { ".svg", "image/svg+xml" },
+ { ".swf", "application/x-shockwave-flash" },
+ { ".torrent", "application/x-bittorrent" },
+ { ".tar", "application/x-tar" },
+ { ".tgz", "application/x-tar-gz" },
+ { ".ttf", "application/x-font-ttf" },
+ { ".txt", "text/plain" },
+ { ".wav", "audio/x-wav" },
+ { ".webm", "video/webm" },
+ { ".xml", "text/xml" },
+ { ".xls", "application/excel" },
+ { ".xsl", "application/xml" },
+ { ".xslt", "application/xml" },
+ { ".zip", "application/x-zip-compressed" },
+ { NULL, NULL }
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/** @name Method handlers.
+ * @{
+ */
+static FNRTHTTPSERVERMETHOD rtHttpServerHandleGET;
+static FNRTHTTPSERVERMETHOD rtHttpServerHandleHEAD;
+#ifdef IPRT_HTTP_WITH_WEBDAV
+ static FNRTHTTPSERVERMETHOD rtHttpServerHandleOPTIONS;
+ static FNRTHTTPSERVERMETHOD rtHttpServerHandlePROPFIND;
+#endif
+/** @} */
+
+/**
+ * Structure for maintaining a single method entry for the methods table.
+ */
+typedef struct RTHTTPSERVERMETHOD_ENTRY
+{
+ /** Method ID. */
+ RTHTTPMETHOD enmMethod;
+ /** Function pointer invoked to handle the command. */
+ PFNRTHTTPSERVERMETHOD pfnMethod;
+} RTHTTPSERVERMETHOD_ENTRY;
+/** Pointer to a command entry. */
+typedef RTHTTPSERVERMETHOD_ENTRY *PRTHTTPMETHOD_ENTRY;
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Table of handled methods.
+ */
+static const RTHTTPSERVERMETHOD_ENTRY g_aMethodMap[] =
+{
+ { RTHTTPMETHOD_GET, rtHttpServerHandleGET },
+ { RTHTTPMETHOD_HEAD, rtHttpServerHandleHEAD },
+#ifdef IPRT_HTTP_WITH_WEBDAV
+ { RTHTTPMETHOD_OPTIONS, rtHttpServerHandleOPTIONS },
+ { RTHTTPMETHOD_PROPFIND, rtHttpServerHandlePROPFIND },
+#endif
+ { RTHTTPMETHOD_END, NULL }
+};
+
+/** Maximum length in characters a HTTP server path can have (excluding termination). */
+#define RTHTTPSERVER_MAX_PATH RTPATH_MAX
+
+
+/*********************************************************************************************************************************
+* Internal functions *
+*********************************************************************************************************************************/
+
+/**
+ * Guesses the HTTP MIME type based on a given file extension.
+ *
+ * Note: Has to include the beginning dot, e.g. ".mp3" (see IPRT).
+ *
+ * @returns Guessed MIME type, or "application/octet-stream" if not found.
+ * @param pszFileExt File extension to guess MIME type for.
+ */
+static const char *rtHttpServerGuessMIMEType(const char *pszFileExt)
+{
+ if (pszFileExt)
+ {
+ size_t i = 0;
+ while (s_aFileExtMIMEType[i++].pszExt) /* Slow, but does the job for now. */
+ {
+ if (!RTStrICmp(pszFileExt, s_aFileExtMIMEType[i].pszExt))
+ return s_aFileExtMIMEType[i].pszMIMEType;
+ }
+ }
+
+ return "application/octet-stream";
+}
+
+/**
+ * Initializes a HTTP body.
+ *
+ * @param pBody Body to initialize.
+ * @param cbSize Size of body (in bytes) to allocate. Optional and can be 0.
+ */
+static int rtHttpServerBodyInit(PRTHTTPBODY pBody, size_t cbSize)
+{
+ if (cbSize)
+ {
+ pBody->pvBody = RTMemAlloc(cbSize);
+ AssertPtrReturn(pBody->pvBody, VERR_NO_MEMORY);
+ pBody->cbBodyAlloc = cbSize;
+ }
+ else
+ {
+ pBody->pvBody = NULL;
+ pBody->cbBodyAlloc = 0;
+ }
+
+ pBody->cbBodyUsed = 0;
+ pBody->offBody = 0;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Destroys a HTTP body.
+ *
+ * @param pBody Body to destroy.
+ */
+static void rtHttpServerBodyDestroy(PRTHTTPBODY pBody)
+{
+ if (!pBody)
+ return;
+
+ if (pBody->pvBody)
+ {
+ Assert(pBody->cbBodyAlloc);
+
+ RTMemFree(pBody->pvBody);
+ pBody->pvBody = NULL;
+ }
+
+ pBody->cbBodyAlloc = 0;
+ pBody->cbBodyUsed = 0;
+ pBody->offBody = 0;
+}
+
+/**
+ * Allocates and initializes a new client request.
+ *
+ * @returns Pointer to the new client request, or NULL on OOM.
+ * Needs to be free'd with rtHttpServerReqFree().
+ */
+static PRTHTTPSERVERREQ rtHttpServerReqAlloc(void)
+{
+ PRTHTTPSERVERREQ pReq = (PRTHTTPSERVERREQ)RTMemAllocZ(sizeof(RTHTTPSERVERREQ));
+ AssertPtrReturn(pReq, NULL);
+
+ int rc2 = RTHttpHeaderListInit(&pReq->hHdrLst);
+ AssertRC(rc2);
+
+ rc2 = rtHttpServerBodyInit(&pReq->Body, 0 /* cbSize */);
+ AssertRC(rc2);
+
+ return pReq;
+}
+
+/**
+ * Frees a formerly allocated client request.
+ *
+ * @param pReq Pointer to client request to free.
+ * The pointer will be invalid on return.
+ */
+static void rtHttpServerReqFree(PRTHTTPSERVERREQ pReq)
+{
+ if (!pReq)
+ return;
+
+ RTStrFree(pReq->pszUrl);
+
+ RTHttpHeaderListDestroy(pReq->hHdrLst);
+
+ rtHttpServerBodyDestroy(&pReq->Body);
+
+ RTMemFree(pReq);
+ pReq = NULL;
+}
+
+/**
+ * Initializes a HTTP server response with an allocated body size.
+ *
+ * @returns VBox status code.
+ * @param pResp HTTP server response to initialize.
+ * @param cbBody Body size (in bytes) to allocate.
+ */
+RTR3DECL(int) RTHttpServerResponseInitEx(PRTHTTPSERVERRESP pResp, size_t cbBody)
+{
+ pResp->enmSts = RTHTTPSTATUS_INTERNAL_NOT_SET;
+
+ int rc = RTHttpHeaderListInit(&pResp->hHdrLst);
+ AssertRCReturn(rc, rc);
+
+ rc = rtHttpServerBodyInit(&pResp->Body, cbBody);
+
+ return rc;
+}
+
+/**
+ * Initializes a HTTP server response.
+ *
+ * @returns VBox status code.
+ * @param pResp HTTP server response to initialize.
+ */
+RTR3DECL(int) RTHttpServerResponseInit(PRTHTTPSERVERRESP pResp)
+{
+ return RTHttpServerResponseInitEx(pResp, 0 /* cbBody */);
+}
+
+/**
+ * Destroys a formerly initialized HTTP server response.
+ *
+ * @param pResp Pointer to HTTP server response to destroy.
+ */
+RTR3DECL(void) RTHttpServerResponseDestroy(PRTHTTPSERVERRESP pResp)
+{
+ if (!pResp)
+ return;
+
+ pResp->enmSts = RTHTTPSTATUS_INTERNAL_NOT_SET;
+
+ RTHttpHeaderListDestroy(pResp->hHdrLst);
+
+ rtHttpServerBodyDestroy(&pResp->Body);
+}
+
+
+/*********************************************************************************************************************************
+* Protocol Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Logs the HTTP protocol communication to the debug logger (2).
+ *
+ * @param pClient Client to log communication for.
+ * @param fWrite Whether the server is writing (to client) or reading (from client).
+ * @param pszData Actual protocol communication data to log.
+ */
+static void rtHttpServerLogProto(PRTHTTPSERVERCLIENT pClient, bool fWrite, const char *pszData)
+{
+ RT_NOREF(pClient);
+
+#ifdef LOG_ENABLED
+ if (!pszData) /* Nothing to log? Bail out. */
+ return;
+
+ char **ppapszStrings;
+ size_t cStrings;
+ int rc2 = RTStrSplit(pszData, strlen(pszData), RTHTTPSERVER_HTTP11_EOL_STR, &ppapszStrings, &cStrings);
+ if (RT_SUCCESS(rc2))
+ {
+ for (size_t i = 0; i < cStrings; i++)
+ {
+ Log2(("%s %s\n", fWrite ? ">" : "<", ppapszStrings[i]));
+ RTStrFree(ppapszStrings[i]);
+ }
+
+ RTMemFree(ppapszStrings);
+ }
+#else
+ RT_NOREF(fWrite, pszData);
+#endif
+}
+
+/**
+ * Writes HTTP protocol communication data to a connected client.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to write data to.
+ * @param pszData Data to write. Must be zero-terminated.
+ */
+static int rtHttpServerWriteProto(PRTHTTPSERVERCLIENT pClient, const char *pszData)
+{
+ rtHttpServerLogProto(pClient, true /* fWrite */, pszData);
+ return RTTcpWrite(pClient->hSocket, pszData, strlen(pszData));
+}
+
+/**
+ * Main function for sending a response back to the client.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to reply to.
+ * @param enmSts Status code to send.
+ */
+static int rtHttpServerSendResponse(PRTHTTPSERVERCLIENT pClient, RTHTTPSTATUS enmSts)
+{
+ char *pszResp;
+ int rc = RTStrAPrintf(&pszResp,
+ "%s %RU32 %s\r\n", RTHTTPVER_1_1_STR, enmSts, RTHttpStatusToStr(enmSts));
+ AssertRCReturn(rc, rc);
+ rc = rtHttpServerWriteProto(pClient, pszResp);
+ RTStrFree(pszResp);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Main function for sending response headers back to the client.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to reply to.
+ * @param pHdrLst Header list to send. Optional and can be NULL.
+ */
+static int rtHttpServerSendResponseHdrEx(PRTHTTPSERVERCLIENT pClient, PRTHTTPHEADERLIST pHdrLst)
+{
+ RTHTTPHEADERLIST HdrLst;
+ int rc = RTHttpHeaderListInit(&HdrLst);
+ AssertRCReturn(rc, rc);
+
+#ifdef DEBUG
+ /* Include a timestamp when running a debug build. */
+ RTTIMESPEC tsNow;
+ char szTS[64];
+ RTTimeSpecToString(RTTimeNow(&tsNow), szTS, sizeof(szTS));
+ rc = RTHttpHeaderListAdd(HdrLst, "Date", szTS, strlen(szTS), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCReturn(rc, rc);
+#endif
+
+ /* Note: Deliberately don't include the VBox version due to security reasons. */
+ rc = RTHttpHeaderListAdd(HdrLst, "Server", "Oracle VirtualBox", strlen("Oracle VirtualBox"), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCReturn(rc, rc);
+
+#ifdef IPRT_HTTP_WITH_WEBDAV
+ rc = RTHttpHeaderListAdd(HdrLst, "Allow", "GET, HEAD, PROPFIND", strlen("GET, HEAD, PROPFIND"), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCReturn(rc, rc);
+ rc = RTHttpHeaderListAdd(HdrLst, "DAV", "1", strlen("1"), RTHTTPHEADERLISTADD_F_BACK); /* Note: v1 is sufficient for read-only access. */
+ AssertRCReturn(rc, rc);
+#endif
+
+ char *pszHdr = NULL;
+
+ size_t i = 0;
+ const char *pszEntry;
+ while ((pszEntry = RTHttpHeaderListGetByOrdinal(HdrLst, i++)) != NULL)
+ {
+ rc = RTStrAAppend(&pszHdr, pszEntry);
+ AssertRCBreak(rc);
+ rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR);
+ AssertRCBreak(rc);
+ }
+
+ /* Append optional headers, if any. */
+ if (pHdrLst)
+ {
+ i = 0;
+ while ((pszEntry = RTHttpHeaderListGetByOrdinal(*pHdrLst, i++)) != NULL)
+ {
+ rc = RTStrAAppend(&pszHdr, pszEntry);
+ AssertRCBreak(rc);
+ rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR);
+ AssertRCBreak(rc);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Append trailing EOL. */
+ rc = RTStrAAppend(&pszHdr, RTHTTPSERVER_HTTP11_EOL_STR);
+ if (RT_SUCCESS(rc))
+ rc = rtHttpServerWriteProto(pClient, pszHdr);
+ }
+
+ RTStrFree(pszHdr);
+
+ RTHttpHeaderListDestroy(HdrLst);
+
+ LogFlowFunc(("rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Replies with (three digit) response status back to the client, extended version.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to reply to.
+ * @param enmSts Status code to send.
+ * @param pHdrLst Header list to send. Optional and can be NULL.
+ */
+static int rtHttpServerSendResponseEx(PRTHTTPSERVERCLIENT pClient, RTHTTPSTATUS enmSts, PRTHTTPHEADERLIST pHdrLst)
+{
+ int rc = rtHttpServerSendResponse(pClient, enmSts);
+ if (RT_SUCCESS(rc))
+ rc = rtHttpServerSendResponseHdrEx(pClient, pHdrLst);
+
+ return rc;
+}
+
+/**
+ * Replies with (three digit) response status back to the client.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to reply to.
+ * @param enmSts Status code to send.
+ */
+static int rtHttpServerSendResponseSimple(PRTHTTPSERVERCLIENT pClient, RTHTTPSTATUS enmSts)
+{
+ return rtHttpServerSendResponseEx(pClient, enmSts, NULL /* pHdrLst */);
+}
+
+/**
+ * Sends a chunk of the response body to the client.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to send body to.
+ * @param pvBuf Data buffer to send.
+ * @param cbBuf Size (in bytes) of data buffer to send.
+ * @param pcbSent Where to store the sent bytes. Optional and can be NULL.
+ */
+static int rtHttpServerSendResponseBody(PRTHTTPSERVERCLIENT pClient, void *pvBuf, size_t cbBuf, size_t *pcbSent)
+{
+ int rc = RTTcpWrite(pClient->hSocket, pvBuf, cbBuf);
+ if ( RT_SUCCESS(rc)
+ && pcbSent)
+ *pcbSent = cbBuf;
+
+ return rc;
+}
+
+/**
+ * Resolves a VBox status code to a HTTP status code.
+ *
+ * @returns Resolved HTTP status code, or RTHTTPSTATUS_INTERNALSERVERERROR if not able to resolve.
+ * @param rc VBox status code to resolve.
+ */
+static RTHTTPSTATUS rtHttpServerRcToStatus(int rc)
+{
+ switch (rc)
+ {
+ case VINF_SUCCESS: return RTHTTPSTATUS_OK;
+ case VERR_INVALID_PARAMETER: return RTHTTPSTATUS_BADREQUEST;
+ case VERR_INVALID_POINTER: return RTHTTPSTATUS_BADREQUEST;
+ case VERR_NOT_IMPLEMENTED: return RTHTTPSTATUS_NOTIMPLEMENTED;
+ case VERR_NOT_SUPPORTED: return RTHTTPSTATUS_NOTIMPLEMENTED;
+ case VERR_PATH_NOT_FOUND: return RTHTTPSTATUS_NOTFOUND;
+ case VERR_FILE_NOT_FOUND: return RTHTTPSTATUS_NOTFOUND;
+ case VERR_IS_A_DIRECTORY: return RTHTTPSTATUS_FORBIDDEN;
+ case VERR_NOT_FOUND: return RTHTTPSTATUS_NOTFOUND;
+ default:
+ break;
+ }
+
+ AssertMsgFailed(("rc=%Rrc not handled for HTTP status\n", rc));
+ return RTHTTPSTATUS_INTERNALSERVERERROR;
+}
+
+
+/*********************************************************************************************************************************
+* Command Protocol Handlers *
+*********************************************************************************************************************************/
+
+/**
+ * Handler for the GET method.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to handle GET method for.
+ * @param pReq Client request to handle.
+ */
+static DECLCALLBACK(int) rtHttpServerHandleGET(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
+{
+ LogFlowFuncEnter();
+
+ int rc = VINF_SUCCESS;
+
+ /* If a low-level GET request handler is defined, call it and return. */
+ RTHTTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnGetRequest, pReq);
+
+ RTFSOBJINFO fsObj;
+ RT_ZERO(fsObj); /* Shut up MSVC. */
+
+ char *pszMIMEHint = NULL;
+
+ RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnQueryInfo, pReq, &fsObj, &pszMIMEHint);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ void *pvHandle = NULL;
+ RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnOpen, pReq, &pvHandle);
+
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbBuf = _64K;
+ void *pvBuf = RTMemAlloc(cbBuf);
+ AssertPtrReturn(pvBuf, VERR_NO_MEMORY);
+
+ for (;;)
+ {
+ RTHTTPHEADERLIST HdrLst;
+ rc = RTHttpHeaderListInit(&HdrLst);
+ AssertRCReturn(rc, rc);
+
+ char szVal[16];
+
+ /* Note: For directories fsObj.cbObject contains the actual size (in bytes)
+ * of the body data for the directory listing. */
+
+ ssize_t cch = RTStrPrintf2(szVal, sizeof(szVal), "%RU64", fsObj.cbObject);
+ AssertBreakStmt(cch, VERR_BUFFER_OVERFLOW);
+ rc = RTHttpHeaderListAdd(HdrLst, "Content-Length", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCBreak(rc);
+
+ cch = RTStrPrintf2(szVal, sizeof(szVal), "identity");
+ AssertBreakStmt(cch, VERR_BUFFER_OVERFLOW);
+ rc = RTHttpHeaderListAdd(HdrLst, "Content-Encoding", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCBreak(rc);
+
+ if (pszMIMEHint == NULL)
+ {
+ const char *pszMIME = rtHttpServerGuessMIMEType(RTPathSuffix(pReq->pszUrl));
+ rc = RTHttpHeaderListAdd(HdrLst, "Content-Type", pszMIME, strlen(pszMIME), RTHTTPHEADERLISTADD_F_BACK);
+ }
+ else
+ {
+ rc = RTHttpHeaderListAdd(HdrLst, "Content-Type", pszMIMEHint, strlen(pszMIMEHint), RTHTTPHEADERLISTADD_F_BACK);
+ RTStrFree(pszMIMEHint);
+ pszMIMEHint = NULL;
+ }
+ AssertRCBreak(rc);
+
+ if (pClient->State.msKeepAlive)
+ {
+ /* If the client requested to keep alive the connection,
+ * always override this with 30s and report this back to the client. */
+ pClient->State.msKeepAlive = RT_MS_30SEC; /** @todo Make this configurable. */
+#ifdef DEBUG_andy
+ pClient->State.msKeepAlive = 5000;
+#endif
+ cch = RTStrPrintf2(szVal, sizeof(szVal), "timeout=%RU64", pClient->State.msKeepAlive / RT_MS_1SEC); /** @todo No pipelining support here yet. */
+ AssertBreakStmt(cch, VERR_BUFFER_OVERFLOW);
+ rc = RTHttpHeaderListAdd(HdrLst, "Keep-Alive", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCReturn(rc, rc);
+ }
+
+ rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_OK, &HdrLst);
+
+ RTHttpHeaderListDestroy(HdrLst);
+
+ if (rc == VERR_BROKEN_PIPE) /* Could happen on fast reloads. */
+ break;
+ AssertRCReturn(rc, rc);
+
+ size_t cbToRead = fsObj.cbObject;
+ size_t cbRead = 0; /* Shut up GCC. */
+ size_t cbWritten = 0; /* Ditto. */
+ while (cbToRead)
+ {
+ RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRead, pvHandle, pvBuf, RT_MIN(cbBuf, cbToRead), &cbRead);
+ if (RT_FAILURE(rc))
+ break;
+ rc = rtHttpServerSendResponseBody(pClient, pvBuf, cbRead, &cbWritten);
+ AssertBreak(cbToRead >= cbWritten);
+ cbToRead -= cbWritten;
+ if (rc == VERR_NET_CONNECTION_RESET_BY_PEER) /* Clients often apruptly abort the connection when done. */
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ AssertRCBreak(rc);
+ }
+
+ break;
+ } /* for (;;) */
+
+ RTMemFree(pvBuf);
+
+ int rc2 = rc; /* Save rc. */
+
+ RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnClose, pvHandle);
+
+ if (RT_FAILURE(rc2)) /* Restore original rc on failure. */
+ rc = rc2;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Handler for the HEAD method.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to handle HEAD method for.
+ * @param pReq Client request to handle.
+ */
+static DECLCALLBACK(int) rtHttpServerHandleHEAD(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
+{
+ LogFlowFuncEnter();
+
+ /* If a low-level HEAD request handler is defined, call it and return. */
+ RTHTTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnHeadRequest, pReq);
+
+ int rc = VINF_SUCCESS;
+
+ RTFSOBJINFO fsObj;
+ RT_ZERO(fsObj); /* Shut up MSVC. */
+
+ RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnQueryInfo, pReq, &fsObj, NULL /* pszMIMEHint */);
+ if (RT_SUCCESS(rc))
+ {
+ RTHTTPHEADERLIST HdrLst;
+ rc = RTHttpHeaderListInit(&HdrLst);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Note: A response to a HEAD request does not have a body.
+ * All entity headers below are assumed to describe the the response a similar GET
+ * request would return (but then with a body).
+ */
+ char szVal[16];
+
+ ssize_t cch = RTStrPrintf2(szVal, sizeof(szVal), "%RU64", fsObj.cbObject);
+ AssertReturn(cch, VERR_BUFFER_OVERFLOW);
+ rc = RTHttpHeaderListAdd(HdrLst, "Content-Length", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCReturn(rc, rc);
+
+ cch = RTStrPrintf2(szVal, sizeof(szVal), "identity");
+ AssertReturn(cch, VERR_BUFFER_OVERFLOW);
+ rc = RTHttpHeaderListAdd(HdrLst, "Content-Encoding", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCReturn(rc, rc);
+
+ const char *pszMIME = rtHttpServerGuessMIMEType(RTPathSuffix(pReq->pszUrl));
+ rc = RTHttpHeaderListAdd(HdrLst, "Content-Type", pszMIME, strlen(pszMIME), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCReturn(rc, rc);
+
+ rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_OK, &HdrLst);
+ AssertRCReturn(rc, rc);
+
+ RTHttpHeaderListDestroy(HdrLst);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+#ifdef IPRT_HTTP_WITH_WEBDAV
+/**
+ * Handler for the OPTIONS method.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to handle OPTIONS method for.
+ * @param pReq Client request to handle.
+ */
+static DECLCALLBACK(int) rtHttpServerHandleOPTIONS(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
+{
+ LogFlowFuncEnter();
+
+ RT_NOREF(pReq);
+
+ int rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_OK, NULL /* pHdrLst */);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Handler for the PROPFIND (WebDAV) method.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to handle PROPFIND method for.
+ * @param pReq Client request to handle.
+ */
+static DECLCALLBACK(int) rtHttpServerHandlePROPFIND(PRTHTTPSERVERCLIENT pClient, PRTHTTPSERVERREQ pReq)
+{
+ LogFlowFuncEnter();
+
+ int rc = VINF_SUCCESS;
+
+ /* If a low-level GET request handler is defined, call it and return. */
+ RTHTTPSERVER_HANDLE_CALLBACK_VA_RET(pfnOnGetRequest, pReq);
+
+ RTFSOBJINFO fsObj;
+ RT_ZERO(fsObj); /* Shut up MSVC. */
+
+ RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnQueryInfo, pReq, &fsObj, NULL /* pszMIMEHint */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ void *pvHandle = NULL;
+ RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnOpen, pReq, &pvHandle);
+
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbBuf = _64K;
+ void *pvBuf = RTMemAlloc(cbBuf);
+ AssertPtrReturn(pvBuf, VERR_NO_MEMORY);
+
+ for (;;)
+ {
+ RTHTTPHEADERLIST HdrLst;
+ rc = RTHttpHeaderListInit(&HdrLst);
+ AssertRCReturn(rc, rc);
+
+ char szVal[16];
+
+ rc = RTHttpHeaderListAdd(HdrLst, "Content-Type", "text/xml; charset=utf-8", strlen("text/xml; charset=utf-8"), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCBreak(rc);
+
+ /* Note: For directories fsObj.cbObject contains the actual size (in bytes)
+ * of the body data for the directory listing. */
+
+ ssize_t cch = RTStrPrintf2(szVal, sizeof(szVal), "%RU64", fsObj.cbObject);
+ AssertBreakStmt(cch, VERR_BUFFER_OVERFLOW);
+ rc = RTHttpHeaderListAdd(HdrLst, "Content-Length", szVal, strlen(szVal), RTHTTPHEADERLISTADD_F_BACK);
+ AssertRCBreak(rc);
+
+ rc = rtHttpServerSendResponseEx(pClient, RTHTTPSTATUS_MULTISTATUS, &HdrLst);
+ AssertRCReturn(rc, rc);
+
+ RTHttpHeaderListDestroy(HdrLst);
+
+ size_t cbToRead = fsObj.cbObject;
+ size_t cbRead = 0; /* Shut up GCC. */
+ size_t cbWritten = 0; /* Ditto. */
+ while (cbToRead)
+ {
+ RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnRead, pvHandle, pvBuf, RT_MIN(cbBuf, cbToRead), &cbRead);
+ if (RT_FAILURE(rc))
+ break;
+ //rtHttpServerLogProto(pClient, true /* fWrite */, (const char *)pvBuf);
+ rc = rtHttpServerSendResponseBody(pClient, pvBuf, cbRead, &cbWritten);
+ AssertBreak(cbToRead >= cbWritten);
+ cbToRead -= cbWritten;
+ if (rc == VERR_NET_CONNECTION_RESET_BY_PEER) /* Clients often apruptly abort the connection when done. */
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ AssertRCBreak(rc);
+ }
+
+ break;
+ } /* for (;;) */
+
+ RTMemFree(pvBuf);
+
+ int rc2 = rc; /* Save rc. */
+
+ RTHTTPSERVER_HANDLE_CALLBACK_VA(pfnClose, pvHandle);
+
+ if (RT_FAILURE(rc2)) /* Restore original rc on failure. */
+ rc = rc2;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+#endif /* IPRT_HTTP_WITH_WEBDAV */
+
+/**
+ * Validates if a given path is valid or not.
+ *
+ * @returns \c true if path is valid, or \c false if not.
+ * @param pszPath Path to check.
+ * @param fIsAbsolute Whether the path to check is an absolute path or not.
+ */
+static bool rtHttpServerPathIsValid(const char *pszPath, bool fIsAbsolute)
+{
+ if (!pszPath)
+ return false;
+
+ bool fIsValid = strlen(pszPath)
+ && RTStrIsValidEncoding(pszPath)
+ && RTStrStr(pszPath, "..") == NULL; /** @todo Very crude for now -- improve this. */
+ if ( fIsValid
+ && fIsAbsolute)
+ {
+ RTFSOBJINFO objInfo;
+ int rc2 = RTPathQueryInfo(pszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc2))
+ {
+ fIsValid = RTFS_IS_DIRECTORY(objInfo.Attr.fMode)
+ || RTFS_IS_FILE(objInfo.Attr.fMode);
+
+ /* No symlinks and other stuff not allowed. */
+ }
+ else
+ fIsValid = false;
+ }
+
+ LogFlowFunc(("pszPath=%s -> %RTbool\n", pszPath, fIsValid));
+ return fIsValid;
+
+}
+
+/**
+ * Parses headers and sets (replaces) a given header list.
+ *
+ * @returns VBox status code.
+ * @param hList Header list to fill parsed headers in.
+ * @param cStrings Number of strings to parse for \a papszStrings.
+ * @param papszStrings Array of strings to parse.
+ */
+static int rtHttpServerParseHeaders(RTHTTPHEADERLIST hList, size_t cStrings, char **papszStrings)
+{
+ /* Nothing to parse left? Bail out early. */
+ if ( !cStrings
+ || !papszStrings)
+ return VINF_SUCCESS;
+
+#ifdef LOG_ENABLED
+ for (size_t i = 0; i < cStrings; i++)
+ LogFlowFunc(("Header: %s\n", papszStrings[i]));
+#endif
+
+ int rc = RTHttpHeaderListSet(hList, cStrings, papszStrings);
+
+ LogFlowFunc(("rc=%Rrc, cHeaders=%zu\n", rc, cStrings));
+ return rc;
+}
+
+/**
+ * Main function for parsing and allocating a client request.
+ *
+ * See: https://tools.ietf.org/html/rfc2616#section-2.2
+ *
+ * @returns VBox status code.
+ * @param pClient Client to parse request from.
+ * @param pszReq Request string with headers to parse.
+ * @param cbReq Size (in bytes) of request string to parse.
+ * @param ppReq Where to store the allocated client request on success.
+ * Needs to be free'd via rtHttpServerReqFree().
+ */
+static int rtHttpServerParseRequest(PRTHTTPSERVERCLIENT pClient, const char *pszReq, size_t cbReq,
+ PRTHTTPSERVERREQ *ppReq)
+{
+ RT_NOREF(pClient);
+
+ AssertPtrReturn(pszReq, VERR_INVALID_POINTER);
+ AssertReturn(cbReq, VERR_INVALID_PARAMETER);
+
+ /* We only support UTF-8 charset for now. */
+ AssertReturn(RTStrIsValidEncoding(pszReq), VERR_INVALID_PARAMETER);
+
+ char **ppapszReq = NULL;
+ size_t cReq = 0;
+ int rc = RTStrSplit(pszReq, cbReq, RTHTTPSERVER_HTTP11_EOL_STR, &ppapszReq, &cReq);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (!cReq)
+ return VERR_INVALID_PARAMETER;
+
+#ifdef LOG_ENABLED
+ for (size_t i = 0; i < cReq; i++)
+ LogFlowFunc(("%s\n", ppapszReq[i]));
+#endif
+
+ PRTHTTPSERVERREQ pReq = NULL;
+
+ char **ppapszFirstLine = NULL;
+ size_t cFirstLine = 0;
+ rc = RTStrSplit(ppapszReq[0], strlen(ppapszReq[0]), " ", &ppapszFirstLine, &cFirstLine);
+ if (RT_SUCCESS(rc))
+ {
+ if (cFirstLine < 3) /* At leat the method, path and version has to be present. */
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ while (RT_SUCCESS(rc)) /* To use break. */
+ {
+ pReq = rtHttpServerReqAlloc();
+ AssertPtrBreakStmt(pReq, rc = VERR_NO_MEMORY);
+
+ /*
+ * Parse method to use. Method names are case sensitive.
+ */
+ const char *pszMethod = ppapszFirstLine[0];
+ if (!RTStrCmp(pszMethod, "GET")) pReq->enmMethod = RTHTTPMETHOD_GET;
+ else if (!RTStrCmp(pszMethod, "HEAD")) pReq->enmMethod = RTHTTPMETHOD_HEAD;
+#ifdef IPRT_HTTP_WITH_WEBDAV
+ else if (!RTStrCmp(pszMethod, "OPTIONS")) pReq->enmMethod = RTHTTPMETHOD_OPTIONS;
+ else if (!RTStrCmp(pszMethod, "PROPFIND")) pReq->enmMethod = RTHTTPMETHOD_PROPFIND;
+#endif
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ /*
+ * Parse requested path.
+ */
+ /** @todo Do URL unescaping here. */
+ const char *pszPath = ppapszFirstLine[1];
+ if (!rtHttpServerPathIsValid(pszPath, false /* fIsAbsolute */))
+ {
+ rc = VERR_PATH_NOT_FOUND;
+ break;
+ }
+
+ pReq->pszUrl = RTStrDup(pszPath);
+
+ /*
+ * Parse HTTP version to use.
+ * We're picky heree: Only HTTP 1.1 is supported by now.
+ */
+ const char *pszVer = ppapszFirstLine[2];
+ if (RTStrCmp(pszVer, RTHTTPVER_1_1_STR)) /** @todo Use RTStrVersionCompare. Later. */
+ {
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ /** @todo Anything else needed for the first line here? */
+
+ /*
+ * Process headers, if any.
+ */
+ if (cReq > 1)
+ {
+ rc = rtHttpServerParseHeaders(pReq->hHdrLst, cReq - 1, &ppapszReq[1]);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTHttpHeaderListGet(pReq->hHdrLst, "Connection", RTSTR_MAX))
+ pClient->State.msKeepAlive = RT_MS_30SEC; /** @todo Insert the real value here. */
+ }
+ }
+ break;
+ } /* for (;;) */
+
+ /*
+ * Cleanup.
+ */
+
+ for (size_t i = 0; i < cFirstLine; i++)
+ RTStrFree(ppapszFirstLine[i]);
+ RTMemFree(ppapszFirstLine);
+
+ for (size_t i = 0; i < cReq; i++)
+ RTStrFree(ppapszReq[i]);
+ RTMemFree(ppapszReq);
+
+ if (RT_FAILURE(rc))
+ {
+ rtHttpServerReqFree(pReq);
+ pReq = NULL;
+ }
+ else
+ *ppReq = pReq;
+
+ return rc;
+}
+
+/**
+ * Main function for processing client requests.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to process request for.
+ * @param pszReq Request string to parse and handle.
+ * @param cbReq Size (in bytes) of request string.
+ */
+static int rtHttpServerProcessRequest(PRTHTTPSERVERCLIENT pClient, char *pszReq, size_t cbReq)
+{
+ RTHTTPSTATUS enmSts = RTHTTPSTATUS_INTERNAL_NOT_SET;
+
+ PRTHTTPSERVERREQ pReq = NULL; /* Shut up GCC. */
+ int rc = rtHttpServerParseRequest(pClient, pszReq, cbReq, &pReq);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("Request %s %s\n", RTHttpMethodToStr(pReq->enmMethod), pReq->pszUrl));
+
+ unsigned i = 0;
+ for (; i < RT_ELEMENTS(g_aMethodMap); i++)
+ {
+ const RTHTTPSERVERMETHOD_ENTRY *pMethodEntry = &g_aMethodMap[i];
+ if (pReq->enmMethod == pMethodEntry->enmMethod)
+ {
+ /* Hand in request to method handler. */
+ int rcMethod = pMethodEntry->pfnMethod(pClient, pReq);
+ if (RT_FAILURE(rcMethod))
+ LogFunc(("Request %s %s failed with %Rrc\n", RTHttpMethodToStr(pReq->enmMethod), pReq->pszUrl, rcMethod));
+
+ enmSts = rtHttpServerRcToStatus(rcMethod);
+ break;
+ }
+ }
+
+ if (i == RT_ELEMENTS(g_aMethodMap))
+ enmSts = RTHTTPSTATUS_NOTIMPLEMENTED;
+
+ rtHttpServerReqFree(pReq);
+ }
+ else
+ enmSts = RTHTTPSTATUS_BADREQUEST;
+
+ if (enmSts != RTHTTPSTATUS_INTERNAL_NOT_SET)
+ {
+ int rc2 = rtHttpServerSendResponseSimple(pClient, enmSts);
+ if (RT_SUCCESS(rc))
+ rc = rc2;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Main loop for processing client requests.
+ *
+ * @returns VBox status code.
+ * @param pClient Client to process requests for.
+ */
+static int rtHttpServerClientMain(PRTHTTPSERVERCLIENT pClient)
+{
+ int rc;
+
+ char szReq[RTHTTPSERVER_MAX_REQ_LEN + 1];
+
+ LogFlowFunc(("Client connected\n"));
+
+ /* Initialize client state. */
+ pClient->State.msKeepAlive = 0;
+
+ RTMSINTERVAL cWaitMs = RT_INDEFINITE_WAIT; /* The first wait always waits indefinitely. */
+ uint64_t tsLastReadMs = 0;
+
+ for (;;)
+ {
+ rc = RTTcpSelectOne(pClient->hSocket, cWaitMs);
+ if (RT_FAILURE(rc))
+ {
+ LogFlowFunc(("RTTcpSelectOne=%Rrc (cWaitMs=%RU64)\n", rc, cWaitMs));
+ if (rc == VERR_TIMEOUT)
+ {
+ if (pClient->State.msKeepAlive) /* Keep alive handling needed? */
+ {
+ if (!tsLastReadMs)
+ tsLastReadMs = RTTimeMilliTS();
+ const uint64_t tsDeltaMs = pClient->State.msKeepAlive - (RTTimeMilliTS() - tsLastReadMs);
+ LogFlowFunc(("tsLastReadMs=%RU64, tsDeltaMs=%RU64\n", tsLastReadMs, tsDeltaMs));
+ Log3Func(("Keep alive active (%RU32ms): %RU64ms remaining\n", pClient->State.msKeepAlive, tsDeltaMs));
+ if ( tsDeltaMs > cWaitMs
+ && tsDeltaMs < pClient->State.msKeepAlive)
+ continue;
+
+ LogFunc(("Keep alive active: Client did not respond within %RU32ms, closing\n", pClient->State.msKeepAlive));
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+
+ break;
+ }
+
+ LogFlowFunc(("Reading client request ...\n"));
+
+ tsLastReadMs = RTTimeMilliTS();
+ cWaitMs = 200; /* All consequtive waits do busy waiting for now. */
+
+ char *pszReq = szReq;
+ size_t cbRead;
+ size_t cbToRead = sizeof(szReq);
+ size_t cbReadTotal = 0;
+
+ do
+ {
+ rc = RTTcpReadNB(pClient->hSocket, pszReq, cbToRead, &cbRead);
+ if (RT_FAILURE(rc))
+ break;
+
+ if (!cbRead)
+ break;
+
+ /* Make sure to terminate string read so far. */
+ pszReq[cbRead] = '\0';
+
+ /* End of request reached? */
+ /** @todo BUGBUG Improve this. */
+ char *pszEOR = RTStrStr(&pszReq[cbReadTotal], "\r\n\r\n");
+ if (pszEOR)
+ {
+ cbReadTotal = pszEOR - pszReq;
+ *pszEOR = '\0';
+ break;
+ }
+
+ AssertBreak(cbToRead >= cbRead);
+ cbToRead -= cbRead;
+ cbReadTotal += cbRead;
+ AssertBreak(cbReadTotal <= sizeof(szReq));
+ pszReq += cbRead;
+
+ } while (cbToRead);
+
+ if ( RT_SUCCESS(rc)
+ && cbReadTotal)
+ {
+ LogFlowFunc(("Received client request (%zu bytes)\n", cbReadTotal));
+
+ rtHttpServerLogProto(pClient, false /* fWrite */, szReq);
+
+ rc = rtHttpServerProcessRequest(pClient, szReq, cbReadTotal);
+ }
+ else
+ break;
+
+ } /* for */
+
+ if (RT_FAILURE(rc))
+ {
+ switch (rc)
+ {
+ case VERR_NET_CONNECTION_RESET_BY_PEER:
+ {
+ LogFunc(("Client closed the connection\n"));
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ default:
+ LogFunc(("Client processing failed with %Rrc\n", rc));
+ break;
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Per-client thread for serving the server's control connection.
+ *
+ * @returns VBox status code.
+ * @param hSocket Socket handle to use for the control connection.
+ * @param pvUser User-provided arguments. Of type PRTHTTPSERVERINTERNAL.
+ */
+static DECLCALLBACK(int) rtHttpServerClientThread(RTSOCKET hSocket, void *pvUser)
+{
+ PRTHTTPSERVERINTERNAL pThis = (PRTHTTPSERVERINTERNAL)pvUser;
+ RTHTTPSERVER_VALID_RETURN(pThis);
+
+ LogFlowFuncEnter();
+
+ RTHTTPSERVERCLIENT Client;
+ RT_ZERO(Client);
+
+ Client.pServer = pThis;
+ Client.hSocket = hSocket;
+
+ return rtHttpServerClientMain(&Client);
+}
+
+RTR3DECL(int) RTHttpServerCreate(PRTHTTPSERVER hHttpServer, const char *pszAddress, uint16_t uPort,
+ PRTHTTPSERVERCALLBACKS pCallbacks, void *pvUser, size_t cbUser)
+{
+ AssertPtrReturn(hHttpServer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
+ AssertReturn (uPort, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pCallbacks, VERR_INVALID_POINTER);
+ /* pvUser is optional. */
+
+ int rc;
+
+ PRTHTTPSERVERINTERNAL pThis = (PRTHTTPSERVERINTERNAL)RTMemAllocZ(sizeof(RTHTTPSERVERINTERNAL));
+ if (pThis)
+ {
+ pThis->u32Magic = RTHTTPSERVER_MAGIC;
+ pThis->Callbacks = *pCallbacks;
+ pThis->pvUser = pvUser;
+ pThis->cbUser = cbUser;
+
+ rc = RTTcpServerCreate(pszAddress, uPort, RTTHREADTYPE_DEFAULT, "httpsrv",
+ rtHttpServerClientThread, pThis /* pvUser */, &pThis->pTCPServer);
+ if (RT_SUCCESS(rc))
+ {
+ *hHttpServer = (RTHTTPSERVER)pThis;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+RTR3DECL(int) RTHttpServerDestroy(RTHTTPSERVER hHttpServer)
+{
+ if (hHttpServer == NIL_RTHTTPSERVER)
+ return VINF_SUCCESS;
+
+ PRTHTTPSERVERINTERNAL pThis = hHttpServer;
+ RTHTTPSERVER_VALID_RETURN(pThis);
+
+ AssertPtr(pThis->pTCPServer);
+
+ int rc = VINF_SUCCESS;
+
+ PRTHTTPSERVERCALLBACKS pCallbacks = &pThis->Callbacks;
+ if (pCallbacks->pfnDestroy)
+ {
+ RTHTTPCALLBACKDATA Data = { NULL /* pClient */, pThis->pvUser, pThis->cbUser };
+ rc = pCallbacks->pfnDestroy(&Data);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTTcpServerDestroy(pThis->pTCPServer);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32Magic = RTHTTPSERVER_MAGIC_DEAD;
+
+ RTMemFree(pThis);
+ }
+ }
+
+ return rc;
+}
diff --git a/src/VBox/Runtime/r3/init-data.cpp b/src/VBox/Runtime/r3/init-data.cpp
new file mode 100644
index 00000000..8cc2cb09
--- /dev/null
+++ b/src/VBox/Runtime/r3/init-data.cpp
@@ -0,0 +1,63 @@
+/* $Id: init-data.cpp $ */
+/** @file
+ * IPRT - Init Data Ring-3.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include "internal/initterm.h"
+#include "internal/time.h" /* g_u64ProgramStartNanoTS */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The number of calls to RTR3Init*. */
+DECL_HIDDEN_DATA(int32_t volatile) g_crtR3Users = 0;
+/** Whether we're currently initializing the IPRT. */
+DECL_HIDDEN_DATA(bool volatile) g_frtR3Initializing = false;
+/**
+ * Set if the atexit callback has been called, i.e. indicating
+ * that the process is terminating.
+ */
+DECL_HIDDEN_DATA(bool volatile) g_frtAtExitCalled = false;
+
+/**
+ * Program start nanosecond TS.
+ */
+DECL_HIDDEN_DATA(uint64_t) g_u64ProgramStartNanoTS = 0;
+
diff --git a/src/VBox/Runtime/r3/init.cpp b/src/VBox/Runtime/r3/init.cpp
new file mode 100644
index 00000000..e10ba6b4
--- /dev/null
+++ b/src/VBox/Runtime/r3/init.cpp
@@ -0,0 +1,666 @@
+/* $Id: init.cpp $ */
+/** @file
+ * IPRT - Init Ring-3.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/types.h> /* darwin: UINT32_C and others. */
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+#else
+# include <unistd.h>
+# ifndef RT_OS_OS2
+# include <pthread.h>
+# include <signal.h>
+# include <errno.h>
+# define IPRT_USE_SIG_CHILD_DUMMY
+# endif
+#endif
+#ifdef RT_OS_OS2
+# include <InnoTekLIBC/fork.h>
+# define INCL_DOSMISC
+# include <os2.h>
+#endif
+#ifndef IPRT_NO_CRT
+# include <locale.h>
+#endif
+
+#include <iprt/initterm.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/time.h>
+#include <iprt/string.h>
+#include <iprt/param.h>
+#ifdef RT_OS_WINDOWS
+# include <iprt/getopt.h>
+# include <iprt/utf16.h>
+#endif
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+# include <iprt/file.h>
+# include <VBox/sup.h>
+#endif
+#include <stdlib.h>
+
+#include "init.h"
+#include "internal/alignmentchecks.h"
+#include "internal/initterm.h"
+#include "internal/path.h"
+#include "internal/process.h"
+#include "internal/thread.h"
+#include "internal/time.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The IPRT init flags. */
+static uint32_t g_fInitFlags;
+
+/** The argument count of the program. */
+static int g_crtArgs = -1;
+/** The arguments of the program (UTF-8). This is "leaked". */
+static char ** g_papszrtArgs;
+/** The original argument vector of the program. */
+static char ** g_papszrtOrgArgs;
+
+#ifdef IPRT_WITH_ALIGNMENT_CHECKS
+/**
+ * Whether alignment checks are enabled.
+ * This is set if the environment variable IPRT_ALIGNMENT_CHECKS is 1.
+ */
+RTDATADECL(bool) g_fRTAlignmentChecks = false;
+#endif
+
+
+#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_HAIKU) \
+ || defined(RT_OS_LINUX) || defined(RT_OS_OS2) || defined(RT_OS_SOLARIS) /** @todo add host init hooks everywhere. */
+/* Stubs */
+DECLHIDDEN(int) rtR3InitNativeFirst(uint32_t fFlags) { RT_NOREF_PV(fFlags); return VINF_SUCCESS; }
+DECLHIDDEN(int) rtR3InitNativeFinal(uint32_t fFlags) { RT_NOREF_PV(fFlags); return VINF_SUCCESS; }
+DECLHIDDEN(void) rtR3InitNativeObtrusive(uint32_t fFlags) { RT_NOREF_PV(fFlags); }
+#endif
+
+
+/**
+ * atexit callback.
+ *
+ * This makes sure any loggers are flushed and will later also work the
+ * termination callback chain.
+ */
+static void rtR3ExitCallback(void) RT_NOTHROW_DEF
+{
+ ASMAtomicWriteBool(&g_frtAtExitCalled, true);
+
+ if (g_crtR3Users > 0)
+ {
+ PRTLOGGER pLogger = RTLogGetDefaultInstance();
+ if (pLogger)
+ RTLogFlush(pLogger);
+
+ pLogger = RTLogRelGetDefaultInstance();
+ if (pLogger)
+ RTLogFlush(pLogger);
+ }
+}
+
+
+#ifndef RT_OS_WINDOWS
+/**
+ * Fork callback, child context.
+ */
+static void rtR3ForkChildCallback(void)
+{
+ g_ProcessSelf = getpid();
+}
+#endif /* RT_OS_WINDOWS */
+
+#ifdef RT_OS_OS2
+/** Fork completion callback for OS/2. Only called in the child. */
+static void rtR3ForkOs2ChildCompletionCallback(void *pvArg, int rc, __LIBC_FORKCTX enmCtx)
+{
+ Assert(enmCtx == __LIBC_FORK_CTX_CHILD); NOREF(enmCtx);
+ NOREF(pvArg);
+
+ if (!rc)
+ rtR3ForkChildCallback();
+}
+
+/** Low-level fork callback for OS/2. */
+int rtR3ForkOs2Child(__LIBC_PFORKHANDLE pForkHandle, __LIBC_FORKOP enmOperation)
+{
+ if (enmOperation == __LIBC_FORK_OP_EXEC_CHILD)
+ return pForkHandle->pfnCompletionCallback(pForkHandle, rtR3ForkOs2ChildCompletionCallback, NULL, __LIBC_FORK_CTX_CHILD);
+ return 0;
+}
+
+# define static static volatile /** @todo _FORK_CHILD1 causes unresolved externals in optimized builds. Fix macro. */
+_FORK_CHILD1(0, rtR3ForkOs2Child);
+# undef static
+#endif /* RT_OS_OS2 */
+
+
+
+/**
+ * Internal worker which initializes or re-initializes the
+ * program path, name and directory globals.
+ *
+ * @returns IPRT status code.
+ * @param pszProgramPath The program path, NULL if not specified.
+ */
+static int rtR3InitProgramPath(const char *pszProgramPath)
+{
+ /*
+ * We're reserving 32 bytes here for file names as what not.
+ */
+ if (!pszProgramPath)
+ {
+ int rc = rtProcInitExePath(g_szrtProcExePath, sizeof(g_szrtProcExePath) - 32);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ else
+ {
+ size_t cch = strlen(pszProgramPath);
+ Assert(cch > 1);
+ AssertMsgReturn(cch < sizeof(g_szrtProcExePath) - 32, ("%zu\n", cch), VERR_BUFFER_OVERFLOW);
+ memcpy(g_szrtProcExePath, pszProgramPath, cch + 1);
+ }
+
+ /*
+ * Parse the name.
+ */
+ ssize_t offName;
+ g_cchrtProcExePath = RTPathParseSimple(g_szrtProcExePath, &g_cchrtProcExeDir, &offName, NULL);
+ g_offrtProcName = offName;
+ return VINF_SUCCESS;
+}
+
+
+#ifdef RT_OS_WINDOWS
+/**
+ * Checks the two argument vectors contains the same strings.
+ */
+DECLINLINE(bool) rtR3InitArgvEquals(int cArgs, char **papszArgs1, char **papszArgs2)
+{
+ if (papszArgs1 != papszArgs2)
+ while (cArgs-- > 0)
+ if (strcmp(papszArgs1[cArgs], papszArgs2[cArgs]) != 0)
+ return false;
+ return true;
+}
+#endif
+
+
+/**
+ * Internal worker which initializes or re-initializes the
+ * program path, name and directory globals.
+ *
+ * @returns IPRT status code.
+ * @param fFlags Flags, see RTR3INIT_XXX.
+ * @param cArgs Pointer to the argument count.
+ * @param ppapszArgs Pointer to the argument vector pointer. NULL
+ * allowed if @a cArgs is 0.
+ */
+static int rtR3InitArgv(uint32_t fFlags, int cArgs, char ***ppapszArgs)
+{
+ NOREF(fFlags);
+ if (cArgs)
+ {
+ AssertPtr(ppapszArgs);
+ AssertPtr(*ppapszArgs);
+ char **papszOrgArgs = *ppapszArgs;
+
+ /*
+ * Normally we should only be asked to convert arguments once. If we
+ * are though, it should be the already convered arguments.
+ */
+ if (g_crtArgs != -1)
+ {
+ AssertReturn( g_crtArgs == cArgs
+ && g_papszrtArgs == papszOrgArgs,
+ VERR_WRONG_ORDER); /* only init once! */
+ return VINF_SUCCESS;
+ }
+
+#if !defined(IPRT_NO_CRT) || !defined(RT_OS_WINDOWS)
+ if (!(fFlags & RTR3INIT_FLAGS_UTF8_ARGV))
+ {
+ /*
+ * Convert the arguments.
+ */
+ char **papszArgs;
+
+# ifdef RT_OS_WINDOWS
+ /* HACK ALERT! Try convert from unicode versions if possible.
+ Unfortunately for us, __wargv is only initialized if we have a unicode
+ main function. So, use getoptarv.cpp code to do the conversions and
+ hope it gives us the same result. (CommandLineToArgvW was not in NT 3.1.) */
+ if ( cArgs == __argc
+ && rtR3InitArgvEquals(cArgs, papszOrgArgs, __argv))
+ {
+ char *pszCmdLine = NULL;
+ int rc = RTUtf16ToUtf8Tag(GetCommandLineW(), &pszCmdLine, "will-leak:rtR3InitArgv");
+ AssertRCReturn(rc, rc);
+
+ int cArgsFromCmdLine = -1;
+ rc = RTGetOptArgvFromString(&papszArgs, &cArgsFromCmdLine, pszCmdLine,
+ RTGETOPTARGV_CNV_QUOTE_MS_CRT | RTGETOPTARGV_CNV_MODIFY_INPUT, NULL);
+ AssertMsgRCReturn(rc, ("pszCmdLine='%s' rc=%Rrc\n", pszCmdLine, rc), rc);
+ AssertMsg(cArgsFromCmdLine == cArgs,
+ ("cArgsFromCmdLine=%d cArgs=%d pszCmdLine='%s' rc=%Rrc\n", cArgsFromCmdLine, cArgs, pszCmdLine));
+ }
+ else
+# endif
+ {
+ papszArgs = (char **)RTMemAllocZTag((cArgs + 1) * sizeof(char *), "will-leak:rtR3InitArgv");
+ if (!papszArgs)
+ return VERR_NO_MEMORY;
+
+ for (int i = 0; i < cArgs; i++)
+ {
+ int rc = RTStrCurrentCPToUtf8(&papszArgs[i], papszOrgArgs[i]);
+ if (RT_FAILURE(rc))
+ {
+ while (i--)
+ RTStrFree(papszArgs[i]);
+ RTMemFree(papszArgs);
+ return rc;
+ }
+ }
+ }
+
+ papszArgs[cArgs] = NULL;
+
+ g_papszrtOrgArgs = papszOrgArgs;
+ g_papszrtArgs = papszArgs;
+ g_crtArgs = cArgs;
+
+ *ppapszArgs = papszArgs;
+ }
+ else
+#endif /* !IPRT_NO_CRT || !RT_OS_WINDOWS */
+ {
+ /*
+ * The arguments are already UTF-8, no conversion needed.
+ */
+ g_papszrtOrgArgs = papszOrgArgs;
+ g_papszrtArgs = papszOrgArgs;
+ g_crtArgs = cArgs;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+#ifdef IPRT_USE_SIG_CHILD_DUMMY
+/**
+ * Dummy SIGCHILD handler.
+ *
+ * Assigned on rtR3Init only when SIGCHILD handler is set SIGIGN or SIGDEF to
+ * ensure waitpid works properly for the terminated processes.
+ */
+static void rtR3SigChildHandler(int iSignal)
+{
+ NOREF(iSignal);
+}
+#endif /* IPRT_USE_SIG_CHILD_DUMMY */
+
+
+/**
+ * rtR3Init worker.
+ */
+static int rtR3InitBody(uint32_t fFlags, int cArgs, char ***ppapszArgs, const char *pszProgramPath)
+{
+ /*
+ * Early native initialization.
+ */
+ int rc = rtR3InitNativeFirst(fFlags);
+ AssertMsgRCReturn(rc, ("rtR3InitNativeFirst failed with %Rrc\n", rc), rc);
+
+ /*
+ * Disable error popups.
+ */
+#if defined(RT_OS_OS2) /** @todo move to private code. */
+ DosError(FERR_DISABLEHARDERR);
+#endif
+
+#ifndef IPRT_NO_CRT
+ /*
+ * Init C runtime locale before we do anything that may end up converting
+ * paths or we'll end up using the "C" locale for path conversion.
+ */
+ setlocale(LC_CTYPE, "");
+#endif
+
+ /*
+ * The Process ID.
+ */
+#ifdef _MSC_VER
+ g_ProcessSelf = GetCurrentProcessId(); /* since NT 3.1, not 3.51+ as listed on geoffchappell.com */
+#else
+ g_ProcessSelf = getpid();
+#endif
+
+ /*
+ * Save the init flags.
+ */
+ g_fInitFlags |= fFlags;
+
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+# ifdef VBOX
+ /*
+ * This MUST be done as the very first thing, before any file is opened.
+ * The log is opened on demand, but the first log entries may be caused
+ * by rtThreadInit() below.
+ */
+ const char *pszDisableHostCache = getenv("VBOX_DISABLE_HOST_DISK_CACHE");
+ if ( pszDisableHostCache != NULL
+ && *pszDisableHostCache
+ && strcmp(pszDisableHostCache, "0") != 0)
+ {
+ RTFileSetForceFlags(RTFILE_O_WRITE, RTFILE_O_WRITE_THROUGH, 0);
+ RTFileSetForceFlags(RTFILE_O_READWRITE, RTFILE_O_WRITE_THROUGH, 0);
+ }
+# endif /* VBOX */
+#endif /* !IN_GUEST && !RT_NO_GIP */
+
+ /*
+ * Thread Thread database and adopt the caller thread as 'main'.
+ * This must be done before everything else or else we'll call into threading
+ * without having initialized TLS entries and suchlike.
+ */
+ rc = rtThreadInit();
+ AssertMsgRCReturn(rc, ("Failed to initialize threads, rc=%Rrc!\n", rc), rc);
+
+ /*
+ * The executable path before SUPLib (windows requirement).
+ */
+ rc = rtR3InitProgramPath(pszProgramPath);
+ AssertLogRelMsgRCReturn(rc, ("Failed to get executable directory path, rc=%Rrc!\n", rc), rc);
+
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+ /*
+ * Initialize SUPLib here so the GIP can get going as early as possible
+ * (improves accuracy for the first client).
+ */
+ if (fFlags & (RTR3INIT_FLAGS_SUPLIB | RTR3INIT_FLAGS_TRY_SUPLIB))
+ {
+ if (!(fFlags & ((SUPR3INIT_F_UNRESTRICTED | SUPR3INIT_F_LIMITED) << RTR3INIT_FLAGS_SUPLIB_SHIFT)))
+ g_fInitFlags |= fFlags |= SUPR3INIT_F_UNRESTRICTED << RTR3INIT_FLAGS_SUPLIB_SHIFT;
+ rc = SUPR3InitEx(fFlags >> RTR3INIT_FLAGS_SUPLIB_SHIFT, NULL /*ppSession*/);
+ AssertMsgReturn(RT_SUCCESS(rc) || (fFlags & RTR3INIT_FLAGS_TRY_SUPLIB),
+ ("Failed to initialize the support library, rc=%Rrc!\n", rc), rc);
+ }
+#endif
+
+ /*
+ * Convert arguments.
+ */
+ rc = rtR3InitArgv(fFlags, cArgs, ppapszArgs);
+ AssertLogRelMsgRCReturn(rc, ("Failed to convert the arguments, rc=%Rrc!\n", rc), rc);
+
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+ /*
+ * The threading is initialized, so we can safely sleep a bit if GIP
+ * needs some time to start updating itself. Currently limited to
+ * the first mapping of GIP (u32TransactionId <= 4), quite possible we
+ * could just ditch this now.
+ */
+ /** @todo consider dropping this... */
+ PSUPGLOBALINFOPAGE pGip;
+ if ( (fFlags & (RTR3INIT_FLAGS_SUPLIB | RTR3INIT_FLAGS_TRY_SUPLIB))
+ && (pGip = g_pSUPGlobalInfoPage) != NULL
+ && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC)
+ {
+ PSUPGIPCPU pGipCpu = SUPGetGipCpuPtr(pGip);
+ if ( pGipCpu
+ && pGipCpu->u32TransactionId <= 4)
+ {
+ RTThreadSleep(pGip->u32UpdateIntervalNS / RT_NS_1MS + 2);
+ RTTimeNanoTS();
+ }
+ }
+#endif
+
+ /*
+ * Init the program start timestamp TS.
+ * Do that here to be sure that the GIP time was properly updated the 1st time.
+ */
+ g_u64ProgramStartNanoTS = RTTimeNanoTS();
+
+ /*
+ * The remainder cannot easily be undone, so it has to go last.
+ */
+
+ /* Fork and exit callbacks. */
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_OS2)
+ rc = pthread_atfork(NULL, NULL, rtR3ForkChildCallback);
+ AssertMsg(rc == 0, ("%d\n", rc));
+#endif
+ atexit(rtR3ExitCallback);
+
+#ifdef IPRT_USE_SIG_CHILD_DUMMY
+ /*
+ * SIGCHLD must not be ignored (that's default), otherwise posix compliant waitpid
+ * implementations won't work right.
+ */
+ for (;;)
+ {
+ struct sigaction saOld;
+ rc = sigaction(SIGCHLD, 0, &saOld); AssertMsg(rc == 0, ("%d/%d\n", rc, errno));
+ if ( rc != 0
+ || (saOld.sa_flags & SA_SIGINFO)
+ || ( saOld.sa_handler != SIG_IGN
+ && saOld.sa_handler != SIG_DFL)
+ )
+ break;
+
+ /* Try install dummy handler. */
+ struct sigaction saNew = saOld;
+ saNew.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+ saNew.sa_handler = rtR3SigChildHandler;
+ rc = sigemptyset(&saNew.sa_mask); AssertMsg(rc == 0, ("%d/%d\n", rc, errno));
+ struct sigaction saOld2;
+ rc = sigaction(SIGCHLD, &saNew, &saOld2); AssertMsg(rc == 0, ("%d/%d\n", rc, errno));
+ if ( rc != 0
+ || ( saOld2.sa_handler == saOld.sa_handler
+ && !(saOld2.sa_flags & SA_SIGINFO))
+ )
+ break;
+
+ /* Race during dynamic load, restore and try again... */
+ sigaction(SIGCHLD, &saOld2, NULL);
+ RTThreadYield();
+ }
+#endif /* IPRT_USE_SIG_CHILD_DUMMY */
+
+#ifdef IPRT_WITH_ALIGNMENT_CHECKS
+ /*
+ * Enable alignment checks.
+ */
+ const char *pszAlignmentChecks = RTEnvGet("IPRT_ALIGNMENT_CHECKS"); /** @todo add RTEnvGetBool */
+ g_fRTAlignmentChecks = pszAlignmentChecks != NULL
+ && pszAlignmentChecks[0] == '1'
+ && pszAlignmentChecks[1] == '\0';
+ if (g_fRTAlignmentChecks)
+ IPRT_ALIGNMENT_CHECKS_ENABLE();
+#endif
+
+ /*
+ * Final native initialization.
+ */
+ rc = rtR3InitNativeFinal(fFlags);
+ AssertMsgRCReturn(rc, ("rtR3InitNativeFinal failed with %Rrc\n", rc), rc);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Internal initialization worker.
+ *
+ * @returns IPRT status code.
+ * @param fFlags Flags, see RTR3INIT_XXX.
+ * @param cArgs Pointer to the argument count.
+ * @param ppapszArgs Pointer to the argument vector pointer. NULL
+ * allowed if @a cArgs is 0.
+ * @param pszProgramPath The program path. Pass NULL if we're to figure it
+ * out ourselves.
+ */
+static int rtR3Init(uint32_t fFlags, int cArgs, char ***ppapszArgs, const char *pszProgramPath)
+{
+ /* no entry log flow, because prefixes and thread may freak out. */
+ Assert(!(fFlags & ~RTR3INIT_FLAGS_VALID_MASK));
+ Assert(!(fFlags & RTR3INIT_FLAGS_DLL) || cArgs == 0);
+
+ /*
+ * Do reference counting, only initialize the first time around.
+ *
+ * We are ASSUMING that nobody will be able to race RTR3Init* calls when the
+ * first one, the real init, is running (second assertion).
+ */
+ int32_t cUsers = ASMAtomicIncS32(&g_crtR3Users);
+ if (cUsers != 1)
+ {
+ AssertMsg(cUsers > 1, ("%d\n", cUsers));
+ Assert(!g_frtR3Initializing);
+
+#if !defined(IN_GUEST) && !defined(RT_NO_GIP)
+ /* Initialize the support library if requested. We've always ignored the
+ status code here for some reason, making the two flags same. */
+ if (fFlags & (RTR3INIT_FLAGS_SUPLIB | RTR3INIT_FLAGS_TRY_SUPLIB))
+ {
+ if (!(fFlags & ((SUPR3INIT_F_UNRESTRICTED | SUPR3INIT_F_LIMITED) << RTR3INIT_FLAGS_SUPLIB_SHIFT)))
+ fFlags |= SUPR3INIT_F_UNRESTRICTED << RTR3INIT_FLAGS_SUPLIB_SHIFT;
+ SUPR3InitEx(fFlags >> RTR3INIT_FLAGS_SUPLIB_SHIFT, NULL /*ppSession*/);
+ g_fInitFlags |= fFlags & (RTR3INIT_FLAGS_SUPLIB | RTR3INIT_FLAGS_TRY_SUPLIB | RTR3INIT_FLAGS_SUPLIB_MASK);
+ }
+#endif
+ g_fInitFlags |= fFlags & RTR3INIT_FLAGS_UTF8_ARGV;
+
+ if ( !(fFlags & RTR3INIT_FLAGS_UNOBTRUSIVE)
+ && (g_fInitFlags & RTR3INIT_FLAGS_UNOBTRUSIVE))
+ {
+ g_fInitFlags &= ~RTR3INIT_FLAGS_UNOBTRUSIVE;
+ g_fInitFlags |= fFlags & RTR3INIT_FLAGS_STANDALONE_APP;
+ rtR3InitNativeObtrusive(g_fInitFlags | fFlags);
+ rtThreadReInitObtrusive();
+ }
+ else
+ Assert(!(fFlags & RTR3INIT_FLAGS_STANDALONE_APP) || (g_fInitFlags & RTR3INIT_FLAGS_STANDALONE_APP));
+
+ int rc = VINF_SUCCESS;
+ if (pszProgramPath)
+ rc = rtR3InitProgramPath(pszProgramPath);
+ if (RT_SUCCESS(rc))
+ rc = rtR3InitArgv(fFlags, cArgs, ppapszArgs);
+ return rc;
+ }
+
+ /*
+ * Do the initialization.
+ */
+ ASMAtomicWriteBool(&g_frtR3Initializing, true);
+ int rc = rtR3InitBody(fFlags, cArgs, ppapszArgs, pszProgramPath);
+ ASMAtomicWriteBool(&g_frtR3Initializing, false);
+ if (RT_FAILURE(rc))
+ {
+ /* failure */
+ ASMAtomicDecS32(&g_crtR3Users);
+ return rc;
+ }
+
+ /* success */
+ LogFlow(("rtR3Init: returns VINF_SUCCESS\n"));
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTR3InitExe(int cArgs, char ***ppapszArgs, uint32_t fFlags)
+{
+ Assert(!(fFlags & RTR3INIT_FLAGS_DLL));
+ return rtR3Init(fFlags, cArgs, ppapszArgs, NULL);
+}
+
+
+RTR3DECL(int) RTR3InitExeNoArguments(uint32_t fFlags)
+{
+ Assert(!(fFlags & RTR3INIT_FLAGS_DLL));
+ return rtR3Init(fFlags, 0, NULL, NULL);
+}
+
+
+RTR3DECL(int) RTR3InitDll(uint32_t fFlags)
+{
+ Assert(!(fFlags & RTR3INIT_FLAGS_DLL));
+ return rtR3Init(fFlags | RTR3INIT_FLAGS_DLL, 0, NULL, NULL);
+}
+
+
+RTR3DECL(int) RTR3InitEx(uint32_t iVersion, uint32_t fFlags, int cArgs, char ***ppapszArgs, const char *pszProgramPath)
+{
+ AssertReturn(iVersion == RTR3INIT_VER_CUR, VERR_NOT_SUPPORTED);
+ return rtR3Init(fFlags, cArgs, ppapszArgs, pszProgramPath);
+}
+
+
+RTR3DECL(bool) RTR3InitIsInitialized(void)
+{
+ return g_crtR3Users >= 1 && !g_frtR3Initializing;
+}
+
+
+RTR3DECL(bool) RTR3InitIsUnobtrusive(void)
+{
+ return RT_BOOL(g_fInitFlags & RTR3INIT_FLAGS_UNOBTRUSIVE);
+}
+
+
+#if 0 /** @todo implement RTR3Term. */
+RTR3DECL(void) RTR3Term(void)
+{
+}
+#endif
+
diff --git a/src/VBox/Runtime/r3/init.h b/src/VBox/Runtime/r3/init.h
new file mode 100644
index 00000000..95d7e52c
--- /dev/null
+++ b/src/VBox/Runtime/r3/init.h
@@ -0,0 +1,50 @@
+/* $Id: init.h $ */
+/** @file
+ * IPRT - Ring-3 initialization.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_init_h
+#define IPRT_INCLUDED_SRC_r3_init_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+DECLHIDDEN(int) rtR3InitNativeFirst(uint32_t fFlags);
+DECLHIDDEN(int) rtR3InitNativeFinal(uint32_t fFlags);
+DECLHIDDEN(void) rtR3InitNativeObtrusive(uint32_t fFlags);
+
+#endif /* !IPRT_INCLUDED_SRC_r3_init_h */
+
diff --git a/src/VBox/Runtime/r3/linux/Makefile.kup b/src/VBox/Runtime/r3/linux/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/Makefile.kup
diff --git a/src/VBox/Runtime/r3/linux/RTFileCopyPartEx-linux.cpp b/src/VBox/Runtime/r3/linux/RTFileCopyPartEx-linux.cpp
new file mode 100644
index 00000000..0b1d93d7
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/RTFileCopyPartEx-linux.cpp
@@ -0,0 +1,196 @@
+/* $Id: RTFileCopyPartEx-linux.cpp $ */
+/** @file
+ * IPRT - RTFileCopyPartEx, linux specific implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/file.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#ifndef __NR_copy_file_range
+# if defined(RT_ARCH_X86)
+# define __NR_copy_file_range 377
+# elif defined(RT_ARCH_AMD64)
+# define __NR_copy_file_range 326
+# endif
+#endif
+
+
+#ifndef __NR_copy_file_range
+# include "../../generic/RTFileCopyPartEx-generic.cpp"
+#else /* __NR_copy_file_range - whole file */
+/* Include the generic code as a fallback since copy_file_range is rather new . */
+# define IPRT_FALLBACK_VERSION
+# include "../../generic/RTFileCopyPartEx-generic.cpp"
+# undef IPRT_FALLBACK_VERSION
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static int32_t volatile g_fCopyFileRangeSupported = -1;
+
+
+DECLINLINE(loff_t)
+MyCopyFileRangeSysCall(int fdIn, loff_t *poffIn, int fdOut, loff_t *poffOut, size_t cbChunk, unsigned int fFlags)
+{
+ return syscall(__NR_copy_file_range, fdIn, poffIn, fdOut, poffOut, cbChunk, fFlags);
+}
+
+
+DECL_NO_INLINE(static, bool) HasCopyFileRangeSyscallSlow(void)
+{
+ errno = 0;
+ MyCopyFileRangeSysCall(-1, NULL, -1, NULL, 4096, 0);
+ if (errno != ENOSYS)
+ {
+ ASMAtomicWriteS32(&g_fCopyFileRangeSupported, 1);
+ return true;
+ }
+ ASMAtomicWriteS32(&g_fCopyFileRangeSupported, 0);
+ return false;
+}
+
+DECLINLINE(bool) HasCopyFileRangeSyscall(void)
+{
+ int32_t i = ASMAtomicUoReadS32(&g_fCopyFileRangeSupported);
+ if (i != -1)
+ return i == 1;
+ return HasCopyFileRangeSyscallSlow();
+}
+
+
+
+RTDECL(int) RTFileCopyPartPrep(PRTFILECOPYPARTBUFSTATE pBufState, uint64_t cbToCopy)
+{
+ if (HasCopyFileRangeSyscall())
+ {
+ pBufState->iAllocType = -42;
+ pBufState->pbBuf = NULL;
+ pBufState->cbBuf = 0;
+ pBufState->uMagic = RTFILECOPYPARTBUFSTATE_MAGIC;
+ return VINF_SUCCESS;
+ }
+ return rtFileCopyPartPrepFallback(pBufState, cbToCopy);
+}
+
+
+RTDECL(void) RTFileCopyPartCleanup(PRTFILECOPYPARTBUFSTATE pBufState)
+{
+ return rtFileCopyPartCleanupFallback(pBufState);
+}
+
+
+RTDECL(int) RTFileCopyPartEx(RTFILE hFileSrc, RTFOFF offSrc, RTFILE hFileDst, RTFOFF offDst, uint64_t cbToCopy,
+ uint32_t fFlags, PRTFILECOPYPARTBUFSTATE pBufState, uint64_t *pcbCopied)
+{
+ /*
+ * Validate input.
+ */
+ if (pcbCopied)
+ *pcbCopied = 0;
+ AssertReturn(pBufState->uMagic == RTFILECOPYPARTBUFSTATE_MAGIC, VERR_INVALID_FLAGS);
+ if (pBufState->iAllocType == -42)
+ { /* more and more likely as time goes */ }
+ else
+ return rtFileCopyPartExFallback(hFileSrc, offSrc, hFileDst, offDst, cbToCopy, fFlags, pBufState, pcbCopied);
+ AssertReturn(offSrc >= 0, VERR_NEGATIVE_SEEK);
+ AssertReturn(offDst >= 0, VERR_NEGATIVE_SEEK);
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ /*
+ * If nothing to copy, return right away.
+ */
+ if (!cbToCopy)
+ return VINF_SUCCESS;
+
+ /*
+ * Do the copying.
+ */
+ uint64_t cbCopied = 0;
+ int rc = VINF_SUCCESS;
+ do
+ {
+ size_t cbThisCopy = (size_t)RT_MIN(cbToCopy - cbCopied, _1G);
+ loff_t offThisDst = offSrc + cbCopied;
+ loff_t offThisSrc = offDst + cbCopied;
+ ssize_t cbActual = MyCopyFileRangeSysCall((int)RTFileToNative(hFileSrc), &offThisSrc,
+ (int)RTFileToNative(hFileDst), &offThisDst,
+ cbThisCopy, 0);
+ if (cbActual < 0)
+ {
+ rc = errno;
+ Assert(rc != 0);
+ rc = rc != 0 ? RTErrConvertFromErrno(rc) : VERR_READ_ERROR;
+ if (rc != VERR_NOT_SAME_DEVICE || cbCopied != 0)
+ break;
+
+ /* Fall back to generic implementation if the syscall refuses to handle the case. */
+ rc = rtFileCopyPartPrepFallback(pBufState, cbToCopy);
+ if (RT_SUCCESS(rc))
+ return rtFileCopyPartExFallback(hFileSrc, offSrc, hFileDst, offDst, cbToCopy, fFlags, pBufState, pcbCopied);
+ return rc;
+ }
+ Assert(offThisSrc == offSrc + (int64_t)cbCopied + cbActual);
+ Assert(offThisDst == offDst + (int64_t)cbCopied + cbActual);
+
+ if (cbActual == 0)
+ {
+ if (!pcbCopied)
+ rc = VERR_EOF;
+ break;
+ }
+
+ cbCopied += cbActual;
+ } while (cbCopied < cbToCopy);
+
+ if (pcbCopied)
+ *pcbCopied = cbCopied;
+
+ return rc;
+}
+
+#endif /* __NR_copy_file_range */
+
diff --git a/src/VBox/Runtime/r3/linux/RTFileQuerySectorSize-linux.cpp b/src/VBox/Runtime/r3/linux/RTFileQuerySectorSize-linux.cpp
new file mode 100644
index 00000000..ffd615fb
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/RTFileQuerySectorSize-linux.cpp
@@ -0,0 +1,88 @@
+/* $Id: RTFileQuerySectorSize-linux.cpp $ */
+/** @file
+ * IPRT - RTFileQuerySectorSize, Linux implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#include <errno.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+
+RTDECL(int) RTFileQuerySectorSize(RTFILE hFile, uint32_t *pcbSector)
+{
+ AssertPtrReturn(pcbSector, VERR_INVALID_PARAMETER);
+
+ int rc;
+ int const fd = (int)RTFileToNative(hFile);
+ struct stat DevStat = { 0 };
+ if (!fstat(fd, &DevStat))
+ {
+ if (S_ISBLK(DevStat.st_mode))
+ {
+ int cbLogicalBlock = 0;
+ if (!ioctl(fd, BLKSSZGET, &cbLogicalBlock))
+ {
+ AssertReturn(cbLogicalBlock > 0, VERR_INVALID_FUNCTION);
+ *pcbSector = cbLogicalBlock;
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("ioctl failed: errno=%d / %Rrc\n", errno, rc));
+ }
+ else
+ {
+ AssertMsgFailed(("not a block device.\n"));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("fstat failed: errno=%d / %Rrc\n", errno, rc));
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/linux/RTFileSetAllocationSize-linux.cpp b/src/VBox/Runtime/r3/linux/RTFileSetAllocationSize-linux.cpp
new file mode 100644
index 00000000..f3acd7fe
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/RTFileSetAllocationSize-linux.cpp
@@ -0,0 +1,86 @@
+/* $Id: RTFileSetAllocationSize-linux.cpp $ */
+/** @file
+ * IPRT - RTFileSetAllocationSize, linux implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include <iprt/file.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+
+/**
+ * The Linux specific fallocate() method.
+ */
+typedef int (*PFNLNXFALLOCATE) (int iFd, int fMode, off_t offStart, off_t cb);
+/** Flag to specify that the file size should not be extended. */
+#define LNX_FALLOC_FL_KEEP_SIZE 1
+
+RTDECL(int) RTFileSetAllocationSize(RTFILE hFile, uint64_t cbSize, uint32_t fFlags)
+{
+ AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTFILE_ALLOC_SIZE_F_VALID), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(sizeof(off_t) >= sizeof(cbSize) || RT_HIDWORD(cbSize) == 0,
+ ("64-bit filesize not supported! cbSize=%lld\n", cbSize),
+ VERR_NOT_SUPPORTED);
+
+ int rc;
+ PFNLNXFALLOCATE pfnLnxFAllocate = (PFNLNXFALLOCATE)(uintptr_t)dlsym(RTLD_DEFAULT, "fallocate64");
+ if (RT_VALID_PTR(pfnLnxFAllocate))
+ {
+ int fLnxFlags = (fFlags & RTFILE_ALLOC_SIZE_F_KEEP_SIZE) ? LNX_FALLOC_FL_KEEP_SIZE : 0;
+ int rcLnx = pfnLnxFAllocate(RTFileToNative(hFile), fLnxFlags, 0, cbSize);
+ if (rcLnx == 0)
+ rc = VINF_SUCCESS;
+ else if (errno == EOPNOTSUPP)
+ rc = VERR_NOT_SUPPORTED;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTFileSetAllocationSize);
diff --git a/src/VBox/Runtime/r3/linux/RTProcIsRunningByName-linux.cpp b/src/VBox/Runtime/r3/linux/RTProcIsRunningByName-linux.cpp
new file mode 100644
index 00000000..6049fa1a
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/RTProcIsRunningByName-linux.cpp
@@ -0,0 +1,128 @@
+/* $Id: RTProcIsRunningByName-linux.cpp $ */
+/** @file
+ * IPRT - RTProcIsRunningByName, Linux implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/param.h>
+#include <iprt/assert.h>
+
+#include <unistd.h>
+
+
+RTR3DECL(bool) RTProcIsRunningByName(const char *pszName)
+{
+ /*
+ * Quick validation.
+ */
+ if (!pszName)
+ return false;
+
+ bool const fWithPath = RTPathHavePath(pszName);
+
+ /*
+ * Enumerate /proc.
+ */
+ RTDIR hDir;
+ int rc = RTDirOpen(&hDir, "/proc");
+ AssertMsgRCReturn(rc, ("RTDirOpen on /proc failed: rc=%Rrc\n", rc), false);
+ if (RT_SUCCESS(rc))
+ {
+ RTDIRENTRY DirEntry;
+ while (RT_SUCCESS(RTDirRead(hDir, &DirEntry, NULL)))
+ {
+ /*
+ * Filter numeric directory entries only.
+ */
+ if ( ( DirEntry.enmType == RTDIRENTRYTYPE_DIRECTORY
+ || DirEntry.enmType == RTDIRENTRYTYPE_UNKNOWN)
+ && RTStrToUInt32(DirEntry.szName) > 0)
+ {
+ /*
+ * Try readlink on exe first since it's more faster and reliable.
+ * Fall back on reading the first line in cmdline if that fails
+ * (access errors typically). cmdline is unreliable as it might
+ * contain whatever the execv caller passes as argv[0].
+ */
+ char szName[RTPATH_MAX];
+ RTStrPrintf(szName, sizeof(szName), "/proc/%s/exe", &DirEntry.szName[0]);
+ char szExe[RTPATH_MAX];
+ int cchLink = readlink(szName, szExe, sizeof(szExe) - 1);
+ if ( cchLink > 0
+ && (size_t)cchLink < sizeof(szExe))
+ {
+ szExe[cchLink] = '\0';
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ RTStrPrintf(szName, sizeof(szName), "/proc/%s/cmdline", &DirEntry.szName[0]);
+ PRTSTREAM pStream;
+ rc = RTStrmOpen(szName, "r", &pStream);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrmGetLine(pStream, szExe, sizeof(szExe));
+ RTStrmClose(pStream);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We are interested on the file name part only.
+ */
+ char const *pszProcName = fWithPath ? szExe : RTPathFilename(szExe);
+ if (RTStrCmp(pszProcName, pszName) == 0)
+ {
+ /* Found it! */
+ RTDirClose(hDir);
+ return true;
+ }
+ }
+ }
+ }
+ RTDirClose(hDir);
+ }
+
+ return false;
+}
+
diff --git a/src/VBox/Runtime/r3/linux/RTSystemFirmware-linux.cpp b/src/VBox/Runtime/r3/linux/RTSystemFirmware-linux.cpp
new file mode 100644
index 00000000..2d7b8986
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/RTSystemFirmware-linux.cpp
@@ -0,0 +1,115 @@
+/* $Id: RTSystemFirmware-linux.cpp $ */
+/** @file
+ * IPRT - System firmware information, linux.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/system.h>
+
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/string.h>
+#include <iprt/linux/sysfs.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Defines the UEFI Globals UUID that is used here as filename suffix (case sensitive). */
+#define VBOX_UEFI_UUID_GLOBALS "8be4df61-93ca-11d2-aa0d-00e098032b8c"
+
+
+RTDECL(int) RTSystemQueryFirmwareType(PRTSYSFWTYPE penmFirmwareType)
+{
+ if (RTLinuxSysFsExists("firmware/efi/"))
+ *penmFirmwareType = RTSYSFWTYPE_UEFI;
+ else if (RTLinuxSysFsExists(""))
+ *penmFirmwareType = RTSYSFWTYPE_BIOS;
+ else
+ {
+ *penmFirmwareType = RTSYSFWTYPE_INVALID;
+ return VERR_NOT_SUPPORTED;
+ }
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSystemQueryFirmwareType);
+
+
+RTDECL(int) RTSystemQueryFirmwareBoolean(RTSYSFWBOOL enmBoolean, bool *pfValue)
+{
+ *pfValue = false;
+
+ /*
+ * Translate the property to variable base filename.
+ */
+ const char *pszName;
+ switch (enmBoolean)
+ {
+ case RTSYSFWBOOL_SECURE_BOOT:
+ pszName = "firmware/efi/efivars/SecureBoot";
+ break;
+
+ default:
+ AssertReturn(enmBoolean > RTSYSFWBOOL_INVALID && enmBoolean < RTSYSFWBOOL_END, VERR_INVALID_PARAMETER);
+ return VERR_SYS_UNSUPPORTED_FIRMWARE_PROPERTY;
+
+ }
+
+ /*
+ * Try open and read the variable value.
+ */
+ RTFILE hFile;
+ int rc = RTLinuxSysFsOpen(&hFile, "%s-" VBOX_UEFI_UUID_GLOBALS, pszName);
+ /** @todo try other suffixes if file-not-found. */
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t abBuf[16];
+ size_t cbRead = 0;
+ rc = RTLinuxSysFsReadFile(hFile, abBuf, sizeof(abBuf), &cbRead);
+ *pfValue = cbRead > 1 && abBuf[cbRead - 1] != 0;
+ RTFileClose(hFile);
+ }
+ else if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ rc = VINF_SUCCESS;
+ else if (rc == VERR_PERMISSION_DENIED)
+ rc = VERR_NOT_SUPPORTED;
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSystemQueryFirmwareBoolean);
+
diff --git a/src/VBox/Runtime/r3/linux/RTSystemQueryDmiString-linux.cpp b/src/VBox/Runtime/r3/linux/RTSystemQueryDmiString-linux.cpp
new file mode 100644
index 00000000..91cf6eb2
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/RTSystemQueryDmiString-linux.cpp
@@ -0,0 +1,96 @@
+/* $Id: RTSystemQueryDmiString-linux.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryDmiString, linux ring-3.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/linux/sysfs.h>
+
+#include <errno.h>
+
+
+RTDECL(int) RTSystemQueryDmiString(RTSYSDMISTR enmString, char *pszBuf, size_t cbBuf)
+{
+ AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbBuf > 0, VERR_INVALID_PARAMETER);
+ *pszBuf = '\0';
+ AssertReturn(enmString > RTSYSDMISTR_INVALID && enmString < RTSYSDMISTR_END, VERR_INVALID_PARAMETER);
+
+ const char *pszSysFsName;
+ switch (enmString)
+ {
+ case RTSYSDMISTR_PRODUCT_NAME: pszSysFsName = "id/product_name"; break;
+ case RTSYSDMISTR_PRODUCT_VERSION: pszSysFsName = "id/product_version"; break;
+ case RTSYSDMISTR_PRODUCT_UUID: pszSysFsName = "id/product_uuid"; break;
+ case RTSYSDMISTR_PRODUCT_SERIAL: pszSysFsName = "id/product_serial"; break;
+ case RTSYSDMISTR_MANUFACTURER: pszSysFsName = "id/sys_vendor"; break;
+ default:
+ return VERR_NOT_SUPPORTED;
+ }
+
+ size_t cbRead = 0;
+ int rc = RTLinuxSysFsReadStrFile(pszBuf, cbBuf, &cbRead, "devices/virtual/dmi/%s", pszSysFsName);
+ if (RT_FAILURE(rc) && rc != VERR_BUFFER_OVERFLOW)
+ rc = RTLinuxSysFsReadStrFile(pszBuf, cbBuf, &cbRead, "class/dmi/%s", pszSysFsName);
+ if (RT_FAILURE(rc) && rc != VERR_BUFFER_OVERFLOW)
+ {
+ switch (rc)
+ {
+ case VINF_SUCCESS:
+ AssertFailed();
+ break;
+ case VERR_FILE_NOT_FOUND:
+ case VERR_PATH_NOT_FOUND:
+ case VERR_IS_A_DIRECTORY:
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ case VERR_PERMISSION_DENIED:
+ case VERR_ACCESS_DENIED:
+ rc = VERR_ACCESS_DENIED;
+ break;
+ }
+ }
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSystemQueryDmiString);
+
diff --git a/src/VBox/Runtime/r3/linux/RTSystemShutdown-linux.cpp b/src/VBox/Runtime/r3/linux/RTSystemShutdown-linux.cpp
new file mode 100644
index 00000000..fd198e30
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/RTSystemShutdown-linux.cpp
@@ -0,0 +1,111 @@
+/* $Id: RTSystemShutdown-linux.cpp $ */
+/** @file
+ * IPRT - RTSystemShutdown, linux implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTSystemShutdown(RTMSINTERVAL cMsDelay, uint32_t fFlags, const char *pszLogMsg)
+{
+ AssertPtrReturn(pszLogMsg, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTSYSTEM_SHUTDOWN_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Assemble the argument vector.
+ */
+ int iArg = 0;
+ const char *apszArgs[6];
+
+ RT_BZERO(apszArgs, sizeof(apszArgs));
+
+ apszArgs[iArg++] = "/sbin/shutdown";
+ switch (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK)
+ {
+ case RTSYSTEM_SHUTDOWN_HALT:
+ apszArgs[iArg++] = "-h";
+ apszArgs[iArg++] = "-H";
+ break;
+ case RTSYSTEM_SHUTDOWN_REBOOT:
+ apszArgs[iArg++] = "-r";
+ break;
+ case RTSYSTEM_SHUTDOWN_POWER_OFF:
+ case RTSYSTEM_SHUTDOWN_POWER_OFF_HALT:
+ apszArgs[iArg++] = "-h";
+ apszArgs[iArg++] = "-P";
+ break;
+ }
+
+ char szWhen[80];
+ if (cMsDelay < 500)
+ strcpy(szWhen, "now");
+ else
+ RTStrPrintf(szWhen, sizeof(szWhen), "%u", (unsigned)((cMsDelay + 499) / 1000));
+ apszArgs[iArg++] = szWhen;
+
+ apszArgs[iArg++] = pszLogMsg;
+
+
+ /*
+ * Start the shutdown process and wait for it to complete.
+ */
+ RTPROCESS hProc;
+ int rc = RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProc);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ RTPROCSTATUS ProcStatus;
+ rc = RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus);
+ if (RT_SUCCESS(rc))
+ {
+ if ( ProcStatus.enmReason != RTPROCEXITREASON_NORMAL
+ || ProcStatus.iStatus != 0)
+ rc = VERR_SYS_SHUTDOWN_FAILED;
+ }
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSystemShutdown);
+
diff --git a/src/VBox/Runtime/r3/linux/RTThreadGetNativeState-linux.cpp b/src/VBox/Runtime/r3/linux/RTThreadGetNativeState-linux.cpp
new file mode 100644
index 00000000..26c0afdb
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/RTThreadGetNativeState-linux.cpp
@@ -0,0 +1,121 @@
+/* $Id: RTThreadGetNativeState-linux.cpp $ */
+/** @file
+ * IPRT - RTThreadGetNativeState, linux implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include "internal/thread.h"
+
+#include <unistd.h>
+#include <sys/fcntl.h>
+
+
+RTDECL(RTTHREADNATIVESTATE) RTThreadGetNativeState(RTTHREAD hThread)
+{
+ RTTHREADNATIVESTATE enmRet = RTTHREADNATIVESTATE_INVALID;
+ PRTTHREADINT pThread = rtThreadGet(hThread);
+ if (pThread)
+ {
+ enmRet = RTTHREADNATIVESTATE_UNKNOWN;
+
+ char szName[512];
+ RTStrPrintf(szName, sizeof(szName), "/proc/self/task/%u/stat", pThread->tid);
+ int fd = open(szName, O_RDONLY, 0);
+ if (fd >= 0)
+ {
+ ssize_t cch = read(fd, szName, sizeof(szName) - 1);
+ close(fd);
+ if (cch > 0)
+ {
+ szName[cch] = '\0';
+
+ /* skip the pid, the (comm name) and stop at the status char. */
+ const char *psz = szName;
+ while ( *psz
+ && ( *psz != ')'
+ || !RT_C_IS_SPACE(psz[1])
+ || !RT_C_IS_ALPHA(psz[2])
+ || !RT_C_IS_SPACE(psz[3])
+ )
+ )
+ psz++;
+ if (*psz == ')')
+ {
+ switch (psz[2])
+ {
+ case 'R': /* running */
+ enmRet = RTTHREADNATIVESTATE_RUNNING;
+ break;
+
+ case 'S': /* sleeping */
+ case 'D': /* disk sleeping */
+ enmRet = RTTHREADNATIVESTATE_BLOCKED;
+ break;
+
+ case 'T': /* stopped or tracking stop */
+ enmRet = RTTHREADNATIVESTATE_SUSPENDED;
+ break;
+
+ case 'Z': /* zombie */
+ case 'X': /* dead */
+ enmRet = RTTHREADNATIVESTATE_TERMINATED;
+ break;
+
+ default:
+ AssertMsgFailed(("state=%c\n", psz[2]));
+ enmRet = RTTHREADNATIVESTATE_UNKNOWN;
+ break;
+ }
+ }
+ else
+ AssertMsgFailed(("stat='%s'\n", szName));
+ }
+ }
+ rtThreadRelease(pThread);
+ }
+ return enmRet;
+}
+
diff --git a/src/VBox/Runtime/r3/linux/fileaio-linux.cpp b/src/VBox/Runtime/r3/linux/fileaio-linux.cpp
new file mode 100644
index 00000000..2f365a45
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/fileaio-linux.cpp
@@ -0,0 +1,847 @@
+/* $Id: fileaio-linux.cpp $ */
+/** @file
+ * IPRT - File async I/O, native implementation for the Linux host platform.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @page pg_rtfileaio_linux RTFile Async I/O - Linux Implementation Notes
+ * @internal
+ *
+ * Linux implements the kernel async I/O API through the io_* syscalls. They are
+ * not exposed in the glibc (the aio_* API uses userspace threads and blocking
+ * I/O operations to simulate async behavior). There is an external library
+ * called libaio which implements these syscalls but because we don't want to
+ * have another dependency and this library is not installed by default and the
+ * interface is really simple we use the kernel interface directly using wrapper
+ * functions.
+ *
+ * The interface has some limitations. The first one is that the file must be
+ * opened with O_DIRECT. This disables caching done by the kernel which can be
+ * compensated if the user of this API implements caching itself. The next
+ * limitation is that data buffers must be aligned at a 512 byte boundary or the
+ * request will fail.
+ */
+/** @todo r=bird: What's this about "must be opened with O_DIRECT"? An
+ * explanation would be nice, esp. seeing what Linus is quoted saying
+ * about it in the open man page... */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/thread.h>
+#include "internal/fileaio.h"
+
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <errno.h>
+
+#include <iprt/file.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** The async I/O context handle */
+typedef unsigned long LNXKAIOCONTEXT;
+
+/**
+ * Supported commands for the iocbs
+ */
+enum
+{
+ LNXKAIO_IOCB_CMD_READ = 0,
+ LNXKAIO_IOCB_CMD_WRITE = 1,
+ LNXKAIO_IOCB_CMD_FSYNC = 2,
+ LNXKAIO_IOCB_CMD_FDSYNC = 3
+};
+
+/**
+ * The iocb structure of a request which is passed to the kernel.
+ *
+ * We redefined this here because the version in the header lacks padding
+ * for 32bit.
+ */
+typedef struct LNXKAIOIOCB
+{
+ /** Opaque pointer to data which is returned on an I/O event. */
+ void *pvUser;
+#ifdef RT_ARCH_X86
+ uint32_t u32Padding0;
+#endif
+ /** Contains the request number and is set by the kernel. */
+ uint32_t u32Key;
+ /** Reserved. */
+ uint32_t u32Reserved0;
+ /** The I/O opcode. */
+ uint16_t u16IoOpCode;
+ /** Request priority. */
+ int16_t i16Priority;
+ /** The file descriptor. */
+ uint32_t uFileDesc;
+ /** The userspace pointer to the buffer containing/receiving the data. */
+ void *pvBuf;
+#ifdef RT_ARCH_X86
+ uint32_t u32Padding1;
+#endif
+ /** How many bytes to transfer. */
+#if ARCH_BITS == 32
+ uint32_t cbTransfer;
+ uint32_t u32Padding2;
+#elif ARCH_BITS == 64
+ uint64_t cbTransfer;
+#else
+# error "Unknown architecture"
+#endif
+ /** At which offset to start the transfer. */
+ int64_t off;
+ /** Reserved. */
+ uint64_t u64Reserved1;
+ /** Flags */
+ uint32_t fFlags;
+ /** Readyness signal file descriptor. */
+ uint32_t u32ResFd;
+} LNXKAIOIOCB, *PLNXKAIOIOCB;
+
+/**
+ * I/O event structure to notify about completed requests.
+ * Redefined here too because of the padding.
+ */
+typedef struct LNXKAIOIOEVENT
+{
+ /** The pvUser field from the iocb. */
+ void *pvUser;
+#if ARCH_BITS == 32
+ uint32_t u32Padding0;
+#endif
+ /** The LNXKAIOIOCB object this event is for. */
+ PLNXKAIOIOCB *pIoCB;
+#if ARCH_BITS == 32
+ uint32_t u32Padding1;
+#endif
+ /** The result code of the operation .*/
+#if ARCH_BITS == 32
+ int32_t rc;
+ uint32_t u32Padding2;
+#elif ARCH_BITS == 64
+ int64_t rc;
+#else
+# error "Unknown architecture"
+#endif
+ /** Secondary result code. */
+#if ARCH_BITS == 32
+ int32_t rc2;
+ uint32_t u32Padding3;
+#elif ARCH_BITS == 64
+ int64_t rc2;
+#else
+# error "Unknown architecture"
+#endif
+} LNXKAIOIOEVENT, *PLNXKAIOIOEVENT;
+
+
+/**
+ * Async I/O completion context state.
+ */
+typedef struct RTFILEAIOCTXINTERNAL
+{
+ /** Handle to the async I/O context. */
+ LNXKAIOCONTEXT AioContext;
+ /** Maximum number of requests this context can handle. */
+ int cRequestsMax;
+ /** Current number of requests active on this context. */
+ volatile int32_t cRequests;
+ /** The ID of the thread which is currently waiting for requests. */
+ volatile RTTHREAD hThreadWait;
+ /** Flag whether the thread was woken up. */
+ volatile bool fWokenUp;
+ /** Flag whether the thread is currently waiting in the syscall. */
+ volatile bool fWaiting;
+ /** Flags given during creation. */
+ uint32_t fFlags;
+ /** Magic value (RTFILEAIOCTX_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOCTXINTERNAL;
+/** Pointer to an internal context structure. */
+typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
+
+/**
+ * Async I/O request state.
+ */
+typedef struct RTFILEAIOREQINTERNAL
+{
+ /** The aio control block. This must be the FIRST elment in
+ * the structure! (see notes below) */
+ LNXKAIOIOCB AioCB;
+ /** Current state the request is in. */
+ RTFILEAIOREQSTATE enmState;
+ /** The I/O context this request is associated with. */
+ LNXKAIOCONTEXT AioContext;
+ /** Return code the request completed with. */
+ int Rc;
+ /** Number of bytes actually transferred. */
+ size_t cbTransfered;
+ /** Completion context we are assigned to. */
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ /** Magic value (RTFILEAIOREQ_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOREQINTERNAL;
+/** Pointer to an internal request structure. */
+typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The max number of events to get in one call. */
+#define AIO_MAXIMUM_REQUESTS_PER_CONTEXT 64
+
+
+/**
+ * Creates a new async I/O context.
+ */
+DECLINLINE(int) rtFileAsyncIoLinuxCreate(unsigned cEvents, LNXKAIOCONTEXT *pAioContext)
+{
+ int rc = syscall(__NR_io_setup, cEvents, pAioContext);
+ if (RT_UNLIKELY(rc == -1))
+ {
+ if (errno == EAGAIN)
+ return VERR_FILE_AIO_INSUFFICIENT_EVENTS;
+ else
+ return RTErrConvertFromErrno(errno);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Destroys a async I/O context.
+ */
+DECLINLINE(int) rtFileAsyncIoLinuxDestroy(LNXKAIOCONTEXT AioContext)
+{
+ int rc = syscall(__NR_io_destroy, AioContext);
+ if (RT_UNLIKELY(rc == -1))
+ return RTErrConvertFromErrno(errno);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Submits an array of I/O requests to the kernel.
+ */
+DECLINLINE(int) rtFileAsyncIoLinuxSubmit(LNXKAIOCONTEXT AioContext, long cReqs, LNXKAIOIOCB **ppIoCB, int *pcSubmitted)
+{
+ int rc = syscall(__NR_io_submit, AioContext, cReqs, ppIoCB);
+ if (RT_UNLIKELY(rc == -1))
+ return RTErrConvertFromErrno(errno);
+
+ *pcSubmitted = rc;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Cancels a I/O request.
+ */
+DECLINLINE(int) rtFileAsyncIoLinuxCancel(LNXKAIOCONTEXT AioContext, PLNXKAIOIOCB pIoCB, PLNXKAIOIOEVENT pIoResult)
+{
+ int rc = syscall(__NR_io_cancel, AioContext, pIoCB, pIoResult);
+ if (RT_UNLIKELY(rc == -1))
+ return RTErrConvertFromErrno(errno);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Waits for I/O events.
+ * @returns Number of events (natural number w/ 0), IPRT error code (negative).
+ */
+DECLINLINE(int) rtFileAsyncIoLinuxGetEvents(LNXKAIOCONTEXT AioContext, long cReqsMin, long cReqs,
+ PLNXKAIOIOEVENT paIoResults, struct timespec *pTimeout)
+{
+ int rc = syscall(__NR_io_getevents, AioContext, cReqsMin, cReqs, paIoResults, pTimeout);
+ if (RT_UNLIKELY(rc == -1))
+ return RTErrConvertFromErrno(errno);
+
+ return rc;
+}
+
+RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
+{
+ int rc = VINF_SUCCESS;
+ AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
+
+ /*
+ * Check if the API is implemented by creating a
+ * completion port.
+ */
+ LNXKAIOCONTEXT AioContext = 0;
+ rc = rtFileAsyncIoLinuxCreate(1, &AioContext);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = rtFileAsyncIoLinuxDestroy(AioContext);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Supported - fill in the limits. The alignment is the only restriction. */
+ pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS;
+ pAioLimits->cbBufferAlignment = 512;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
+{
+ AssertPtrReturn(phReq, VERR_INVALID_POINTER);
+
+ /*
+ * Allocate a new request and initialize it.
+ */
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(*pReqInt));
+ if (RT_UNLIKELY(!pReqInt))
+ return VERR_NO_MEMORY;
+
+ pReqInt->pCtxInt = NULL;
+ pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ *phReq = (RTFILEAIOREQ)pReqInt;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
+{
+ /*
+ * Validate the handle and ignore nil.
+ */
+ if (hReq == NIL_RTFILEAIOREQ)
+ return VINF_SUCCESS;
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+
+ /*
+ * Trash the magic and free it.
+ */
+ ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
+ RTMemFree(pReqInt);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker setting up the request.
+ */
+DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
+ uint16_t uTransferDirection,
+ RTFOFF off, void *pvBuf, size_t cbTransfer,
+ void *pvUser)
+{
+ /*
+ * Validate the input.
+ */
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ Assert(hFile != NIL_RTFILE);
+
+ if (uTransferDirection != LNXKAIO_IOCB_CMD_FSYNC)
+ {
+ AssertPtr(pvBuf);
+ Assert(off >= 0);
+ Assert(cbTransfer > 0);
+ }
+
+ /*
+ * Setup the control block and clear the finished flag.
+ */
+ pReqInt->AioCB.u16IoOpCode = uTransferDirection;
+ pReqInt->AioCB.uFileDesc = RTFileToNative(hFile);
+ pReqInt->AioCB.off = off;
+ pReqInt->AioCB.cbTransfer = cbTransfer;
+ pReqInt->AioCB.pvBuf = pvBuf;
+ pReqInt->AioCB.pvUser = pvUser;
+
+ pReqInt->pCtxInt = NULL;
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void *pvBuf, size_t cbRead, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, LNXKAIO_IOCB_CMD_READ,
+ off, pvBuf, cbRead, pvUser);
+}
+
+
+RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void const *pvBuf, size_t cbWrite, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, LNXKAIO_IOCB_CMD_WRITE,
+ off, (void *)pvBuf, cbWrite, pvUser);
+}
+
+
+RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_HANDLE);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+
+ return rtFileAioReqPrepareTransfer(pReqInt, hFile, LNXKAIO_IOCB_CMD_FSYNC,
+ 0, NULL, 0, pvUser);
+}
+
+
+RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
+
+ return pReqInt->AioCB.pvUser;
+}
+
+
+RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ LNXKAIOIOEVENT AioEvent;
+ int rc = rtFileAsyncIoLinuxCancel(pReqInt->AioContext, &pReqInt->AioCB, &AioEvent);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Decrement request count because the request will never arrive at the
+ * completion port.
+ */
+ AssertMsg(RT_VALID_PTR(pReqInt->pCtxInt), ("Invalid state. Request was canceled but wasn't submitted\n"));
+
+ ASMAtomicDecS32(&pReqInt->pCtxInt->cRequests);
+ pReqInt->Rc = VERR_FILE_AIO_CANCELED;
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ return VINF_SUCCESS;
+ }
+ if (rc == VERR_TRY_AGAIN)
+ return VERR_FILE_AIO_IN_PROGRESS;
+ return rc;
+}
+
+
+RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ AssertPtrNull(pcbTransfered);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ if ( pcbTransfered
+ && RT_SUCCESS(pReqInt->Rc))
+ *pcbTransfered = pReqInt->cbTransfered;
+
+ return pReqInt->Rc;
+}
+
+
+RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax,
+ uint32_t fFlags)
+{
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /* The kernel interface needs a maximum. */
+ if (cAioReqsMax == RTFILEAIO_UNLIMITED_REQS)
+ return VERR_OUT_OF_RANGE;
+
+ pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
+ if (RT_UNLIKELY(!pCtxInt))
+ return VERR_NO_MEMORY;
+
+ /* Init the event handle. */
+ int rc = rtFileAsyncIoLinuxCreate(cAioReqsMax, &pCtxInt->AioContext);
+ if (RT_SUCCESS(rc))
+ {
+ pCtxInt->fWokenUp = false;
+ pCtxInt->fWaiting = false;
+ pCtxInt->hThreadWait = NIL_RTTHREAD;
+ pCtxInt->cRequestsMax = cAioReqsMax;
+ pCtxInt->fFlags = fFlags;
+ pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
+ *phAioCtx = (RTFILEAIOCTX)pCtxInt;
+ }
+ else
+ RTMemFree(pCtxInt);
+
+ return rc;
+}
+
+
+RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
+{
+ /* Validate the handle and ignore nil. */
+ if (hAioCtx == NIL_RTFILEAIOCTX)
+ return VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ /* Cannot destroy a busy context. */
+ if (RT_UNLIKELY(pCtxInt->cRequests))
+ return VERR_FILE_AIO_BUSY;
+
+ /* The native bit first, then mark it as dead and free it. */
+ int rc = rtFileAsyncIoLinuxDestroy(pCtxInt->AioContext);
+ if (RT_FAILURE(rc))
+ return rc;
+ ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
+ RTMemFree(pCtxInt);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
+{
+ /* Nil means global here. */
+ if (hAioCtx == NIL_RTFILEAIOCTX)
+ return RTFILEAIO_UNLIMITED_REQS; /** @todo r=bird: I'm a bit puzzled by this return value since it
+ * is completely useless in RTFileAioCtxCreate. */
+
+ /* Return 0 if the handle is invalid, it's better than garbage I think... */
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN_RC(pCtxInt, 0);
+
+ return pCtxInt->cRequestsMax;
+}
+
+RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
+{
+ /* Nothing to do. */
+ NOREF(hAioCtx); NOREF(hFile);
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
+{
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Parameter validation.
+ */
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ uint32_t i = cReqs;
+ PRTFILEAIOREQINTERNAL pReqInt = NULL;
+
+ /*
+ * Validate requests and associate with the context.
+ */
+ while (i-- > 0)
+ {
+ pReqInt = pahReqs[i];
+ if (RTFILEAIOREQ_IS_NOT_VALID(pReqInt))
+ {
+ /* Undo everything and stop submitting. */
+ size_t iUndo = cReqs;
+ while (iUndo-- > i)
+ {
+ pReqInt = pahReqs[iUndo];
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+ pReqInt->pCtxInt = NULL;
+ }
+ return VERR_INVALID_HANDLE;
+ }
+
+ pReqInt->AioContext = pCtxInt->AioContext;
+ pReqInt->pCtxInt = pCtxInt;
+ RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
+ }
+
+ do
+ {
+ /*
+ * We cast pahReqs to the Linux iocb structure to avoid copying the requests
+ * into a temporary array. This is possible because the iocb structure is
+ * the first element in the request structure (see PRTFILEAIOCTXINTERNAL).
+ */
+ int cReqsSubmitted = 0;
+ rc = rtFileAsyncIoLinuxSubmit(pCtxInt->AioContext, cReqs,
+ (PLNXKAIOIOCB *)pahReqs,
+ &cReqsSubmitted);
+ if (RT_FAILURE(rc))
+ {
+ /*
+ * We encountered an error.
+ * This means that the first IoCB
+ * is not correctly initialized
+ * (invalid buffer alignment or bad file descriptor).
+ * Revert every request into the prepared state except
+ * the first one which will switch to completed.
+ * Another reason could be insufficient resources.
+ */
+ i = cReqs;
+ while (i-- > 0)
+ {
+ /* Already validated. */
+ pReqInt = pahReqs[i];
+ pReqInt->pCtxInt = NULL;
+ pReqInt->AioContext = 0;
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+ }
+
+ if (rc == VERR_TRY_AGAIN)
+ return VERR_FILE_AIO_INSUFFICIENT_RESSOURCES;
+ else
+ {
+ /* The first request failed. */
+ pReqInt = pahReqs[0];
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ pReqInt->Rc = rc;
+ pReqInt->cbTransfered = 0;
+ return rc;
+ }
+ }
+
+ /* Advance. */
+ cReqs -= cReqsSubmitted;
+ pahReqs += cReqsSubmitted;
+ ASMAtomicAddS32(&pCtxInt->cRequests, cReqsSubmitted);
+
+ } while (cReqs);
+
+ return rc;
+}
+
+
+RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
+ PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
+{
+ /*
+ * Validate the parameters, making sure to always set pcReqs.
+ */
+ AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
+ *pcReqs = 0; /* always set */
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
+
+ /*
+ * Can't wait if there are not requests around.
+ */
+ if ( RT_UNLIKELY(ASMAtomicUoReadS32(&pCtxInt->cRequests) == 0)
+ && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
+ return VERR_FILE_AIO_NO_REQUEST;
+
+ /*
+ * Convert the timeout if specified.
+ */
+ struct timespec *pTimeout = NULL;
+ struct timespec Timeout = {0,0};
+ uint64_t StartNanoTS = 0;
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ Timeout.tv_sec = cMillies / 1000;
+ Timeout.tv_nsec = cMillies % 1000 * 1000000;
+ pTimeout = &Timeout;
+ StartNanoTS = RTTimeNanoTS();
+ }
+
+ /* Wait for at least one. */
+ if (!cMinReqs)
+ cMinReqs = 1;
+
+ /* For the wakeup call. */
+ Assert(pCtxInt->hThreadWait == NIL_RTTHREAD);
+ ASMAtomicWriteHandle(&pCtxInt->hThreadWait, RTThreadSelf());
+
+ /*
+ * Loop until we're woken up, hit an error (incl timeout), or
+ * have collected the desired number of requests.
+ */
+ int rc = VINF_SUCCESS;
+ int cRequestsCompleted = 0;
+ while (!pCtxInt->fWokenUp)
+ {
+ LNXKAIOIOEVENT aPortEvents[AIO_MAXIMUM_REQUESTS_PER_CONTEXT];
+ int cRequestsToWait = RT_MIN(cReqs, AIO_MAXIMUM_REQUESTS_PER_CONTEXT);
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, true);
+ rc = rtFileAsyncIoLinuxGetEvents(pCtxInt->AioContext, cMinReqs, cRequestsToWait, &aPortEvents[0], pTimeout);
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, false);
+ if (RT_FAILURE(rc))
+ break;
+ uint32_t const cDone = rc;
+ rc = VINF_SUCCESS;
+
+ /*
+ * Process received events / requests.
+ */
+ for (uint32_t i = 0; i < cDone; i++)
+ {
+ /*
+ * The iocb is the first element in our request structure.
+ * So we can safely cast it directly to the handle (see above)
+ */
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)aPortEvents[i].pIoCB;
+ AssertPtr(pReqInt);
+ Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
+
+ /** @todo aeichner: The rc field contains the result code
+ * like you can find in errno for the normal read/write ops.
+ * But there is a second field called rc2. I don't know the
+ * purpose for it yet.
+ */
+ if (RT_UNLIKELY(aPortEvents[i].rc < 0))
+ pReqInt->Rc = RTErrConvertFromErrno(-aPortEvents[i].rc); /* Convert to positive value. */
+ else
+ {
+ pReqInt->Rc = VINF_SUCCESS;
+ pReqInt->cbTransfered = aPortEvents[i].rc;
+ }
+
+ /* Mark the request as finished. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt;
+ }
+
+ /*
+ * Done Yet? If not advance and try again.
+ */
+ if (cDone >= cMinReqs)
+ break;
+ cMinReqs -= cDone;
+ cReqs -= cDone;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ /* The API doesn't return ETIMEDOUT, so we have to fix that ourselves. */
+ uint64_t NanoTS = RTTimeNanoTS();
+ uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
+ if (cMilliesElapsed >= cMillies)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+
+ /* The syscall supposedly updates it, but we're paranoid. :-) */
+ Timeout.tv_sec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) / 1000;
+ Timeout.tv_nsec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) % 1000 * 1000000;
+ }
+ }
+
+ /*
+ * Update the context state and set the return value.
+ */
+ *pcReqs = cRequestsCompleted;
+ ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
+ Assert(pCtxInt->hThreadWait == RTThreadSelf());
+ ASMAtomicWriteHandle(&pCtxInt->hThreadWait, NIL_RTTHREAD);
+
+ /*
+ * Clear the wakeup flag and set rc.
+ */
+ if ( pCtxInt->fWokenUp
+ && RT_SUCCESS(rc))
+ {
+ ASMAtomicXchgBool(&pCtxInt->fWokenUp, false);
+ rc = VERR_INTERRUPTED;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
+{
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ /** @todo r=bird: Define the protocol for how to resume work after calling
+ * this function. */
+
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
+
+ /*
+ * Read the thread handle before the status flag.
+ * If we read the handle after the flag we might
+ * end up with an invalid handle because the thread
+ * waiting in RTFileAioCtxWakeup() might get scheduled
+ * before we read the flag and returns.
+ * We can ensure that the handle is valid if fWaiting is true
+ * when reading the handle before the status flag.
+ */
+ RTTHREAD hThread;
+ ASMAtomicReadHandle(&pCtxInt->hThreadWait, &hThread);
+ bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
+ if ( !fWokenUp
+ && fWaiting)
+ {
+ /*
+ * If a thread waits the handle must be valid.
+ * It is possible that the thread returns from
+ * rtFileAsyncIoLinuxGetEvents() before the signal
+ * is send.
+ * This is no problem because we already set fWokenUp
+ * to true which will let the thread return VERR_INTERRUPTED
+ * and the next call to RTFileAioCtxWait() will not
+ * return VERR_INTERRUPTED because signals are not saved
+ * and will simply vanish if the destination thread can't
+ * receive it.
+ */
+ Assert(hThread != NIL_RTTHREAD);
+ RTThreadPoke(hThread);
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/linux/ioqueue-iouringfile-provider.cpp b/src/VBox/Runtime/r3/linux/ioqueue-iouringfile-provider.cpp
new file mode 100644
index 00000000..f6719664
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/ioqueue-iouringfile-provider.cpp
@@ -0,0 +1,940 @@
+/* $Id: ioqueue-iouringfile-provider.cpp $ */
+/** @file
+ * IPRT - I/O queue, Linux io_uring interface I/O file provider.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @page pg_rtioqueue_linux RTIoQueue - Linux io_uring implementation notes
+ * @internal
+ *
+ * The io_uring interface is the most recent interface added to the Linux kernel
+ * to deliver fast and efficient I/O. It was first added with kernel version 5.1 and is
+ * thus not available on most systems as of writing this backend (July 2019).
+ * It supersedes the old async I/O interface and cleans up with some restrictions like
+ * having to disable caching for the file.
+ * The interface is centered around a submission and completion queue to queue multiple new
+ * requests for the kernel to process and get notified about completions to reduce the amount
+ * of context switches to an absolute minimum. It also offers advanced features like
+ * registering a fixed set of memory buffers for I/O upfront to reduce the processing overhead
+ * even more.
+ *
+ * The first implementation will only make use of the basic features and more advanced features
+ * will be added later.
+ * The adept developer probably noticed that the public IPRT I/O queue API resembles the io_uring
+ * interface in many aspects. This is not by accident but to reduce our own overhead as much as possible
+ * while still keeping a consistent platform independent API which allows efficient implementations on
+ * other hosts when they come up.
+ *
+ * The public kernel io_uring interface is completely defined in this file to avoid dragging in additional
+ * dependencies and to avoid compile problems on older hosts missing the interface just like it is done
+ * for the Linux RTFileAio* API The necessary interface definitions and descriptions where retrieved from:
+ * * http://kernel.dk/io_uring.pdf
+ * * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/io_uring.h
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_IOQUEUE
+#include <iprt/ioqueue.h>
+
+#include <iprt/assertcompile.h>
+#include <iprt/asm.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/uio.h>
+
+#include "internal/ioqueue.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** The syscall number of io_uring_setup(). */
+#define LNX_IOURING_SYSCALL_SETUP 425
+/** The syscall number of io_uring_enter(). */
+#define LNX_IOURING_SYSCALL_ENTER 426
+/** The syscall number of io_uring_register(). */
+#define LNX_IOURING_SYSCALL_REGISTER 427
+/** eventfd2() syscall not associated with io_uring but used for kicking waiters. */
+#define LNX_SYSCALL_EVENTFD2 290
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Linux io_uring completion event.
+ */
+typedef struct LNXIOURINGCQE
+{
+ /** Opaque user data associated with the completed request. */
+ uint64_t u64User;
+ /** The status code of the request. */
+ int32_t rcLnx;
+ /** Some flags which are not used as of now. */
+ uint32_t fFlags;
+} LNXIOURINGCQE;
+AssertCompileSize(LNXIOURINGCQE, 16);
+/** Pointer to a Linux io_uring completion event. */
+typedef LNXIOURINGCQE *PLNXIOURINGCQE;
+/** Pointer to a constant linux io_uring completion event. */
+typedef const LNXIOURINGCQE *PCLNXIOURINGCQE;
+
+
+/**
+ * Linux io_uring submission queue entry.
+ */
+typedef struct LNXIOURINGSQE
+{
+ /** The opcode for the request. */
+ uint8_t u8Opc;
+ /** Common flags for the request. */
+ uint8_t u8Flags;
+ /** Assigned I/O priority. */
+ uint16_t u16IoPrio;
+ /** The file descriptor the request is for. */
+ int32_t i32Fd;
+ /** The start offset into the file for the request. */
+ uint64_t u64OffStart;
+ /** Buffer pointer or Pointer to io vector array depending on opcode. */
+ uint64_t u64AddrBufIoVec;
+ /** Size of the buffer in bytes or number of io vectors. */
+ uint32_t u32BufIoVecSz;
+ /** Opcode dependent data. */
+ union
+ {
+ /** Flags for read/write requests. */
+ uint32_t u32KrnlRwFlags;
+ /** Flags for fsync() like requests. */
+ uint32_t u32FsyncFlags;
+ /** Flags for poll() like requests. */
+ uint16_t u16PollFlags;
+ /** Flags for sync_file_range() like requests. */
+ uint32_t u32SyncFileRangeFlags;
+ /** Flags for requests requiring a msg structure. */
+ uint32_t u32MsgFlags;
+ } uOpc;
+ /** Opaque user data associated with the request and returned durign completion. */
+ uint64_t u64User;
+ /** Request type dependent data. */
+ union
+ {
+ /** Fixed buffer index if indicated by the request flags. */
+ uint16_t u16FixedBufIdx;
+ /** Padding to align the structure to 64 bytes. */
+ uint64_t au64Padding[3];
+ } uReq;
+} LNXIOURINGSQE;
+AssertCompileSize(LNXIOURINGSQE, 64);
+/** Pointer to a Linux io_uring submission queue entry. */
+typedef LNXIOURINGSQE *PLNXIOURINGSQE;
+/** Pointer to a constant Linux io_uring submission queue entry. */
+typedef const LNXIOURINGSQE *PCLNXIOURINGSQE;
+
+
+/**
+ * Linux u_ioring SQ ring header structure to maintain the queue.
+ */
+typedef struct LNXIOURINGSQ
+{
+ /** The current head position to fill in new requests. */
+ uint32_t u32OffHead;
+ /** The current tail position the kernel starts processing from. */
+ uint32_t u32OffTail;
+ /** The mask for the head and tail counters to apply to retrieve the index. */
+ uint32_t u32OffRingMask;
+ /** Number of entries in the SQ ring. */
+ uint32_t u32OffRingEntries;
+ /** Flags set asychronously by the kernel. */
+ uint32_t u32OffFlags;
+ /** Counter of dropped requests. */
+ uint32_t u32OffDroppedReqs;
+ /** Offset where to find the array of SQ entries. */
+ uint32_t u32OffArray;
+ /** Reserved. */
+ uint32_t u32Rsvd0;
+ /** Reserved. */
+ uint64_t u64Rsvd1;
+} LNXIOURINGSQ;
+AssertCompileSize(LNXIOURINGSQ, 40);
+/** Pointer to a Linux u_ioring SQ ring header. */
+typedef LNXIOURINGSQ *PLNXIOURINGSQ;
+/** Pointer to a constant Linux u_ioring SQ ring header. */
+typedef const LNXIOURINGSQ *PCLNXIOURINGSQ;
+
+
+/**
+ * Linux io_uring CQ ring header structure to maintain the queue.
+ */
+typedef struct LNXIOURINGCQ
+{
+ /** The current head position the kernel modifies when completion events happen. */
+ uint32_t u32OffHead;
+ /** The current tail position to read completion events from. */
+ uint32_t u32OffTail;
+ /** The mask for the head and tail counters to apply to retrieve the index. */
+ uint32_t u32OffRingMask;
+ /** Number of entries in the CQ ring. */
+ uint32_t u32OffRingEntries;
+ /** Number of CQ overflows happened. */
+ uint32_t u32OffOverflowCnt;
+ /** */
+ uint32_t u32OffCqes;
+ /** Reserved. */
+ uint64_t au64Rsvd0[2];
+} LNXIOURINGCQ;
+AssertCompileSize(LNXIOURINGCQ, 40);
+/** Pointer to a Linux u_ioring CQ ring header. */
+typedef LNXIOURINGCQ *PLNXIOURINGCQ;
+/** Pointer to a constant Linux u_ioring CQ ring header. */
+typedef const LNXIOURINGCQ *PCLNXIOURINGCQ;
+
+
+/**
+ * Linux io_uring parameters passed to io_uring_setup().
+ */
+typedef struct LNXIOURINGPARAMS
+{
+ /** Number of SQ entries requested, must be power of 2. */
+ uint32_t u32SqEntriesCnt;
+ /** Number of CQ entries requested, must be power of 2. */
+ uint32_t u32CqEntriesCnt;
+ /** Flags for the ring, , see LNX_IOURING_SETUP_F_*. */
+ uint32_t u32Flags;
+ /** Affinity of the kernel side SQ polling thread if enabled. */
+ uint32_t u32SqPollCpu;
+ /** Milliseconds after the kernel side SQ polling thread goes to sleep
+ * if there is are no requests to process. */
+ uint32_t u32SqPollIdleMs;
+ /** Reserved. */
+ uint32_t au32Rsvd0[5];
+ /** Offsets returned for the submission queue. */
+ LNXIOURINGSQ SqOffsets;
+ /** Offsets returned for the completion queue. */
+ LNXIOURINGCQ CqOffsets;
+} LNXIOURINGPARAMS;
+/** Pointer to Linux io_uring parameters. */
+typedef LNXIOURINGPARAMS *PLNXIOURINGPARAMS;
+/** Pointer to constant Linux io_uring parameters. */
+typedef const LNXIOURINGPARAMS *PCLNXIOURINGPARAMS;
+
+
+/** @name LNXIOURINGSQE::u8Opc defined opcodes.
+ * @{ */
+/** Opcode to profile the interface, does nothing. */
+#define LNX_IOURING_OPC_NOP 0
+/** preadv() like request. */
+#define LNX_IOURING_OPC_READV 1
+/** pwritev() like request. */
+#define LNX_IOURING_OPC_WRITEV 2
+/** fsync() like request. */
+#define LNX_IOURING_OPC_FSYNC 3
+/** Read request using a fixed preset buffer. */
+#define LNX_IOURING_OPC_READ_FIXED 4
+/** Write request using a fixed preset buffer. */
+#define LNX_IOURING_OPC_WRITE_FIXED 5
+/** Add file descriptor to pollset. */
+#define LNX_IOURING_OPC_POLL_ADD 6
+/** Remove file descriptor from pollset. */
+#define LNX_IOURING_OPC_POLL_REMOVE 7
+/** sync_file_range() like request. */
+#define LNX_IOURING_OPC_SYNC_FILE_RANGE 8
+/** sendmsg() like request. */
+#define LNX_IOURING_OPC_SENDMSG 9
+/** recvmsg() like request. */
+#define LNX_IOURING_OPC_RECVMSG 10
+/** @} */
+
+
+/** @name Additional flags for LNX_IOURING_OPC_FSYNC requests.
+ * @{ */
+/** Sync userdata as well instead of metadata only. */
+#define LNX_IOURING_OPC_FSYNC_DATASYNC RT_BIT_32(0)
+/** @} */
+
+
+/** @name Flags for the LNX_IOURING_SYSCALL_SETUP syscall.
+ * @{ */
+/** The I/O context is polled. */
+#define LNX_IOURING_SETUP_F_IOPOLL RT_BIT_32(0)
+/** The kernel should poll the submission queue. */
+#define LNX_IOURING_SETUP_F_SQPOLL RT_BIT_32(1)
+/** Sets the CPU affinity of the kernel thread polling the submission queue. */
+#define LNX_IOURING_SETUP_F_SQAFF RT_BIT_32(2)
+/** @} */
+
+
+/** @name Flags for LNXIOURINGSQE::u8Flags.
+ * @{ */
+/** The file descriptor was registered before use. */
+#define LNX_IOURING_SQE_F_FIXED_FILE RT_BIT(0)
+/** Complete all active requests before issuing the request with the flag set. */
+#define LNX_IOURING_SQE_F_IO_DRAIN RT_BIT(1)
+/** Links the request with the flag set to the next one. */
+#define LNX_IOURING_SQE_F_IO_LINK RT_BIT(2)
+/** @} */
+
+
+/** @name Magic mmap offsets to map submission and completion queues.
+ * @{ */
+/** Used to map the submission queue. */
+#define LNX_IOURING_MMAP_OFF_SQ UINT64_C(0)
+/** Used to map the completion queue. */
+#define LNX_IOURING_MMAP_OFF_CQ UINT64_C(0x8000000)
+/** Used to map the submission queue entries array. */
+#define LNX_IOURING_MMAP_OFF_SQES UINT64_C(0x10000000)
+/** @} */
+
+
+/** @name Flags used for the SQ ring structure.
+ * @{ */
+/** The kernel thread needs a io_uring_enter() wakeup to continue processing requests. */
+#define LNX_IOURING_SQ_RING_F_NEED_WAKEUP RT_BIT_32(0)
+/** @} */
+
+
+/** @name Flags for the LNX_IOURING_SYSCALL_ENTER syscall.
+ * @{ */
+/** Retrieve completion events for the completion queue. */
+#define LNX_IOURING_ENTER_F_GETEVENTS RT_BIT_32(0)
+/** Wakes the suspended kernel thread processing the requests. */
+#define LNX_IOURING_ENTER_F_SQ_WAKEUP RT_BIT_32(1)
+/** @} */
+
+
+/** @name Opcodes for the LNX_IOURING_SYSCALL_REGISTER syscall.
+ * @{ */
+/** Register a fixed set of buffers. */
+#define LNX_IOURING_REGISTER_OPC_BUFFERS_REGISTER 0
+/** Unregisters a fixed set of buffers registered previously. */
+#define LNX_IOURING_REGISTER_OPC_BUFFERS_UNREGISTER 1
+/** Register a fixed set of files. */
+#define LNX_IOURING_REGISTER_OPC_FILES_REGISTER 2
+/** Unregisters a fixed set of files registered previously. */
+#define LNX_IOURING_REGISTER_OPC_FILES_UNREGISTER 3
+/** Register an eventfd associated with the I/O ring. */
+#define LNX_IOURING_REGISTER_OPC_EVENTFD_REGISTER 4
+/** Unregisters an eventfd registered previously. */
+#define LNX_IOURING_REGISTER_OPC_EVENTFD_UNREGISTER 5
+/** @} */
+
+
+/**
+ * SQ ring structure.
+ *
+ * @note Some members of this structure point to memory shared with the kernel,
+ * hence the volatile keyword.
+ */
+typedef struct RTIOQUEUESQ
+{
+ /** Pointer to the head counter. */
+ volatile uint32_t *pidxHead;
+ /** Pointer to the tail counter. */
+ volatile uint32_t *pidxTail;
+ /** Mask to apply for the counters to get to the index. */
+ uint32_t fRingMask;
+ /** Number of entries in the ring. */
+ uint32_t cEntries;
+ /** Pointer to the global flags. */
+ volatile uint32_t *pfFlags;
+ /** Pointer to the indirection array used for indexing the real SQ entries. */
+ volatile uint32_t *paidxSqes;
+} RTIOQUEUESQ;
+
+
+/**
+ * CQ ring structure.
+ *
+ * @note Some members of this structure point to memory shared with the kernel,
+ * hence the volatile keyword.
+ */
+typedef struct RTIOQUEUECQ
+{
+ /** Pointer to the head counter. */
+ volatile uint32_t *pidxHead;
+ /** Pointer to the tail counter. */
+ volatile uint32_t *pidxTail;
+ /** Mask to apply for the counters to get to the index. */
+ uint32_t fRingMask;
+ /** Number of entries in the ring. */
+ uint32_t cEntries;
+ /** Pointer to the completion entry ring. */
+ volatile LNXIOURINGCQE *paCqes;
+} RTIOQUEUECQ;
+
+
+/**
+ * Internal I/O queue provider instance data.
+ */
+typedef struct RTIOQUEUEPROVINT
+{
+ /** The io_uring file descriptor. */
+ int iFdIoCtx;
+ /** The eventfd file descriptor registered with the ring. */
+ int iFdEvt;
+ /** The submission queue. */
+ RTIOQUEUESQ Sq;
+ /** The currently uncommitted tail for the SQ. */
+ uint32_t idxSqTail;
+ /** Numbere of uncommitted SQEs. */
+ uint32_t cSqesToCommit;
+ /** The completion queue. */
+ RTIOQUEUECQ Cq;
+ /** Pointer to the mapped SQES entries. */
+ PLNXIOURINGSQE paSqes;
+ /** Pointer to the iovec structure used for non S/G requests. */
+ struct iovec *paIoVecs;
+ /** Pointer returned by mmap() for the SQ ring, used for unmapping. */
+ void *pvMMapSqRing;
+ /** Pointer returned by mmap() for the CQ ring, used for unmapping. */
+ void *pvMMapCqRing;
+ /** Pointer returned by mmap() for the SQ entries array, used for unmapping. */
+ void *pvMMapSqes;
+ /** Size of the mapped SQ ring, used for unmapping. */
+ size_t cbMMapSqRing;
+ /** Size of the mapped CQ ring, used for unmapping. */
+ size_t cbMMapCqRing;
+ /** Size of the mapped SQ entries array, used for unmapping. */
+ size_t cbMMapSqes;
+ /** Flag whether the waiter was woken up externally. */
+ volatile bool fExtIntr;
+} RTIOQUEUEPROVINT;
+/** Pointer to the internal I/O queue provider instance data. */
+typedef RTIOQUEUEPROVINT *PRTIOQUEUEPROVINT;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Syscall wrapper for io_uring_setup().
+ *
+ * @returns IPRT status code.
+ * @param cEntries Number of entries for submission and completion queues.
+ * @param pParams Additional parameters for the I/O ring and updated return values
+ * on success.
+ * @param piFdIoCtx Where to store the file descriptor of the I/O ring on success.
+ */
+DECLINLINE(int) rtIoQueueLnxIoURingSetup(uint32_t cEntries, PLNXIOURINGPARAMS pParams, int32_t *piFdIoCtx)
+{
+ int rcLnx = syscall(LNX_IOURING_SYSCALL_SETUP, cEntries, pParams);
+ if (RT_UNLIKELY(rcLnx == -1))
+ return RTErrConvertFromErrno(errno);
+
+ *piFdIoCtx = rcLnx;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Syscall wrapper for io_uring_enter().
+ *
+ * @returns IPRT status code.
+ * @param iFdIoCtx The I/O ring file descriptor.
+ * @param cToSubmit Maximum number of requests waiting for processing.
+ * @param cMinComplete Minimum number of completion events to accumulate before returning.
+ * @param fFlags Flags for io_uring_enter(), see LNX_IOURING_ENTER_F_*.
+ */
+DECLINLINE(int) rtIoQueueLnxIoURingEnter(int32_t iFdIoCtx, uint32_t cToSubmit, uint32_t cMinComplete,
+ uint32_t fFlags)
+{
+ int rcLnx = syscall(LNX_IOURING_SYSCALL_ENTER, iFdIoCtx, cToSubmit, cMinComplete, fFlags,
+ NULL, 0);
+ if (RT_UNLIKELY(rcLnx == -1))
+ return RTErrConvertFromErrno(errno);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Syscall wrapper for io_uring_register().
+ *
+ * @returns IPRT status code.
+ * @param iFdIoCtx The I/O ring file descriptor.
+ * @param uOpc Operation to perform, see LNX_IOURING_REGISTER_OPC_*.
+ * @param pvArg Opaque arguments.
+ * @param cArgs Number of arguments.
+ */
+DECLINLINE(int) rtIoQueueLnxIoURingRegister(int32_t iFdIoCtx, uint32_t uOpc, void *pvArg,
+ uint32_t cArgs)
+{
+ int rcLnx = syscall(LNX_IOURING_SYSCALL_REGISTER, iFdIoCtx, uOpc, pvArg, cArgs);
+ if (RT_UNLIKELY(rcLnx == -1))
+ return RTErrConvertFromErrno(errno);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * mmap() wrapper for the common bits and returning an IPRT status code.
+ *
+ * @returns IPRT status code.
+ * @param iFdIoCtx The I/O ring file descriptor.
+ * @param offMmap The mmap() offset.
+ * @param cbMmap How much to map.
+ * @param ppv Where to store the pointer to the mapping on success.
+ */
+DECLINLINE(int) rtIoQueueLnxIoURingMmap(int iFdIoCtx, off_t offMmap, size_t cbMmap, void **ppv)
+{
+ void *pv = mmap(0, cbMmap, PROT_READ | PROT_WRITE , MAP_SHARED | MAP_POPULATE, iFdIoCtx, offMmap);
+ if (pv != MAP_FAILED)
+ {
+ *ppv = pv;
+ return VINF_SUCCESS;
+ }
+
+ return RTErrConvertFromErrno(errno);
+}
+
+
+/**
+ * eventfd2() syscall wrapper.
+ *
+ * @returns IPRT status code.
+ * @param uValInit The initial value of the maintained counter.
+ * @param fFlags Flags controlling the eventfd behavior.
+ * @param piFdEvt Where to store the file descriptor of the eventfd object on success.
+ */
+DECLINLINE(int) rtIoQueueLnxEventfd2(uint32_t uValInit, uint32_t fFlags, int *piFdEvt)
+{
+ int rcLnx = syscall(LNX_SYSCALL_EVENTFD2, uValInit, fFlags);
+ if (RT_UNLIKELY(rcLnx == -1))
+ return RTErrConvertFromErrno(errno);
+
+ *piFdEvt = rcLnx;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks the completion event queue for pending events.
+ *
+ * @param pThis The provider instance.
+ * @param paCEvt Pointer to the array of completion events.
+ * @param cCEvt Maximum number of completion events the array can hold.
+ * @param pcCEvtSeen Where to store the number of completion events processed.
+ */
+static void rtIoQueueLnxIoURingFileProvCqCheck(PRTIOQUEUEPROVINT pThis, PRTIOQUEUECEVT paCEvt,
+ uint32_t cCEvt, uint32_t *pcCEvtSeen)
+{
+ /* The fencing and atomic accesses are kind of overkill and probably not required (dev paranoia). */
+ ASMReadFence();
+ uint32_t idxCqHead = ASMAtomicReadU32(pThis->Cq.pidxHead);
+ uint32_t idxCqTail = ASMAtomicReadU32(pThis->Cq.pidxTail);
+ ASMReadFence();
+
+ uint32_t cCEvtSeen = 0;
+
+ while ( idxCqTail != idxCqHead
+ && cCEvtSeen < cCEvt)
+ {
+ /* Get the index. */
+ uint32_t idxCqe = idxCqHead & pThis->Cq.fRingMask;
+ volatile LNXIOURINGCQE *pCqe = &pThis->Cq.paCqes[idxCqe];
+
+ paCEvt->pvUser = (void *)(uintptr_t)pCqe->u64User;
+ if (pCqe->rcLnx >= 0)
+ {
+ paCEvt->rcReq = VINF_SUCCESS;
+ paCEvt->cbXfered = (size_t)pCqe->rcLnx;
+ }
+ else
+ paCEvt->rcReq = RTErrConvertFromErrno(-pCqe->rcLnx);
+
+#ifdef RT_STRICT /* poison */
+ memset((void *)pCqe, 0xff, sizeof(*pCqe));
+#endif
+
+ paCEvt++;
+ cCEvtSeen++;
+ idxCqHead++;
+ }
+
+ *pcCEvtSeen = cCEvtSeen;
+
+ /* Paranoia strikes again. */
+ ASMWriteFence();
+ ASMAtomicWriteU32(pThis->Cq.pidxHead, idxCqHead);
+ ASMWriteFence();
+}
+
+
+/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnIsSupported} */
+static DECLCALLBACK(bool) rtIoQueueLnxIoURingFileProv_IsSupported(void)
+{
+ /*
+ * Try to create a simple I/O ring and close it again.
+ * The common code/public API already checked for the proper handle type.
+ */
+ int iFdIoCtx = 0;
+ bool fSupp = false;
+ LNXIOURINGPARAMS Params;
+ RT_ZERO(Params);
+
+ int rc = rtIoQueueLnxIoURingSetup(16, &Params, &iFdIoCtx);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check that we can register an eventfd descriptor to get notified about
+ * completion events while being able to kick the waiter externally out of the wait.
+ */
+ int iFdEvt = 0;
+ rc = rtIoQueueLnxEventfd2(0 /*uValInit*/, 0 /*fFlags*/, &iFdEvt);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtIoQueueLnxIoURingRegister(iFdIoCtx, LNX_IOURING_REGISTER_OPC_EVENTFD_REGISTER,
+ &iFdEvt, 1 /*cArgs*/);
+ if (RT_SUCCESS(rc))
+ fSupp = true;
+
+ int rcLnx = close(iFdEvt); Assert(!rcLnx); RT_NOREF(rcLnx);
+ }
+ int rcLnx = close(iFdIoCtx); Assert(!rcLnx); RT_NOREF(rcLnx);
+ }
+
+ return fSupp;
+}
+
+
+/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnQueueInit} */
+static DECLCALLBACK(int) rtIoQueueLnxIoURingFileProv_QueueInit(RTIOQUEUEPROV hIoQueueProv, uint32_t fFlags,
+ uint32_t cSqEntries, uint32_t cCqEntries)
+{
+ RT_NOREF(fFlags, cCqEntries);
+
+ PRTIOQUEUEPROVINT pThis = hIoQueueProv;
+ LNXIOURINGPARAMS Params;
+ RT_ZERO(Params);
+
+ pThis->cSqesToCommit = 0;
+ pThis->fExtIntr = false;
+
+ int rc = rtIoQueueLnxIoURingSetup(cSqEntries, &Params, &pThis->iFdIoCtx);
+ if (RT_SUCCESS(rc))
+ {
+ /* Map the rings into userspace. */
+ pThis->cbMMapSqRing = Params.SqOffsets.u32OffArray + Params.u32SqEntriesCnt * sizeof(uint32_t);
+ pThis->cbMMapCqRing = Params.CqOffsets.u32OffCqes + Params.u32CqEntriesCnt * sizeof(LNXIOURINGCQE);
+ pThis->cbMMapSqes = Params.u32SqEntriesCnt * sizeof(LNXIOURINGSQE);
+
+ pThis->paIoVecs = (struct iovec *)RTMemAllocZ(Params.u32SqEntriesCnt * sizeof(struct iovec));
+ if (RT_LIKELY(pThis->paIoVecs))
+ {
+ rc = rtIoQueueLnxEventfd2(0 /*uValInit*/, 0 /*fFlags*/, &pThis->iFdEvt);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtIoQueueLnxIoURingRegister(pThis->iFdIoCtx, LNX_IOURING_REGISTER_OPC_EVENTFD_REGISTER, &pThis->iFdEvt, 1 /*cArgs*/);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtIoQueueLnxIoURingMmap(pThis->iFdIoCtx, LNX_IOURING_MMAP_OFF_SQ, pThis->cbMMapSqRing, &pThis->pvMMapSqRing);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtIoQueueLnxIoURingMmap(pThis->iFdIoCtx, LNX_IOURING_MMAP_OFF_CQ, pThis->cbMMapCqRing, &pThis->pvMMapCqRing);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtIoQueueLnxIoURingMmap(pThis->iFdIoCtx, LNX_IOURING_MMAP_OFF_SQES, pThis->cbMMapSqes, &pThis->pvMMapSqes);
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t *pbTmp = (uint8_t *)pThis->pvMMapSqRing;
+
+ pThis->Sq.pidxHead = (uint32_t *)(pbTmp + Params.SqOffsets.u32OffHead);
+ pThis->Sq.pidxTail = (uint32_t *)(pbTmp + Params.SqOffsets.u32OffTail);
+ pThis->Sq.fRingMask = *(uint32_t *)(pbTmp + Params.SqOffsets.u32OffRingMask);
+ pThis->Sq.cEntries = *(uint32_t *)(pbTmp + Params.SqOffsets.u32OffRingEntries);
+ pThis->Sq.pfFlags = (uint32_t *)(pbTmp + Params.SqOffsets.u32OffFlags);
+ pThis->Sq.paidxSqes = (uint32_t *)(pbTmp + Params.SqOffsets.u32OffArray);
+ pThis->idxSqTail = *pThis->Sq.pidxTail;
+
+ pThis->paSqes = (PLNXIOURINGSQE)pThis->pvMMapSqes;
+
+ pbTmp = (uint8_t *)pThis->pvMMapCqRing;
+
+ pThis->Cq.pidxHead = (uint32_t *)(pbTmp + Params.CqOffsets.u32OffHead);
+ pThis->Cq.pidxTail = (uint32_t *)(pbTmp + Params.CqOffsets.u32OffTail);
+ pThis->Cq.fRingMask = *(uint32_t *)(pbTmp + Params.CqOffsets.u32OffRingMask);
+ pThis->Cq.cEntries = *(uint32_t *)(pbTmp + Params.CqOffsets.u32OffRingEntries);
+ pThis->Cq.paCqes = (PLNXIOURINGCQE)(pbTmp + Params.CqOffsets.u32OffCqes);
+ return VINF_SUCCESS;
+ }
+
+ munmap(pThis->pvMMapCqRing, pThis->cbMMapCqRing);
+ }
+
+ munmap(pThis->pvMMapSqRing, pThis->cbMMapSqRing);
+ }
+
+ rc = rtIoQueueLnxIoURingRegister(pThis->iFdIoCtx, LNX_IOURING_REGISTER_OPC_EVENTFD_UNREGISTER, NULL, 0);
+ AssertRC(rc);
+ }
+
+ close(pThis->iFdEvt);
+ }
+
+ RTMemFree(pThis->paIoVecs);
+ }
+
+ int rcLnx = close(pThis->iFdIoCtx); Assert(!rcLnx); RT_NOREF(rcLnx);
+ }
+
+ return rc;
+}
+
+
+/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnQueueDestroy} */
+static DECLCALLBACK(void) rtIoQueueLnxIoURingFileProv_QueueDestroy(RTIOQUEUEPROV hIoQueueProv)
+{
+ PRTIOQUEUEPROVINT pThis = hIoQueueProv;
+
+ int rcLnx = munmap(pThis->pvMMapSqRing, pThis->cbMMapSqRing); Assert(!rcLnx); RT_NOREF(rcLnx);
+ rcLnx = munmap(pThis->pvMMapCqRing, pThis->cbMMapCqRing); Assert(!rcLnx); RT_NOREF(rcLnx);
+ rcLnx = munmap(pThis->pvMMapSqes, pThis->cbMMapSqes); Assert(!rcLnx); RT_NOREF(rcLnx);
+
+ int rc = rtIoQueueLnxIoURingRegister(pThis->iFdIoCtx, LNX_IOURING_REGISTER_OPC_EVENTFD_UNREGISTER, NULL, 0);
+ AssertRC(rc);
+
+ close(pThis->iFdEvt);
+ close(pThis->iFdIoCtx);
+ RTMemFree(pThis->paIoVecs);
+
+ RT_ZERO(pThis);
+}
+
+
+/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnHandleRegister} */
+static DECLCALLBACK(int) rtIoQueueLnxIoURingFileProv_HandleRegister(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle)
+{
+ RT_NOREF(hIoQueueProv, pHandle);
+ /** @todo Add support for fixed file sets later. */
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnHandleDeregister} */
+static DECLCALLBACK(int) rtIoQueueLnxIoURingFileProv_HandleDeregister(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle)
+{
+ RT_NOREF(hIoQueueProv, pHandle);
+ /** @todo Add support for fixed file sets later. */
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnReqPrepare} */
+static DECLCALLBACK(int) rtIoQueueLnxIoURingFileProv_ReqPrepare(RTIOQUEUEPROV hIoQueueProv, PCRTHANDLE pHandle, RTIOQUEUEOP enmOp,
+ uint64_t off, void *pvBuf, size_t cbBuf, uint32_t fReqFlags,
+ void *pvUser)
+{
+ PRTIOQUEUEPROVINT pThis = hIoQueueProv;
+ RT_NOREF(fReqFlags);
+
+ uint32_t idx = pThis->idxSqTail & pThis->Sq.fRingMask;
+ PLNXIOURINGSQE pSqe = &pThis->paSqes[idx];
+ struct iovec *pIoVec = &pThis->paIoVecs[idx];
+
+ pIoVec->iov_base = pvBuf;
+ pIoVec->iov_len = cbBuf;
+
+ pSqe->u8Flags = 0;
+ pSqe->u16IoPrio = 0;
+ pSqe->i32Fd = (int32_t)RTFileToNative(pHandle->u.hFile);
+ pSqe->u64OffStart = off;
+ pSqe->u64AddrBufIoVec = (uint64_t)(uintptr_t)pIoVec;
+ pSqe->u32BufIoVecSz = 1;
+ pSqe->u64User = (uint64_t)(uintptr_t)pvUser;
+
+ switch (enmOp)
+ {
+ case RTIOQUEUEOP_READ:
+ pSqe->u8Opc = LNX_IOURING_OPC_READV;
+ pSqe->uOpc.u32KrnlRwFlags = 0;
+ break;
+ case RTIOQUEUEOP_WRITE:
+ pSqe->u8Opc = LNX_IOURING_OPC_WRITEV;
+ pSqe->uOpc.u32KrnlRwFlags = 0;
+ break;
+ case RTIOQUEUEOP_SYNC:
+ pSqe->u8Opc = LNX_IOURING_OPC_FSYNC;
+ pSqe->uOpc.u32FsyncFlags = 0;
+ break;
+ default:
+ AssertMsgFailedReturn(("Invalid I/O queue operation: %d\n", enmOp),
+ VERR_INVALID_PARAMETER);
+ }
+
+ pThis->Sq.paidxSqes[idx] = idx;
+ pThis->idxSqTail++;
+ pThis->cSqesToCommit++;
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnCommit} */
+static DECLCALLBACK(int) rtIoQueueLnxIoURingFileProv_Commit(RTIOQUEUEPROV hIoQueueProv, uint32_t *pcReqsCommitted)
+{
+ PRTIOQUEUEPROVINT pThis = hIoQueueProv;
+
+ ASMWriteFence();
+ ASMAtomicWriteU32(pThis->Sq.pidxTail, pThis->idxSqTail);
+ ASMWriteFence();
+
+ int rc = rtIoQueueLnxIoURingEnter(pThis->iFdIoCtx, pThis->cSqesToCommit, 0, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ *pcReqsCommitted = pThis->cSqesToCommit;
+ pThis->cSqesToCommit = 0;
+ }
+
+ return rc;
+}
+
+
+/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnEvtWait} */
+static DECLCALLBACK(int) rtIoQueueLnxIoURingFileProv_EvtWait(RTIOQUEUEPROV hIoQueueProv, PRTIOQUEUECEVT paCEvt, uint32_t cCEvt,
+ uint32_t cMinWait, uint32_t *pcCEvt, uint32_t fFlags)
+{
+ PRTIOQUEUEPROVINT pThis = hIoQueueProv;
+ int rc = VINF_SUCCESS;
+ uint32_t cCEvtSeen = 0;
+
+ RT_NOREF(fFlags);
+
+ /*
+ * Check the completion queue first for any completed events which might save us a
+ * context switch later on.
+ */
+ rtIoQueueLnxIoURingFileProvCqCheck(pThis, paCEvt, cCEvt, &cCEvtSeen);
+
+ while ( cCEvtSeen < cMinWait
+ && RT_SUCCESS(rc))
+ {
+ /*
+ * We can employ a blocking read on the event file descriptor, it will return
+ * either when woken up externally or when there are completion events pending.
+ */
+ uint64_t uCnt = 0; /**< The counter value returned upon a successful read(). */
+ ssize_t rcLnx = read(pThis->iFdEvt, &uCnt, sizeof(uCnt));
+ if (rcLnx == sizeof(uCnt))
+ {
+ uint32_t cCEvtThisSeen = 0;
+ rtIoQueueLnxIoURingFileProvCqCheck(pThis, &paCEvt[cCEvtSeen], cCEvt - cCEvtSeen, &cCEvtThisSeen);
+ cCEvtSeen += cCEvtThisSeen;
+
+ /* Whether we got woken up externally. */
+ if (ASMAtomicXchgBool(&pThis->fExtIntr, false))
+ rc = VERR_INTERRUPTED;
+ }
+ else if (rcLnx == -1)
+ rc = RTErrConvertFromErrno(errno);
+ else
+ AssertMsgFailed(("Unexpected read() -> 0\n"));
+ }
+
+ *pcCEvt = cCEvtSeen;
+ return rc;
+}
+
+
+/** @interface_method_impl{RTIOQUEUEPROVVTABLE,pfnEvtWaitWakeup} */
+static DECLCALLBACK(int) rtIoQueueLnxIoURingFileProv_EvtWaitWakeup(RTIOQUEUEPROV hIoQueueProv)
+{
+ PRTIOQUEUEPROVINT pThis = hIoQueueProv;
+ int rc = VINF_SUCCESS;
+
+ if (!ASMAtomicXchgBool(&pThis->fExtIntr, true))
+ {
+ const uint64_t uValAdd = 1;
+ ssize_t rcLnx = write(pThis->iFdEvt, &uValAdd, sizeof(uValAdd));
+
+ Assert(rcLnx == -1 || rcLnx == sizeof(uValAdd));
+ if (rcLnx == -1)
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Async file I/O queue provider virtual method table.
+ */
+RT_DECL_DATA_CONST(RTIOQUEUEPROVVTABLE const) g_RTIoQueueLnxIoURingProv =
+{
+ /** uVersion */
+ RTIOQUEUEPROVVTABLE_VERSION,
+ /** pszId */
+ "LnxIoURingFile",
+ /** cbIoQueueProv */
+ sizeof(RTIOQUEUEPROVINT),
+ /** enmHnd */
+ RTHANDLETYPE_FILE,
+ /** fFlags */
+ 0,
+ /** pfnIsSupported */
+ rtIoQueueLnxIoURingFileProv_IsSupported,
+ /** pfnQueueInit */
+ rtIoQueueLnxIoURingFileProv_QueueInit,
+ /** pfnQueueDestroy */
+ rtIoQueueLnxIoURingFileProv_QueueDestroy,
+ /** pfnHandleRegister */
+ rtIoQueueLnxIoURingFileProv_HandleRegister,
+ /** pfnHandleDeregister */
+ rtIoQueueLnxIoURingFileProv_HandleDeregister,
+ /** pfnReqPrepare */
+ rtIoQueueLnxIoURingFileProv_ReqPrepare,
+ /** pfnReqPrepareSg */
+ NULL,
+ /** pfnCommit */
+ rtIoQueueLnxIoURingFileProv_Commit,
+ /** pfnEvtWait */
+ rtIoQueueLnxIoURingFileProv_EvtWait,
+ /** pfnEvtWaitWakeup */
+ rtIoQueueLnxIoURingFileProv_EvtWaitWakeup,
+ /** uEndMarker */
+ RTIOQUEUEPROVVTABLE_VERSION
+};
+
diff --git a/src/VBox/Runtime/r3/linux/krnlmod-linux.cpp b/src/VBox/Runtime/r3/linux/krnlmod-linux.cpp
new file mode 100644
index 00000000..6d81d530
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/krnlmod-linux.cpp
@@ -0,0 +1,358 @@
+/* $Id: krnlmod-linux.cpp $ */
+/** @file
+ * IPRT - Kernel module, Linux.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYSTEM
+#include <iprt/krnlmod.h>
+#include <iprt/linux/sysfs.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/types.h>
+
+
+/**
+ * Internal kernel information record state.
+ */
+typedef struct RTKRNLMODINFOINT
+{
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** Reference count for the kernel module. */
+ uint32_t cRefKrnlMod;
+ /** Load address of the kernel module. */
+ RTR0UINTPTR uLoadAddr;
+ /** Size of the kernel module. */
+ size_t cbKrnlMod;
+ /** Size of the name in characters including the zero terminator. */
+ size_t cchName;
+ /** Module name - variable in size. */
+ char achName[1];
+} RTKRNLMODINFOINT;
+/** Pointer to the internal kernel module information record. */
+typedef RTKRNLMODINFOINT *PRTKRNLMODINFOINT;
+/** Pointer to a const internal kernel module information record. */
+typedef const RTKRNLMODINFOINT *PCRTKRNLMODINFOINT;
+
+
+
+/**
+ * Destroy the given kernel module information record.
+ *
+ * @param pThis The record to destroy.
+ */
+static void rtKrnlModInfoDestroy(PRTKRNLMODINFOINT pThis)
+{
+ RTMemFree(pThis);
+}
+
+
+static int rtKrnlModLinuxReadIntFileDef(unsigned uBase, int64_t *pi64, int64_t i64Def,
+ const char *pszName, const char *pszPath)
+{
+ int rc = RTLinuxSysFsReadIntFile(uBase, pi64, "module/%s/%s", pszName, pszPath);
+ if (rc == VERR_FILE_NOT_FOUND)
+ {
+ *pi64 = i64Def;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+/**
+ * Creates a new kernel module information record for the given module.
+ *
+ * @returns IPRT status code.
+ * @param pszName The kernel module name.
+ * @param phKrnlModInfo Where to store the handle to the kernel module information record
+ * on success.
+ */
+static int rtKrnlModLinuxInfoCreate(const char *pszName, PRTKRNLMODINFO phKrnlModInfo)
+{
+ int rc = VINF_SUCCESS;
+ size_t cchName = strlen(pszName) + 1;
+ PRTKRNLMODINFOINT pThis = (PRTKRNLMODINFOINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTKRNLMODINFOINT, achName[cchName]));
+ if (RT_LIKELY(pThis))
+ {
+ memcpy(&pThis->achName[0], pszName, cchName);
+ pThis->cchName = cchName;
+ pThis->cRefs = 1;
+
+ int64_t iTmp = 0;
+ rc = rtKrnlModLinuxReadIntFileDef(10, &iTmp, 0, pszName, "refcnt");
+ if (RT_SUCCESS(rc))
+ pThis->cRefKrnlMod = (uint32_t)iTmp;
+
+ rc = rtKrnlModLinuxReadIntFileDef(10, &iTmp, 0, pszName, "coresize");
+ if (RT_SUCCESS(rc))
+ pThis->cbKrnlMod = iTmp;
+
+ rc = rtKrnlModLinuxReadIntFileDef(16, &iTmp, 0, pszName, "sections/.text");
+ if (RT_SUCCESS(rc))
+ pThis->uLoadAddr = iTmp;
+
+ if (RT_SUCCESS(rc))
+ *phKrnlModInfo = pThis;
+ else
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTKrnlModQueryLoaded(const char *pszName, bool *pfLoaded)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfLoaded, VERR_INVALID_POINTER);
+
+ int rc = RTLinuxSysFsExists("module/%s", pszName);
+ if (rc == VINF_SUCCESS)
+ *pfLoaded = true;
+ else if (rc == VERR_FILE_NOT_FOUND)
+ {
+ *pfLoaded = false;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTKrnlModLoadedQueryInfo(const char *pszName, PRTKRNLMODINFO phKrnlModInfo)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(phKrnlModInfo, VERR_INVALID_POINTER);
+
+ int rc = RTLinuxSysFsExists("module/%s", pszName);
+ if (rc == VINF_SUCCESS)
+ rc = rtKrnlModLinuxInfoCreate(pszName, phKrnlModInfo);
+ else if (rc == VERR_FILE_NOT_FOUND)
+ rc = VERR_NOT_FOUND;
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTKrnlModLoadedGetCount(void)
+{
+ uint32_t cKmodsLoaded = 0;
+
+ RTDIR hDir = NULL;
+ int rc = RTDirOpen(&hDir, "/sys/module");
+ if (RT_SUCCESS(rc))
+ {
+ RTDIRENTRY DirEnt;
+ rc = RTDirRead(hDir, &DirEnt, NULL);
+ while (RT_SUCCESS(rc))
+ {
+ if (!RTDirEntryIsStdDotLink(&DirEnt))
+ cKmodsLoaded++;
+ rc = RTDirRead(hDir, &DirEnt, NULL);
+ }
+
+ RTDirClose(hDir);
+ }
+
+
+ return cKmodsLoaded;
+}
+
+
+RTDECL(int) RTKrnlModLoadedQueryInfoAll(PRTKRNLMODINFO pahKrnlModInfo, uint32_t cEntriesMax,
+ uint32_t *pcEntries)
+{
+ if (cEntriesMax > 0)
+ AssertPtrReturn(pahKrnlModInfo, VERR_INVALID_POINTER);
+
+ uint32_t cKmodsLoaded = RTKrnlModLoadedGetCount();
+ if (cEntriesMax < cKmodsLoaded)
+ {
+ if (*pcEntries)
+ *pcEntries = cKmodsLoaded;
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ RTDIR hDir = NULL;
+ int rc = RTDirOpen(&hDir, "/sys/module");
+ if (RT_SUCCESS(rc))
+ {
+ unsigned idxKrnlModInfo = 0;
+ RTDIRENTRY DirEnt;
+
+ rc = RTDirRead(hDir, &DirEnt, NULL);
+ while (RT_SUCCESS(rc))
+ {
+ if (!RTDirEntryIsStdDotLink(&DirEnt))
+ {
+ rc = rtKrnlModLinuxInfoCreate(DirEnt.szName, &pahKrnlModInfo[idxKrnlModInfo]);
+ if (RT_SUCCESS(rc))
+ idxKrnlModInfo++;
+ }
+
+ if (RT_SUCCESS(rc))
+ rc = RTDirRead(hDir, &DirEnt, NULL);
+ }
+
+ if (rc == VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+ else if (RT_FAILURE(rc))
+ {
+ /* Rollback */
+ while (idxKrnlModInfo-- > 0)
+ RTKrnlModInfoRelease(pahKrnlModInfo[idxKrnlModInfo]);
+ }
+
+ if (*pcEntries)
+ *pcEntries = cKmodsLoaded;
+
+ RTDirClose(hDir);
+ }
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoRetain(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoRelease(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ if (!pThis)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0)
+ rtKrnlModInfoDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoGetRefCnt(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->cRefKrnlMod;
+}
+
+
+RTDECL(const char *) RTKrnlModInfoGetName(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, NULL);
+
+ return &pThis->achName[0];
+}
+
+
+RTDECL(const char *) RTKrnlModInfoGetFilePath(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, NULL);
+
+ return NULL;
+}
+
+
+RTDECL(size_t) RTKrnlModInfoGetSize(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->cbKrnlMod;
+}
+
+
+RTDECL(RTR0UINTPTR) RTKrnlModInfoGetLoadAddr(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->uLoadAddr;
+}
+
+
+RTDECL(int) RTKrnlModInfoQueryRefModInfo(RTKRNLMODINFO hKrnlModInfo, uint32_t idx,
+ PRTKRNLMODINFO phKrnlModInfoRef)
+{
+ RT_NOREF3(hKrnlModInfo, idx, phKrnlModInfoRef);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTDECL(int) RTKrnlModLoadByName(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModLoadByPath(const char *pszPath)
+{
+ AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModUnloadByName(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
diff --git a/src/VBox/Runtime/r3/linux/mp-linux.cpp b/src/VBox/Runtime/r3/linux/mp-linux.cpp
new file mode 100644
index 00000000..935dfd22
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/mp-linux.cpp
@@ -0,0 +1,328 @@
+/* $Id: mp-linux.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Linux.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYSTEM
+#include <stdio.h>
+#include <errno.h>
+
+#include <iprt/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/cpuset.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/linux/sysfs.h>
+
+
+/**
+ * Internal worker that determines the max possible CPU count.
+ *
+ * @returns Max cpus.
+ */
+static RTCPUID rtMpLinuxMaxCpus(void)
+{
+#if 0 /* this doesn't do the right thing :-/ */
+ int cMax = sysconf(_SC_NPROCESSORS_CONF);
+ Assert(cMax >= 1);
+ return cMax;
+#else
+ static uint32_t s_cMax = 0;
+ if (!s_cMax)
+ {
+ int cMax = 1;
+ for (unsigned iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
+ if (RTLinuxSysFsExists("devices/system/cpu/cpu%d", iCpu))
+ cMax = iCpu + 1;
+ ASMAtomicUoWriteU32((uint32_t volatile *)&s_cMax, cMax);
+ return cMax;
+ }
+ return s_cMax;
+#endif
+}
+
+/**
+ * Internal worker that picks the processor speed in MHz from /proc/cpuinfo.
+ *
+ * @returns CPU frequency.
+ */
+static uint32_t rtMpLinuxGetFrequency(RTCPUID idCpu)
+{
+ FILE *pFile = fopen("/proc/cpuinfo", "r");
+ if (!pFile)
+ return 0;
+
+ char sz[256];
+ RTCPUID idCpuFound = NIL_RTCPUID;
+ uint32_t Frequency = 0;
+ while (fgets(sz, sizeof(sz), pFile))
+ {
+ char *psz;
+ if ( !strncmp(sz, RT_STR_TUPLE("processor"))
+ && (sz[10] == ' ' || sz[10] == '\t' || sz[10] == ':')
+ && (psz = strchr(sz, ':')))
+ {
+ psz += 2;
+ int64_t iCpu;
+ int rc = RTStrToInt64Ex(psz, NULL, 0, &iCpu);
+ if (RT_SUCCESS(rc))
+ idCpuFound = iCpu;
+ }
+ else if ( idCpu == idCpuFound
+ && !strncmp(sz, RT_STR_TUPLE("cpu MHz"))
+ && (sz[10] == ' ' || sz[10] == '\t' || sz[10] == ':')
+ && (psz = strchr(sz, ':')))
+ {
+ psz += 2;
+ int64_t v;
+ int rc = RTStrToInt64Ex(psz, &psz, 0, &v);
+ if (RT_SUCCESS(rc))
+ {
+ Frequency = v;
+ break;
+ }
+ }
+ }
+ fclose(pFile);
+ return Frequency;
+}
+
+
+/** @todo RTmpCpuId(). */
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < rtMpLinuxMaxCpus() ? (int)idCpu : -1;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu < rtMpLinuxMaxCpus() ? iCpu : NIL_RTCPUID;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return rtMpLinuxMaxCpus() - 1;
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ /** @todo check if there is a simpler interface than this... */
+ int64_t i = 0;
+ int rc = RTLinuxSysFsReadIntFile(0, &i, "devices/system/cpu/cpu%d/online", (int)idCpu);
+ if ( RT_FAILURE(rc)
+ && RTLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu))
+ {
+ /** @todo Assert(!RTLinuxSysFsExists("devices/system/cpu/cpu%d/online",
+ * (int)idCpu));
+ * Unfortunately, the online file wasn't always world readable (centos
+ * 2.6.18-164). */
+ i = 1;
+ rc = VINF_SUCCESS;
+ }
+
+ AssertMsg(i == 0 || i == -1 || i == 1, ("i=%d\n", i));
+ return RT_SUCCESS(rc) && i != 0;
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ /** @todo check this up with hotplugging! */
+ return RTLinuxSysFsExists("devices/system/cpu/cpu%d", (int)idCpu);
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCpuSetEmpty(pSet);
+ RTCPUID cMax = rtMpLinuxMaxCpus();
+ for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
+ if (RTMpIsCpuPossible(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ RTCPUSET Set;
+ RTMpGetSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+
+
+RTDECL(RTCPUID) RTMpGetCoreCount(void)
+{
+ RTCPUID cMax = rtMpLinuxMaxCpus();
+ uint32_t *paidCores = (uint32_t *)alloca(sizeof(paidCores[0]) * (cMax + 1));
+ uint32_t *paidPckgs = (uint32_t *)alloca(sizeof(paidPckgs[0]) * (cMax + 1));
+ uint32_t cCores = 0;
+ for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
+ {
+ if (RTMpIsCpuPossible(idCpu))
+ {
+ int64_t idCore = 0;
+ int64_t idPckg = 0;
+
+ int rc = RTLinuxSysFsReadIntFile(0, &idCore, "devices/system/cpu/cpu%d/topology/core_id", (int)idCpu);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsReadIntFile(0, &idPckg, "devices/system/cpu/cpu%d/topology/physical_package_id", (int)idCpu);
+
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t i;
+
+ for (i = 0; i < cCores; i++)
+ if ( paidCores[i] == (uint32_t)idCore
+ && paidPckgs[i] == (uint32_t)idPckg)
+ break;
+ if (i >= cCores)
+ {
+ paidCores[cCores] = (uint32_t)idCore;
+ paidPckgs[cCores] = (uint32_t)idPckg;
+ cCores++;
+ }
+ }
+ }
+ }
+ Assert(cCores > 0);
+ return cCores;
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ RTCpuSetEmpty(pSet);
+ RTCPUID cMax = rtMpLinuxMaxCpus();
+ for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
+ if (RTMpIsCpuOnline(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ RTCPUSET Set;
+ RTMpGetOnlineSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
+{
+ RTCPUID cMax = rtMpLinuxMaxCpus();
+ uint32_t *paidCores = (uint32_t *)alloca(sizeof(paidCores[0]) * (cMax + 1));
+ uint32_t *paidPckgs = (uint32_t *)alloca(sizeof(paidPckgs[0]) * (cMax + 1));
+ uint32_t cCores = 0;
+ for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
+ {
+ if (RTMpIsCpuOnline(idCpu))
+ {
+ int64_t idCore = 0;
+ int64_t idPckg = 0;
+
+ int rc = RTLinuxSysFsReadIntFile(0, &idCore, "devices/system/cpu/cpu%d/topology/core_id", (int)idCpu);
+ if (RT_SUCCESS(rc))
+ rc = RTLinuxSysFsReadIntFile(0, &idPckg, "devices/system/cpu/cpu%d/topology/physical_package_id", (int)idCpu);
+
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t i;
+
+ for (i = 0; i < cCores; i++)
+ if ( paidCores[i] == idCore
+ && paidPckgs[i] == idPckg)
+ break;
+ if (i >= cCores)
+ {
+ paidCores[cCores] = idCore;
+ paidPckgs[cCores] = idPckg;
+ cCores++;
+ }
+ }
+ }
+ }
+ Assert(cCores > 0);
+ return cCores;
+}
+
+
+
+RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
+{
+ int64_t kHz = 0;
+ int rc = RTLinuxSysFsReadIntFile(0, &kHz, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq", (int)idCpu);
+ if (RT_FAILURE(rc))
+ {
+ /*
+ * The file may be just unreadable - in that case use plan B, i.e.
+ * /proc/cpuinfo to get the data we want. The assumption is that if
+ * cpuinfo_cur_freq doesn't exist then the speed won't change, and
+ * thus cur == max. If it does exist then cpuinfo contains the
+ * current frequency.
+ */
+ kHz = rtMpLinuxGetFrequency(idCpu) * 1000;
+ }
+ return (kHz + 999) / 1000;
+}
+
+
+RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
+{
+ int64_t kHz = 0;
+ int rc = RTLinuxSysFsReadIntFile(0, &kHz, "devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu);
+ if (RT_FAILURE(rc))
+ {
+ /*
+ * Check if the file isn't there - if it is there, then /proc/cpuinfo
+ * would provide current frequency information, which is wrong.
+ */
+ if (!RTLinuxSysFsExists("devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", (int)idCpu))
+ kHz = rtMpLinuxGetFrequency(idCpu) * 1000;
+ else
+ kHz = 0;
+ }
+ return (kHz + 999) / 1000;
+}
diff --git a/src/VBox/Runtime/r3/linux/rtProcInitExePath-linux.cpp b/src/VBox/Runtime/r3/linux/rtProcInitExePath-linux.cpp
new file mode 100644
index 00000000..bd3edc12
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/rtProcInitExePath-linux.cpp
@@ -0,0 +1,79 @@
+/* $Id: rtProcInitExePath-linux.cpp $ */
+/** @file
+ * IPRT - rtProcInitName, Linux.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <unistd.h>
+#include <errno.h>
+
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include "internal/process.h"
+#include "internal/path.h"
+
+
+DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath)
+{
+ /*
+ * Read the /proc/self/exe link, convert to native and return it.
+ */
+ int cchLink = readlink("/proc/self/exe", pszPath, cchPath - 1);
+ if (cchLink > 0 && (size_t)cchLink <= cchPath - 1)
+ {
+ pszPath[cchLink] = '\0';
+
+ char const *pszTmp;
+ int rc = rtPathFromNative(&pszTmp, pszPath, NULL);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, cchLink, pszPath), rc);
+ if (pszTmp != pszPath)
+ {
+ rc = RTStrCopy(pszPath, cchPath, pszTmp);
+ rtPathFreeIprt(pszTmp, pszPath);
+ }
+ return rc;
+ }
+
+ int err = errno;
+ int rc = RTErrConvertFromErrno(err);
+ AssertMsgFailed(("rc=%Rrc err=%d cchLink=%d\n", rc, err, cchLink));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/linux/sched-linux.cpp b/src/VBox/Runtime/r3/linux/sched-linux.cpp
new file mode 100644
index 00000000..0b7370e0
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/sched-linux.cpp
@@ -0,0 +1,707 @@
+/* $Id: sched-linux.cpp $ */
+/** @file
+ * IPRT - Scheduling, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/*
+ * !WARNING!
+ *
+ * When talking about lowering and raising priority, we do *NOT* refer to
+ * the common direction priority values takes on unix systems (lower means
+ * higher). So, when we raise the priority of a linux thread the nice
+ * value will decrease, and when we lower the priority the nice value
+ * will increase. Confusing, right?
+ *
+ * !WARNING!
+ */
+
+
+
+/** @def THREAD_LOGGING
+ * Be very careful with enabling this, it may cause deadlocks when combined
+ * with the 'thread' logging prefix.
+ */
+#ifdef DOXYGEN_RUNNING
+# define THREAD_LOGGING
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <errno.h>
+#include <pthread.h>
+#include <limits.h>
+#include <sched.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#include <iprt/thread.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/errcore.h>
+#include "internal/sched.h"
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/** Array scheduler attributes corresponding to each of the thread types.
+ * @internal */
+typedef struct PROCPRIORITYTYPE
+{
+ /** For sanity include the array index. */
+ RTTHREADTYPE enmType;
+ /** The thread priority or nice delta - depends on which priority type. */
+ int iPriority;
+} PROCPRIORITYTYPE;
+
+
+/**
+ * Configuration of one priority.
+ * @internal
+ */
+typedef struct
+{
+ /** The priority. */
+ RTPROCPRIORITY enmPriority;
+ /** The name of this priority. */
+ const char *pszName;
+ /** The process nice value. */
+ int iNice;
+ /** The delta applied to the iPriority value. */
+ int iDelta;
+ /** Array scheduler attributes corresponding to each of the thread types. */
+ const PROCPRIORITYTYPE *paTypes;
+} PROCPRIORITY;
+
+
+/**
+ * Saved priority settings
+ * @internal
+ */
+typedef struct
+{
+ /** Process priority. */
+ int iPriority;
+ /** Process level. */
+ struct sched_param SchedParam;
+ /** Process level. */
+ int iPolicy;
+ /** pthread level. */
+ struct sched_param PthreadSchedParam;
+ /** pthread level. */
+ int iPthreadPolicy;
+} SAVEDPRIORITY, *PSAVEDPRIORITY;
+
+
+/**
+ * Priorities for checking by separate thread
+ * @internal
+ */
+typedef struct
+{
+ /** The current thread priority to assume first. */
+ int iCurrent;
+ /** The thread priority to try set afterwards. */
+ int iNew;
+} VALIDATORPRIORITYPAIR, *PVALIDATORPRIORITYPAIR;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Deltas for a process in which we are not restricted
+ * to only be lowering the priority.
+ */
+static const PROCPRIORITYTYPE g_aTypesLinuxFree[RTTHREADTYPE_END] =
+{
+ { RTTHREADTYPE_INVALID, -999999999 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, +3 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 },
+ { RTTHREADTYPE_EMULATION, +1 },
+ { RTTHREADTYPE_DEFAULT, 0 },
+ { RTTHREADTYPE_GUI, 0 },
+ { RTTHREADTYPE_MAIN_WORKER, 0 },
+ { RTTHREADTYPE_VRDP_IO, -1 },
+ { RTTHREADTYPE_DEBUGGER, -1 },
+ { RTTHREADTYPE_MSG_PUMP, -2 },
+ { RTTHREADTYPE_IO, -3 },
+ { RTTHREADTYPE_TIMER, -4 }
+};
+
+/**
+ * Deltas for a process in which we are restricted and can only lower the priority.
+ */
+static const PROCPRIORITYTYPE g_aTypesLinuxRestricted[RTTHREADTYPE_END] =
+{
+ { RTTHREADTYPE_INVALID, -999999999 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, +3 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 },
+ { RTTHREADTYPE_EMULATION, +1 },
+ { RTTHREADTYPE_DEFAULT, 0 },
+ { RTTHREADTYPE_GUI, 0 },
+ { RTTHREADTYPE_MAIN_WORKER, 0 },
+ { RTTHREADTYPE_VRDP_IO, 0 },
+ { RTTHREADTYPE_DEBUGGER, 0 },
+ { RTTHREADTYPE_MSG_PUMP, 0 },
+ { RTTHREADTYPE_IO, 0 },
+ { RTTHREADTYPE_TIMER, 0 }
+};
+
+/**
+ * All threads have the same priority.
+ *
+ * This is typically chosen when we find that we can't raise the priority
+ * to the process default of a thread created by a low priority thread.
+ */
+static const PROCPRIORITYTYPE g_aTypesLinuxFlat[RTTHREADTYPE_END] =
+{
+ { RTTHREADTYPE_INVALID, -999999999 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, 0 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, 0 },
+ { RTTHREADTYPE_EMULATION, 0 },
+ { RTTHREADTYPE_DEFAULT, 0 },
+ { RTTHREADTYPE_GUI, 0 },
+ { RTTHREADTYPE_MAIN_WORKER, 0 },
+ { RTTHREADTYPE_VRDP_IO, 0 },
+ { RTTHREADTYPE_DEBUGGER, 0 },
+ { RTTHREADTYPE_MSG_PUMP, 0 },
+ { RTTHREADTYPE_IO, 0 },
+ { RTTHREADTYPE_TIMER, 0 }
+};
+
+/**
+ * Process and thread level priority, full access at thread level.
+ */
+static const PROCPRIORITY g_aUnixConfigs[] =
+{
+ { RTPROCPRIORITY_FLAT, "Flat", 0, 0, g_aTypesLinuxFlat },
+ { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesLinuxFree },
+ { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesLinuxFlat },
+ { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesLinuxFree },
+ { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesLinuxFlat },
+ { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesLinuxFree },
+ { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesLinuxFlat },
+ { RTPROCPRIORITY_LOW, "Low", 19, 19, g_aTypesLinuxFlat },
+ { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesLinuxRestricted },
+ { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesLinuxRestricted },
+ { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesLinuxRestricted },
+ { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesLinuxFree },
+ { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesLinuxRestricted },
+ { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesLinuxFlat },
+ { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesLinuxFree },
+ { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesLinuxFree },
+ { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesLinuxFree },
+ { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesLinuxFree },
+ { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesLinuxFree },
+ { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesLinuxRestricted },
+ { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesLinuxRestricted },
+ { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesLinuxRestricted },
+ { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesLinuxRestricted },
+ { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesLinuxRestricted },
+ { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesLinuxFlat },
+ { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesLinuxFlat },
+ { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesLinuxFlat },
+ { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesLinuxFlat },
+ { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesLinuxFlat }
+};
+
+/**
+ * The dynamic default priority configuration.
+ *
+ * This will be recalulated at runtime depending on what the
+ * system allow us to do and what the current priority is.
+ */
+static PROCPRIORITY g_aDefaultPriority =
+{
+ RTPROCPRIORITY_LOW, "Default", 0, 0, g_aTypesLinuxRestricted
+};
+
+/** Pointer to the current priority configuration. */
+static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority;
+
+/** Set if we can raise the priority of a thread beyond the default.
+ *
+ * It might mean we have the CAP_SYS_NICE capability or that the
+ * process's RLIMIT_NICE is higher than the priority of the thread
+ * calculating the defaults.
+ */
+static bool g_fCanRaisePriority = false;
+
+/** Set if we can restore the priority after having temporarily lowered or raised it. */
+static bool g_fCanRestorePriority = false;
+
+/** Set if we can NOT raise the priority to the process default in a thread
+ * created by a thread running below the process default.
+ */
+static bool g_fScrewedUpMaxPriorityLimitInheritance = true;
+
+/** The highest priority we can set. */
+static int g_iMaxPriority = 0;
+
+/** The lower priority we can set. */
+static int g_iMinPriority = 19;
+
+/** Set when we've successfully determined the capabilities of the process and kernel. */
+static bool g_fInitialized = false;
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+/**
+ * Saves all the scheduling attributes we can think of.
+ */
+static void rtSchedNativeSave(PSAVEDPRIORITY pSave)
+{
+ memset(pSave, 0xff, sizeof(*pSave));
+
+ errno = 0;
+ pSave->iPriority = getpriority(PRIO_PROCESS, 0 /* current process */);
+ Assert(errno == 0);
+
+ errno = 0;
+ sched_getparam(0 /* current process */, &pSave->SchedParam);
+ Assert(errno == 0);
+
+ errno = 0;
+ pSave->iPolicy = sched_getscheduler(0 /* current process */);
+ Assert(errno == 0);
+
+ int rc = pthread_getschedparam(pthread_self(), &pSave->iPthreadPolicy, &pSave->PthreadSchedParam);
+ Assert(rc == 0); NOREF(rc);
+}
+
+
+/**
+ * Restores scheduling attributes.
+ * Most of this won't work right, but anyway...
+ */
+static void rtSchedNativeRestore(PSAVEDPRIORITY pSave)
+{
+ setpriority(PRIO_PROCESS, 0, pSave->iPriority);
+ sched_setscheduler(0, pSave->iPolicy, &pSave->SchedParam);
+ sched_setparam(0, &pSave->SchedParam);
+ pthread_setschedparam(pthread_self(), pSave->iPthreadPolicy, &pSave->PthreadSchedParam);
+}
+
+
+/**
+ * Called on the priority proxy thread if requested running, otherwise
+ * rtSchedRunThread() calls it directly.
+ */
+static DECLCALLBACK(int) rtSchedRunThreadCallback(pthread_t *pThread, void *(*pfnThread)(void *pvArg), void *pvArg)
+{
+ int rc = pthread_create(pThread, NULL, pfnThread, pvArg);
+ if (!rc)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(rc);
+}
+
+
+/**
+ * Starts a worker thread and wait for it to complete.
+ *
+ * We cannot use RTThreadCreate since we're already owner of the RW lock.
+ */
+static int rtSchedRunThread(void *(*pfnThread)(void *pvArg), void *pvArg, bool fUsePriorityProxy)
+{
+ /*
+ * Create the thread.
+ */
+ pthread_t Thread;
+ int rc;
+#ifndef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+ RT_NOREF(fUsePriorityProxy);
+#else
+ if ( fUsePriorityProxy
+ && rtThreadPosixPriorityProxyStart())
+ rc = rtThreadPosixPriorityProxyCall(NULL, (PFNRT)rtSchedRunThreadCallback, 3, &Thread, pfnThread, pvArg);
+ else
+#endif
+ rc = rtSchedRunThreadCallback(&Thread, pfnThread, pvArg);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Wait for the thread to finish.
+ */
+ void *pvRet = (void *)-1;
+ do
+ {
+ rc = pthread_join(Thread, &pvRet);
+ } while (rc == EINTR);
+ if (rc)
+ return RTErrConvertFromErrno(rc);
+ return (int)(uintptr_t)pvRet;
+ }
+ return rc;
+}
+
+
+static void rtSchedDumpPriority(void)
+{
+#ifdef THREAD_LOGGING
+ Log(("Priority: g_fCanRaisePriority=%RTbool g_fCanRestorePriority=%RTbool g_fScrewedUpMaxPriorityLimitInheritance=%RTbool\n",
+ g_fCanRaisePriority, g_fCanRestorePriority, g_fScrewedUpMaxPriorityLimitInheritance));
+ Log(("Priority: g_iMaxPriority=%d g_iMinPriority=%d\n", g_iMaxPriority, g_iMinPriority));
+ Log(("Priority: enmPriority=%d \"%s\" iNice=%d iDelta=%d\n",
+ g_pProcessPriority->enmPriority,
+ g_pProcessPriority->pszName,
+ g_pProcessPriority->iNice,
+ g_pProcessPriority->iDelta));
+ Log(("Priority: %2d INFREQUENT_POLLER = %d\n", RTTHREADTYPE_INFREQUENT_POLLER, g_pProcessPriority->paTypes[RTTHREADTYPE_INFREQUENT_POLLER].iPriority));
+ Log(("Priority: %2d MAIN_HEAVY_WORKER = %d\n", RTTHREADTYPE_MAIN_HEAVY_WORKER, g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_HEAVY_WORKER].iPriority));
+ Log(("Priority: %2d EMULATION = %d\n", RTTHREADTYPE_EMULATION , g_pProcessPriority->paTypes[RTTHREADTYPE_EMULATION ].iPriority));
+ Log(("Priority: %2d DEFAULT = %d\n", RTTHREADTYPE_DEFAULT , g_pProcessPriority->paTypes[RTTHREADTYPE_DEFAULT ].iPriority));
+ Log(("Priority: %2d GUI = %d\n", RTTHREADTYPE_GUI , g_pProcessPriority->paTypes[RTTHREADTYPE_GUI ].iPriority));
+ Log(("Priority: %2d MAIN_WORKER = %d\n", RTTHREADTYPE_MAIN_WORKER , g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_WORKER ].iPriority));
+ Log(("Priority: %2d VRDP_IO = %d\n", RTTHREADTYPE_VRDP_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_VRDP_IO ].iPriority));
+ Log(("Priority: %2d DEBUGGER = %d\n", RTTHREADTYPE_DEBUGGER , g_pProcessPriority->paTypes[RTTHREADTYPE_DEBUGGER ].iPriority));
+ Log(("Priority: %2d MSG_PUMP = %d\n", RTTHREADTYPE_MSG_PUMP , g_pProcessPriority->paTypes[RTTHREADTYPE_MSG_PUMP ].iPriority));
+ Log(("Priority: %2d IO = %d\n", RTTHREADTYPE_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_IO ].iPriority));
+ Log(("Priority: %2d TIMER = %d\n", RTTHREADTYPE_TIMER , g_pProcessPriority->paTypes[RTTHREADTYPE_TIMER ].iPriority));
+#endif
+}
+
+
+/**
+ * This just checks if it can raise the priority after having been
+ * created by a thread with a low priority.
+ *
+ * @returns zero on success, non-zero on failure.
+ * @param pvUser The priority of the parent before it was lowered (cast to int).
+ */
+static void *rtSchedNativeSubProberThread(void *pvUser)
+{
+ int iPriority = getpriority(PRIO_PROCESS, 0);
+ Assert(iPriority == g_iMinPriority);
+
+ if (setpriority(PRIO_PROCESS, 0, iPriority + 1))
+ return (void *)-1;
+ if (setpriority(PRIO_PROCESS, 0, (int)(intptr_t)pvUser))
+ return (void *)-1;
+ return (void *)0;
+}
+
+
+/**
+ * The prober thread.
+ * We don't want to mess with the priority of the calling thread.
+ *
+ * @remark This is pretty presumptive stuff, but if it works on Linux and
+ * FreeBSD it does what I want.
+ */
+static void *rtSchedNativeProberThread(void *pvUser)
+{
+ NOREF(pvUser);
+ SAVEDPRIORITY SavedPriority;
+ rtSchedNativeSave(&SavedPriority);
+
+ /*
+ * Check if we can get higher priority (typically only root can do this).
+ * (Won't work right if our priority is -19 to start with, but what the heck.)
+ *
+ * We assume that the priority range is -19 to 19. Should probably find the right
+ * define for this.
+ */
+ int iStart = getpriority(PRIO_PROCESS, 0);
+ int i = iStart;
+ while (i-- > -20)
+ if (setpriority(PRIO_PROCESS, 0, i))
+ break;
+ g_iMaxPriority = getpriority(PRIO_PROCESS, 0);
+ g_fCanRaisePriority = g_iMaxPriority < iStart;
+ g_fCanRestorePriority = setpriority(PRIO_PROCESS, 0, iStart) == 0;
+
+ /*
+ * Check if we temporarily lower the thread priority.
+ * Again, we assume we're not at the extreme end of the priority scale.
+ */
+ iStart = getpriority(PRIO_PROCESS, 0);
+ i = iStart;
+ while (i++ < 19)
+ if (setpriority(PRIO_PROCESS, 0, i))
+ break;
+ g_iMinPriority = getpriority(PRIO_PROCESS, 0);
+ if ( setpriority(PRIO_PROCESS, 0, iStart)
+ || getpriority(PRIO_PROCESS, 0) != iStart)
+ g_fCanRestorePriority = false;
+ if (g_iMinPriority == g_iMaxPriority)
+ g_fCanRestorePriority = g_fCanRaisePriority = false;
+
+ /*
+ * Check what happens to child threads when the parent lowers the
+ * priority when it's being created.
+ */
+ iStart = getpriority(PRIO_PROCESS, 0);
+ g_fScrewedUpMaxPriorityLimitInheritance = true;
+ if ( g_fCanRestorePriority
+ && !setpriority(PRIO_PROCESS, 0, g_iMinPriority)
+ && iStart != g_iMinPriority)
+ {
+ if (rtSchedRunThread(rtSchedNativeSubProberThread, (void *)(intptr_t)iStart, false /*fUsePriorityProxy*/) == 0)
+ g_fScrewedUpMaxPriorityLimitInheritance = false;
+ }
+
+ /* done */
+ rtSchedNativeRestore(&SavedPriority);
+ return (void *)VINF_SUCCESS;
+}
+
+
+/**
+ * Calculate the scheduling properties for all the threads in the default
+ * process priority, assuming the current thread have the type enmType.
+ *
+ * @returns iprt status code.
+ * @param enmType The thread type to be assumed for the current thread.
+ */
+DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+
+ /*
+ * First figure out what's we're allowed to do in this process.
+ */
+ if (!g_fInitialized)
+ {
+ int iPriority = getpriority(PRIO_PROCESS, 0);
+#ifdef RLIMIT_RTPRIO
+ /** @todo */
+#endif
+ int rc = rtSchedRunThread(rtSchedNativeProberThread, NULL, false /*fUsePriorityProxy*/);
+ if (RT_FAILURE(rc))
+ return rc;
+ Assert(getpriority(PRIO_PROCESS, 0) == iPriority); NOREF(iPriority);
+ g_fInitialized = true;
+ }
+
+ /*
+ * Select the right priority type table and update the default
+ * process priority structure.
+ */
+ if (g_fCanRaisePriority && g_fCanRestorePriority && !g_fScrewedUpMaxPriorityLimitInheritance)
+ g_aDefaultPriority.paTypes = &g_aTypesLinuxFree[0];
+ else if (!g_fCanRaisePriority && g_fCanRestorePriority && !g_fScrewedUpMaxPriorityLimitInheritance)
+ g_aDefaultPriority.paTypes = &g_aTypesLinuxRestricted[0];
+ else
+ g_aDefaultPriority.paTypes = &g_aTypesLinuxFlat[0];
+ Assert(enmType == g_aDefaultPriority.paTypes[enmType].enmType);
+
+ int iPriority = getpriority(PRIO_PROCESS, 0 /* current process */);
+ g_aDefaultPriority.iNice = iPriority - g_aDefaultPriority.paTypes[enmType].iPriority;
+ g_aDefaultPriority.iDelta = g_aDefaultPriority.iNice;
+
+ rtSchedDumpPriority();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * The process priority validator thread.
+ * (We don't want to mess with the priority of the calling thread.)
+ */
+static void *rtSchedNativeValidatorThread(void *pvUser)
+{
+ PVALIDATORPRIORITYPAIR pPrioPair = (PVALIDATORPRIORITYPAIR)pvUser;
+ SAVEDPRIORITY SavedPriority;
+ rtSchedNativeSave(&SavedPriority);
+
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Set the priority to the current value for specified thread type, but
+ * only if we have any threads of this type (caller checked - INT_MAX).
+ */
+ if (pPrioPair->iCurrent != INT_MAX)
+ if (setpriority(PRIO_PROCESS, 0, pPrioPair->iCurrent))
+ rc = RTErrConvertFromErrno(errno);
+
+ /*
+ * Try set the new priority.
+ */
+ if (RT_SUCCESS(rc) && setpriority(PRIO_PROCESS, 0, pPrioPair->iNew))
+ rc = RTErrConvertFromErrno(errno);
+
+ /* done */
+ rtSchedNativeRestore(&SavedPriority);
+ return (void *)(intptr_t)rc;
+}
+
+
+/**
+ * Validates the ability to apply suggested priority scheme.
+ *
+ * The function checks that we're able to apply all the thread types in the
+ * suggested priority scheme.
+ *
+ * @returns iprt status code.
+ * @param pCfg The priority scheme to validate.
+ * @param fHavePriorityProxy Set if we've got a priority proxy thread,
+ * otherwise clear.
+ */
+static int rtSchedNativeCheckThreadTypes(const PROCPRIORITY *pCfg, bool fHavePriorityProxy)
+{
+ int i = RTTHREADTYPE_END;
+ while (--i > RTTHREADTYPE_INVALID)
+ {
+ VALIDATORPRIORITYPAIR PrioPair;
+ PrioPair.iCurrent = g_pProcessPriority->paTypes[i].iPriority + g_pProcessPriority->iDelta;
+ PrioPair.iNew = pCfg->paTypes[i].iPriority + pCfg->iDelta;
+ if (g_acRTThreadTypeStats[i] == 0)
+ PrioPair.iCurrent = INT_MAX;
+
+#ifdef RT_STRICT
+ int const iPriority = getpriority(PRIO_PROCESS, 0);
+#endif
+ int rc = rtSchedRunThread(rtSchedNativeValidatorThread, &PrioPair, fHavePriorityProxy /*fUsePriorityProxy*/);
+ Assert(getpriority(PRIO_PROCESS, 0) == iPriority);
+
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority)
+{
+ Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST);
+
+#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+ /*
+ * Make sure the proxy creation thread is started so we don't 'lose' our
+ * initial priority if it's lowered.
+ */
+ bool const fHavePriorityProxy = rtThreadPosixPriorityProxyStart();
+#else
+ bool const fHavePriorityProxy = false;
+#endif
+
+ int rc;
+ if (enmPriority == RTPROCPRIORITY_DEFAULT)
+ {
+ /*
+ * If we've lowered priority since the process started, it may be impossible
+ * to raise it again for existing thread (new threads will work fine).
+ */
+ rc = rtSchedNativeCheckThreadTypes(&g_aDefaultPriority, fHavePriorityProxy);
+ if (RT_SUCCESS(rc))
+ g_pProcessPriority = &g_aDefaultPriority;
+ }
+ else
+ {
+ /*
+ * Find a configuration which matches and can be applied.
+ */
+ rc = VERR_NOT_FOUND;
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aUnixConfigs); i++)
+ if (g_aUnixConfigs[i].enmPriority == enmPriority)
+ {
+ int rc2 = rtSchedNativeCheckThreadTypes(&g_aUnixConfigs[i], fHavePriorityProxy);
+ if (RT_SUCCESS(rc2))
+ {
+ g_pProcessPriority = &g_aUnixConfigs[i];
+ rc = VINF_SUCCESS;
+ break;
+ }
+ if (rc == VERR_NOT_FOUND || rc == VERR_ACCESS_DENIED)
+ rc = rc2;
+ }
+ }
+
+#ifdef THREAD_LOGGING
+ LogFlow(("rtProcNativeSetPriority: returns %Rrc enmPriority=%d\n", rc, enmPriority));
+ rtSchedDumpPriority();
+#endif
+ return rc;
+}
+
+
+/**
+ * Called on the priority proxy thread if it's running, otherwise
+ * rtThreadNativeSetPriority calls it directly.
+ */
+static DECLCALLBACK(int) rtThreadLinuxSetPriorityCallback(PRTTHREADINT pThread, int iPriority)
+{
+ if (!setpriority(PRIO_PROCESS, pThread->tid, iPriority))
+ {
+ AssertMsg(iPriority == getpriority(PRIO_PROCESS, pThread->tid),
+ ("iPriority=%d getpriority()=%d\n", iPriority, getpriority(PRIO_PROCESS, pThread->tid)));
+#ifdef THREAD_LOGGING
+ Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPriority=%d pid=%d tid=%d\n",
+ pThread->Core.Key, enmType, iPriority, getpid(), pThread->tid));
+#endif
+ return VINF_SUCCESS;
+ }
+ AssertMsgFailed(("setpriority(,, %d) -> errno=%d rc=%Rrc\n", iPriority, errno, RTErrConvertFromErrno(errno)));
+ return VINF_SUCCESS; //non-fatal for now.
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ /* sanity */
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ Assert(enmType == g_pProcessPriority->paTypes[enmType].enmType);
+
+ /*
+ * The thread ID is zero for alien threads, so skip these or we'd risk
+ * modifying our own priority.
+ */
+ if (!pThread->tid)
+ return VINF_SUCCESS;
+
+ /*
+ * Calculate the thread priority and apply it, preferrably via the priority proxy thread.
+ */
+ int const iPriority = g_pProcessPriority->paTypes[enmType].iPriority + g_pProcessPriority->iDelta;
+#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+ if (rtThreadPosixPriorityProxyStart())
+ return rtThreadPosixPriorityProxyCall(pThread, (PFNRT)rtThreadLinuxSetPriorityCallback, 2, pThread, iPriority);
+#endif
+ return rtThreadLinuxSetPriorityCallback(pThread, iPriority);
+}
+
diff --git a/src/VBox/Runtime/r3/linux/semevent-linux.cpp b/src/VBox/Runtime/r3/linux/semevent-linux.cpp
new file mode 100644
index 00000000..c3a973c1
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/semevent-linux.cpp
@@ -0,0 +1,607 @@
+/* $Id: semevent-linux.cpp $ */
+/** @file
+ * IPRT - Event Semaphore, Linux (2.6.0 and later).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#include <features.h>
+#if __GLIBC_PREREQ(2,6) && !defined(IPRT_WITH_FUTEX_BASED_SEMS)
+
+/*
+ * glibc 2.6 fixed a serious bug in the mutex implementation. We wrote this
+ * linux specific event semaphores code in order to work around the bug. We
+ * will fall back on the pthread-based implementation if glibc is known to
+ * contain the bug fix.
+ *
+ * The external reference to epoll_pwait is a hack which prevents that we link
+ * against glibc < 2.6.
+ */
+# include "../posix/semevent-posix.cpp"
+__asm__ (".global epoll_pwait");
+
+#else /* glibc < 2.6 */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+#include "internal/mem.h"
+#include "internal/strict.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/syscall.h>
+
+#include "semwait-linux.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Linux (single wakup) event semaphore.
+ */
+struct RTSEMEVENTINTERNAL
+{
+ /** Magic value. */
+ intptr_t volatile iMagic;
+ /** The futex state variable.
+ * 0 means not signalled.
+ 1 means signalled. */
+ uint32_t volatile fSignalled;
+ /** The number of waiting threads */
+ int32_t volatile cWaiters;
+#ifdef RTSEMEVENT_STRICT
+ /** Signallers. */
+ RTLOCKVALRECSHRD Signallers;
+ /** Indicates that lock validation should be performed. */
+ bool volatile fEverHadSignallers;
+#endif
+ /** The creation flags. */
+ uint32_t fFlags;
+};
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Whether we can use FUTEX_WAIT_BITSET. */
+static int volatile g_fCanUseWaitBitSet = -1;
+
+
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+
+ /*
+ * Make sure we know whether FUTEX_WAIT_BITSET works.
+ */
+ rtSemLinuxCheckForFutexWaitBitSet(&g_fCanUseWaitBitSet);
+#if defined(DEBUG_bird) && !defined(IN_GUEST)
+ Assert(g_fCanUseWaitBitSet == true);
+#endif
+
+ /*
+ * Allocate semaphore handle.
+ */
+ struct RTSEMEVENTINTERNAL *pThis;
+ if (!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ pThis = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTINTERNAL));
+ else
+ pThis = (struct RTSEMEVENTINTERNAL *)rtMemBaseAlloc(sizeof(struct RTSEMEVENTINTERNAL));
+ if (pThis)
+ {
+ pThis->iMagic = RTSEMEVENT_MAGIC;
+ pThis->cWaiters = 0;
+ pThis->fSignalled = 0;
+ pThis->fFlags = fFlags;
+#ifdef RTSEMEVENT_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemEventAnon = 0;
+ RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
+ "RTSemEvent-%u", ASMAtomicIncU32(&s_iSemEventAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
+ pszNameFmt, va);
+ va_end(va);
+ }
+ pThis->fEverHadSignallers = false;
+#else
+ RT_NOREF(hClass, pszNameFmt);
+#endif
+
+ *phEventSem = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the semaphore and wake up anyone waiting on it.
+ */
+ ASMAtomicXchgSize(&pThis->iMagic, RTSEMEVENT_MAGIC | UINT32_C(0x80000000));
+ if (ASMAtomicXchgS32(&pThis->cWaiters, INT32_MIN / 2) > 0)
+ {
+ sys_futex(&pThis->fSignalled, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
+ usleep(1000);
+ }
+
+ /*
+ * Free the semaphore memory and be gone.
+ */
+#ifdef RTSEMEVENT_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->Signallers);
+#endif
+ if (!(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ RTMemFree(pThis);
+ else
+ rtMemBaseFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMEVENT_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ ASMAtomicWriteU32(&pThis->fSignalled, 1);
+ if (ASMAtomicReadS32(&pThis->cWaiters) < 1)
+ return VINF_SUCCESS;
+
+ /* somebody is waiting, try wake up one of them. */
+ long cWoken = sys_futex(&pThis->fSignalled, FUTEX_WAKE, 1, NULL, NULL, 0);
+ if (RT_LIKELY(cWoken >= 0))
+ return VINF_SUCCESS;
+
+ if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
+ return VERR_SEM_DESTROYED;
+
+ return VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * Performs an indefinite wait on the event.
+ */
+static int rtSemEventLinuxWaitIndefinite(struct RTSEMEVENTINTERNAL *pThis, uint32_t fFlags, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ RT_NOREF_PV(pSrcPos);
+
+ /*
+ * Quickly check whether it's signaled and there are no other waiters.
+ */
+ uint32_t cWaiters = ASMAtomicIncS32(&pThis->cWaiters);
+ if ( cWaiters == 1
+ && ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
+ {
+ ASMAtomicDecS32(&pThis->cWaiters);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * The wait loop.
+ */
+#ifdef RTSEMEVENT_STRICT
+ RTTHREAD hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)
+ ? RTThreadSelfAutoAdopt()
+ : RTThreadSelf();
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ int rc = VINF_SUCCESS;
+ for (;;)
+ {
+#ifdef RTSEMEVENT_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
+ RT_INDEFINITE_WAIT, RTTHREADSTATE_EVENT, true);
+ if (RT_FAILURE(rc))
+ break;
+ }
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
+ long lrc = sys_futex(&pThis->fSignalled, FUTEX_WAIT, 0, NULL /*pTimeout*/, NULL, 0);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
+ if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
+ {
+ rc = VERR_SEM_DESTROYED;
+ break;
+ }
+
+ if (RT_LIKELY(lrc == 0 || lrc == -EWOULDBLOCK))
+ {
+ /* successful wakeup or fSignalled > 0 in the meantime */
+ if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
+ break;
+ }
+ else if (lrc == -ETIMEDOUT)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ else if (lrc == -EINTR)
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_NORESUME)
+ {
+ rc = VERR_INTERRUPTED;
+ break;
+ }
+ }
+ else
+ {
+ /* this shouldn't happen! */
+ AssertMsgFailed(("rc=%ld errno=%d\n", lrc, errno));
+ rc = RTErrConvertFromErrno(lrc);
+ break;
+ }
+ }
+
+ ASMAtomicDecS32(&pThis->cWaiters);
+ return rc;
+}
+
+
+/**
+ * Handle polling (timeout already expired at the time of the call).
+ *
+ * @returns VINF_SUCCESS, VERR_TIMEOUT, VERR_SEM_DESTROYED.
+ * @param pThis The semaphore.
+ */
+static int rtSemEventLinuxWaitPoll(struct RTSEMEVENTINTERNAL *pThis)
+{
+ /*
+ * What we do here is isn't quite fair to anyone else waiting on it, however
+ * it might not be as bad as all that for callers making repeated poll calls
+ * because they cannot block, as that would be a virtual wait but without the
+ * chance of a permanept queue position. So, I hope we can live with this.
+ */
+ if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
+ return VINF_SUCCESS;
+ return VERR_TIMEOUT;
+}
+
+
+/**
+ * Performs an timed wait on the event.
+ */
+static int rtSemEventLinuxWaitTimed(struct RTSEMEVENTINTERNAL *pThis, uint32_t fFlags,
+ uint64_t uTimeout, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ RT_NOREF_PV(pSrcPos);
+
+ /*
+ * Convert the timeout value.
+ */
+ struct timespec TsTimeout;
+ int iWaitOp;
+ uint32_t uWaitVal3;
+ uint64_t nsAbsTimeout = uTimeout; /* (older gcc maybe used uninitialized) */
+ uTimeout = rtSemLinuxCalcDeadline(fFlags, uTimeout, g_fCanUseWaitBitSet, &TsTimeout, &iWaitOp, &uWaitVal3, &nsAbsTimeout);
+ if (uTimeout == 0)
+ return rtSemEventLinuxWaitPoll(pThis);
+ if (uTimeout == UINT64_MAX)
+ return rtSemEventLinuxWaitIndefinite(pThis, fFlags, pSrcPos);
+
+ /*
+ * Quickly check whether it's signaled and there are no other waiters.
+ */
+ uint32_t cWaiters = ASMAtomicIncS32(&pThis->cWaiters);
+ if ( cWaiters == 1
+ && ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
+ {
+ ASMAtomicDecS32(&pThis->cWaiters);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * The wait loop.
+ */
+#ifdef RTSEMEVENT_STRICT
+ RTTHREAD hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)
+ ? RTThreadSelfAutoAdopt()
+ : RTThreadSelf();
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ int rc = VINF_SUCCESS;
+ for (;;)
+ {
+#ifdef RTSEMEVENT_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
+ iWaitOp == FUTEX_WAIT ? uTimeout / RT_NS_1MS : RT_MS_1HOUR /*whatever*/,
+ RTTHREADSTATE_EVENT, true);
+ if (RT_FAILURE(rc))
+ break;
+ }
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
+ long lrc = sys_futex(&pThis->fSignalled, iWaitOp, 0, &TsTimeout, NULL, uWaitVal3);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
+ if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
+ {
+ rc = VERR_SEM_DESTROYED;
+ break;
+ }
+
+ if (RT_LIKELY(lrc == 0 || lrc == -EWOULDBLOCK))
+ {
+ /* successful wakeup or fSignalled > 0 in the meantime */
+ if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
+ break;
+ }
+ else if (lrc == -ETIMEDOUT)
+ {
+#ifdef RT_STRICT
+ uint64_t const uNow = RTTimeNanoTS();
+ AssertMsg(uNow >= nsAbsTimeout || nsAbsTimeout - uNow < RT_NS_1MS,
+ ("%#RX64 - %#RX64 => %#RX64 (%RI64)\n", nsAbsTimeout, uNow, nsAbsTimeout - uNow, nsAbsTimeout - uNow));
+#endif
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ else if (lrc == -EINTR)
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_NORESUME)
+ {
+ rc = VERR_INTERRUPTED;
+ break;
+ }
+ }
+ else
+ {
+ /* this shouldn't happen! */
+ AssertMsgFailed(("rc=%ld errno=%d\n", lrc, errno));
+ rc = RTErrConvertFromErrno(lrc);
+ break;
+ }
+
+ /* adjust the relative timeout */
+ if (iWaitOp == FUTEX_WAIT)
+ {
+ int64_t i64Diff = nsAbsTimeout - RTTimeSystemNanoTS();
+ if (i64Diff < 1000)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ TsTimeout.tv_sec = (uint64_t)i64Diff / RT_NS_1SEC;
+ TsTimeout.tv_nsec = (uint64_t)i64Diff % RT_NS_1SEC;
+ }
+ }
+
+ ASMAtomicDecS32(&pThis->cWaiters);
+ return rc;
+}
+
+
+/**
+ * Internal wait worker function.
+ */
+DECLINLINE(int) rtSemEventLinuxWait(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->iMagic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+#ifdef RT_STRICT
+ uint32_t const fSignalled = pThis->fSignalled;
+ Assert(fSignalled == false || fSignalled == true);
+#endif
+
+ /*
+ * Timed or indefinite wait?
+ */
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ return rtSemEventLinuxWaitIndefinite(pThis, fFlags, pSrcPos);
+ return rtSemEventLinuxWaitTimed(hEventSem, fFlags, uTimeout, pSrcPos);
+}
+
+
+RTDECL(int) RTSemEventWait(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
+{
+ int rc;
+#ifndef RTSEMEVENT_STRICT
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = rtSemEventLinuxWait(hEventSem, RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0, NULL);
+ else
+ rc = rtSemEventLinuxWait(hEventSem, RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = rtSemEventLinuxWait(hEventSem, RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0, &SrcPos);
+ else
+ rc = rtSemEventLinuxWait(hEventSem, RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies, &SrcPos);
+#endif
+ Assert(rc != VERR_INTERRUPTED);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
+{
+ int rc;
+#ifndef RTSEMEVENT_STRICT
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = rtSemEventLinuxWait(hEventSem, RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0, NULL);
+ else
+ rc = rtSemEventLinuxWait(hEventSem, RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = rtSemEventLinuxWait(hEventSem, RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0, &SrcPos);
+ else
+ rc = rtSemEventLinuxWait(hEventSem, RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies, &SrcPos);
+#endif
+ Assert(rc != VERR_INTERRUPTED);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtSemEventLinuxWait(hEventSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemEventLinuxWait(hEventSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemEventLinuxWait(hEventSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+RTDECL(uint32_t) RTSemEventGetResolution(void)
+{
+ /** @todo we have 1ns parameter resolution, but need to verify that this is what
+ * the kernel actually will use when setting the timer. Most likely
+ * it's rounded a little, but hopefully not to a multiple of HZ. */
+ return 1;
+}
+
+
+RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF(hEventSem, hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF(hEventSem, hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->iMagic == RTSEMEVENT_MAGIC);
+
+ RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
+#else
+ RT_NOREF(hEventSem, hThread);
+#endif
+}
+
+#endif /* glibc < 2.6 || IPRT_WITH_FUTEX_BASED_SEMS */
+
diff --git a/src/VBox/Runtime/r3/linux/semeventmulti-linux.cpp b/src/VBox/Runtime/r3/linux/semeventmulti-linux.cpp
new file mode 100644
index 00000000..87554838
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/semeventmulti-linux.cpp
@@ -0,0 +1,600 @@
+/* $Id: semeventmulti-linux.cpp $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphore, Linux (2.6.x+).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+#include <features.h>
+#if __GLIBC_PREREQ(2,6) && !defined(IPRT_WITH_FUTEX_BASED_SEMS)
+
+/*
+ * glibc 2.6 fixed a serious bug in the mutex implementation. We wrote this
+ * linux specific event semaphores code in order to work around the bug. As it
+ * turns out, this code seems to have an unresolved issue (@bugref{2599}), so we'll
+ * fall back on the pthread based implementation if glibc is known to contain
+ * the bug fix.
+ *
+ * The external reference to epoll_pwait is a hack which prevents that we link
+ * against glibc < 2.6.
+ */
+#include "../posix/semeventmulti-posix.cpp"
+__asm__ (".global epoll_pwait");
+
+#else /* glibc < 2.6 */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/syscall.h>
+
+#include "semwait-linux.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Linux multiple wakup event semaphore.
+ */
+struct RTSEMEVENTMULTIINTERNAL
+{
+ /** Magic value. */
+ uint32_t volatile u32Magic;
+ /** The futex state variable, see RTSEMEVENTMULTI_LNX_XXX. */
+ uint32_t volatile uState;
+#ifdef RT_STRICT
+ /** Increased on every signalling call. */
+ uint32_t volatile uSignalSerialNo;
+#endif
+#ifdef RTSEMEVENTMULTI_STRICT
+ /** Signallers. */
+ RTLOCKVALRECSHRD Signallers;
+ /** Indicates that lock validation should be performed. */
+ bool volatile fEverHadSignallers;
+#endif
+};
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @name RTSEMEVENTMULTI_LNX_XXX - state
+ * @{ */
+#define RTSEMEVENTMULTI_LNX_NOT_SIGNALED UINT32_C(0x00000000)
+#define RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS UINT32_C(0x00000001)
+#define RTSEMEVENTMULTI_LNX_SIGNALED UINT32_C(0x00000003)
+/** @} */
+
+#define ASSERT_VALID_STATE(a_uState) \
+ AssertMsg( (a_uState) == RTSEMEVENTMULTI_LNX_NOT_SIGNALED \
+ || (a_uState) == RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS \
+ || (a_uState) == RTSEMEVENTMULTI_LNX_SIGNALED, \
+ (#a_uState "=%s\n", a_uState))
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Whether we can use FUTEX_WAIT_BITSET. */
+static int volatile g_fCanUseWaitBitSet = -1;
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Make sure we know whether FUTEX_WAIT_BITSET works.
+ */
+ rtSemLinuxCheckForFutexWaitBitSet(&g_fCanUseWaitBitSet);
+#if defined(DEBUG_bird) && !defined(IN_GUEST)
+ Assert(g_fCanUseWaitBitSet == true);
+#endif
+
+ /*
+ * Allocate semaphore handle.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
+ pThis->uState = RTSEMEVENTMULTI_LNX_NOT_SIGNALED;
+#ifdef RT_STRICT
+ pThis->uSignalSerialNo = 0;
+#endif
+#ifdef RTSEMEVENTMULTI_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemEventMultiAnon = 0;
+ RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
+ "RTSemEventMulti-%u", ASMAtomicIncU32(&s_iSemEventMultiAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
+ pszNameFmt, va);
+ va_end(va);
+ }
+ pThis->fEverHadSignallers = false;
+#else
+ RT_NOREF(hClass, pszNameFmt);
+#endif
+
+ *phEventMultiSem = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the semaphore and wake up anyone waiting on it.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMEVENTMULTI_MAGIC + 1);
+ if (ASMAtomicXchgU32(&pThis->uState, RTSEMEVENTMULTI_LNX_SIGNALED) == RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS)
+ {
+ sys_futex(&pThis->uState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
+ usleep(1000);
+ }
+
+ /*
+ * Free the semaphore memory and be gone.
+ */
+#ifdef RTSEMEVENTMULTI_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->Signallers);
+#endif
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMEVENTMULTI_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Signal it.
+ */
+#ifdef RT_STRICT
+ ASMAtomicIncU32(&pThis->uSignalSerialNo);
+#endif
+ uint32_t uOld = ASMAtomicXchgU32(&pThis->uState, RTSEMEVENTMULTI_LNX_SIGNALED);
+ if (uOld == RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS)
+ {
+ /* wake up sleeping threads. */
+ long cWoken = sys_futex(&pThis->uState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
+ AssertMsg(cWoken >= 0, ("%ld\n", cWoken)); NOREF(cWoken);
+ }
+ ASSERT_VALID_STATE(uOld);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+#ifdef RT_STRICT
+ uint32_t const uState = pThis->uState;
+ ASSERT_VALID_STATE(uState);
+#endif
+
+ /*
+ * Reset it.
+ */
+ ASMAtomicCmpXchgU32(&pThis->uState, RTSEMEVENTMULTI_LNX_NOT_SIGNALED, RTSEMEVENTMULTI_LNX_SIGNALED);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Performs an indefinite wait on the event.
+ */
+static int rtSemEventMultiLinuxWaitIndefinite(struct RTSEMEVENTMULTIINTERNAL *pThis, uint32_t fFlags, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ RT_NOREF(pSrcPos);
+
+ /*
+ * Quickly check whether it's signaled.
+ */
+ uint32_t uState = ASMAtomicUoReadU32(&pThis->uState);
+ if (uState == RTSEMEVENTMULTI_LNX_SIGNALED)
+ return VINF_SUCCESS;
+ ASSERT_VALID_STATE(uState);
+
+ /*
+ * The wait loop.
+ */
+#ifdef RTSEMEVENTMULTI_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ for (unsigned i = 0;; i++)
+ {
+ /*
+ * Start waiting. We only account for there being or having been
+ * threads waiting on the semaphore to keep things simple.
+ */
+ uState = ASMAtomicUoReadU32(&pThis->uState);
+ if ( uState == RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS
+ || ( uState == RTSEMEVENTMULTI_LNX_NOT_SIGNALED
+ && ASMAtomicCmpXchgU32(&pThis->uState, RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS,
+ RTSEMEVENTMULTI_LNX_NOT_SIGNALED)))
+ {
+#ifdef RTSEMEVENTMULTI_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
+ RT_INDEFINITE_WAIT, RTTHREADSTATE_EVENT_MULTI, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+#ifdef RT_STRICT
+ uint32_t const uPrevSignalSerialNo = ASMAtomicReadU32(&pThis->uSignalSerialNo);
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true);
+ long rc = sys_futex(&pThis->uState, FUTEX_WAIT, 1, NULL /*pTimeout*/, NULL, 0);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI);
+
+ /* Check that the structure is still alive before continuing. */
+ if (RT_LIKELY(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC))
+ { /*likely*/ }
+ else
+ return VERR_SEM_DESTROYED;
+
+ /*
+ * Return if success.
+ */
+ if (rc == 0)
+ {
+ Assert(uPrevSignalSerialNo != ASMAtomicReadU32(&pThis->uSignalSerialNo));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Act on the wakup code.
+ */
+ if (rc == -EWOULDBLOCK)
+ /* retry, the value changed. */;
+ else if (rc == -EINTR)
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_NORESUME)
+ return VERR_INTERRUPTED;
+ }
+ else
+ {
+ /* this shouldn't happen! */
+ AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
+ return RTErrConvertFromErrno(rc);
+ }
+ }
+ else if (uState == RTSEMEVENTMULTI_LNX_SIGNALED)
+ return VINF_SUCCESS;
+ else
+ ASSERT_VALID_STATE(uState);
+ }
+}
+
+
+/**
+ * Handle polling (timeout already expired at the time of the call).
+ *
+ * @returns VINF_SUCCESS, VERR_TIMEOUT, VERR_SEM_DESTROYED.
+ * @param pThis The semaphore.
+ */
+static int rtSemEventMultiLinuxWaitPoll(struct RTSEMEVENTMULTIINTERNAL *pThis)
+{
+ uint32_t uState = ASMAtomicUoReadU32(&pThis->uState);
+ if (uState == RTSEMEVENTMULTI_LNX_SIGNALED)
+ return VINF_SUCCESS;
+ return VERR_TIMEOUT;
+}
+
+
+/**
+ * Performs an indefinite wait on the event.
+ */
+static int rtSemEventMultiLinuxWaitTimed(struct RTSEMEVENTMULTIINTERNAL *pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ RT_NOREF(pSrcPos);
+
+ /*
+ * Quickly check whether it's signaled.
+ */
+ uint32_t uState = ASMAtomicUoReadU32(&pThis->uState);
+ if (uState == RTSEMEVENTMULTI_LNX_SIGNALED)
+ return VINF_SUCCESS;
+ ASSERT_VALID_STATE(uState);
+
+ /*
+ * Convert the timeout value.
+ */
+ struct timespec TsTimeout;
+ int iWaitOp;
+ uint32_t uWaitVal3;
+ uint64_t nsAbsTimeout = uTimeout; /* (older gcc maybe used uninitialized) */
+ uTimeout = rtSemLinuxCalcDeadline(fFlags, uTimeout, g_fCanUseWaitBitSet, &TsTimeout, &iWaitOp, &uWaitVal3, &nsAbsTimeout);
+ if (uTimeout == 0)
+ return rtSemEventMultiLinuxWaitPoll(pThis);
+ if (uTimeout == UINT64_MAX)
+ return rtSemEventMultiLinuxWaitIndefinite(pThis, fFlags, pSrcPos);
+
+ /*
+ * The wait loop.
+ */
+#ifdef RTSEMEVENTMULTI_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ for (unsigned i = 0;; i++)
+ {
+ /*
+ * Start waiting. We only account for there being or having been
+ * threads waiting on the semaphore to keep things simple.
+ */
+ uState = ASMAtomicUoReadU32(&pThis->uState);
+ if ( uState == RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS
+ || ( uState == RTSEMEVENTMULTI_LNX_NOT_SIGNALED
+ && ASMAtomicCmpXchgU32(&pThis->uState, RTSEMEVENTMULTI_LNX_NOT_SIGNALED_WAITERS,
+ RTSEMEVENTMULTI_LNX_NOT_SIGNALED)))
+ {
+#ifdef RTSEMEVENTMULTI_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
+ uTimeout / UINT32_C(1000000), RTTHREADSTATE_EVENT_MULTI, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+#ifdef RT_STRICT
+ uint32_t const uPrevSignalSerialNo = ASMAtomicReadU32(&pThis->uSignalSerialNo);
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true);
+ long rc = sys_futex(&pThis->uState, iWaitOp, 1, &TsTimeout, NULL, uWaitVal3);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI);
+
+ /* Check that the structure is still alive before continuing. */
+ if (RT_LIKELY(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC))
+ { /*likely*/ }
+ else
+ return VERR_SEM_DESTROYED;
+
+ /*
+ * Return if success.
+ */
+ if (rc == 0)
+ {
+ Assert(uPrevSignalSerialNo != ASMAtomicReadU32(&pThis->uSignalSerialNo));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Act on the wakup code.
+ */
+ if (rc == -ETIMEDOUT)
+ {
+ /** @todo something is broken here. shows up every now and again in the ata
+ * code. Should try to run the timeout against RTTimeMilliTS to
+ * check that it's doing the right thing... */
+#ifdef RT_STRICT
+ uint64_t const uNow = RTTimeNanoTS();
+ AssertMsg(uNow >= nsAbsTimeout || nsAbsTimeout - uNow < RT_NS_1MS,
+ ("%#RX64 - %#RX64 => %#RX64 (%RI64)\n", nsAbsTimeout, uNow, nsAbsTimeout - uNow, nsAbsTimeout - uNow));
+#endif
+ return VERR_TIMEOUT;
+ }
+ if (rc == -EWOULDBLOCK)
+ {
+ /* retry, the value changed. */;
+ }
+ else if (rc == -EINTR)
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_NORESUME)
+ return VERR_INTERRUPTED;
+ }
+ else
+ {
+ /* this shouldn't happen! */
+ AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
+ return RTErrConvertFromErrno(rc);
+ }
+ }
+ else if (uState == RTSEMEVENTMULTI_LNX_SIGNALED)
+ return VINF_SUCCESS;
+ else
+ ASSERT_VALID_STATE(uState);
+
+ /* adjust the relative timeout if relative */
+ if (iWaitOp == FUTEX_WAIT)
+ {
+ int64_t i64Diff = nsAbsTimeout - RTTimeSystemNanoTS();
+ if (i64Diff < 1000)
+ return VERR_TIMEOUT;
+ TsTimeout.tv_sec = (uint64_t)i64Diff / RT_NS_1SEC;
+ TsTimeout.tv_nsec = (uint64_t)i64Diff % RT_NS_1SEC;
+ }
+ }
+}
+
+/**
+ * Internal wait worker function.
+ */
+DECLINLINE(int) rtSemEventLnxMultiWait(RTSEMEVENTMULTI hEventSem, uint32_t fFlags, uint64_t uTimeout, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Timed or indefinite wait?
+ */
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ return rtSemEventMultiLinuxWaitIndefinite(pThis, fFlags, pSrcPos);
+ return rtSemEventMultiLinuxWaitTimed(hEventSem, fFlags, uTimeout, pSrcPos);
+}
+
+
+#undef RTSemEventMultiWaitEx
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtSemEventLnxMultiWait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemEventLnxMultiWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemEventLnxMultiWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENTMULTI_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF(hEventMultiSem, hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENTMULTI_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF(hEventMultiSem, hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENTMULTI_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
+#else
+ RT_NOREF(hEventMultiSem, hThread);
+#endif
+}
+
+#endif /* glibc < 2.6 || IPRT_WITH_FUTEX_BASED_SEMS */
+
diff --git a/src/VBox/Runtime/r3/linux/semmutex-linux.cpp b/src/VBox/Runtime/r3/linux/semmutex-linux.cpp
new file mode 100644
index 00000000..09cd866f
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/semmutex-linux.cpp
@@ -0,0 +1,475 @@
+/* $Id: semmutex-linux.cpp $ */
+/** @file
+ * IPRT - Mutex Semaphore, Linux (2.6.x+).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/syscall.h>
+#if 0 /* With 2.6.17 futex.h has become C++ unfriendly. */
+# include <linux/futex.h>
+#else
+# define FUTEX_WAIT 0
+# define FUTEX_WAKE 1
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Linux internal representation of a Mutex semaphore.
+ */
+struct RTSEMMUTEXINTERNAL
+{
+ /** The futex state variable.
+ * 0 means unlocked.
+ * 1 means locked, no waiters.
+ * 2 means locked, one or more waiters.
+ */
+ int32_t volatile iState;
+ /** Nesting count. */
+ uint32_t volatile cNestings;
+ /** The owner of the mutex. */
+ pthread_t volatile Owner;
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t volatile u32Magic;
+#ifdef RTSEMMUTEX_STRICT
+ /** Lock validator record associated with this mutex. */
+ RTLOCKVALRECEXCL ValidatorRec;
+#endif
+};
+
+
+
+/**
+ * Wrapper for the futex syscall.
+ */
+static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
+{
+ errno = 0;
+ long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
+ if (rc < 0)
+ {
+ Assert(rc == -1);
+ rc = -errno;
+ }
+ return rc;
+}
+
+
+#undef RTSemMutexCreate
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+}
+
+
+RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate semaphore handle.
+ */
+ struct RTSEMMUTEXINTERNAL *pThis = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+ pThis->iState = 0;
+ pThis->Owner = (pthread_t)~0;
+ pThis->cNestings = 0;
+#ifdef RTSEMMUTEX_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iMutexAnon = 0;
+ RTLockValidatorRecExclInit(&pThis->ValidatorRec, hClass, uSubClass, pThis,
+ !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL),
+ "RTSemMutex-%u", ASMAtomicIncU32(&s_iMutexAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecExclInitV(&pThis->ValidatorRec, hClass, uSubClass, pThis,
+ !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL), pszNameFmt, va);
+ va_end(va);
+ }
+#else
+ RT_NOREF(hClass, uSubClass, pszNameFmt);
+#endif
+
+ *phMutexSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate input.
+ */
+ if (hMutexSem == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+ struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC,
+ ("hMutexSem=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the semaphore and wake up anyone waiting on it.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD);
+ if (ASMAtomicXchgS32(&pThis->iState, 0) > 0)
+ {
+ sys_futex(&pThis->iState, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
+ usleep(1000);
+ }
+ pThis->Owner = (pthread_t)~0;
+ pThis->cNestings = 0;
+#ifdef RTSEMMUTEX_STRICT
+ RTLockValidatorRecExclDelete(&pThis->ValidatorRec);
+#endif
+
+ /*
+ * Free the semaphore memory and be gone.
+ */
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTSemMutexSetSubClass(RTSEMMUTEX hMutexSem, uint32_t uSubClass)
+{
+#ifdef RTSEMMUTEX_STRICT
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+
+ return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorRec, uSubClass);
+#else
+ RT_NOREF(hMutexSem, uSubClass);
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+#endif
+}
+
+
+DECL_FORCE_INLINE(int) rtSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, bool fAutoResume, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ RT_NOREF(pSrcPos);
+
+ /*
+ * Validate input.
+ */
+ struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Check if nested request.
+ */
+ pthread_t Self = pthread_self();
+ if ( pThis->Owner == Self
+ && pThis->cNestings > 0)
+ {
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclRecursion(&pThis->ValidatorRec, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ ASMAtomicIncU32(&pThis->cNestings);
+ return VINF_SUCCESS;
+ }
+
+#ifdef RTSEMMUTEX_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
+ if (cMillies)
+ {
+ int rc9 = RTLockValidatorRecExclCheckOrder(&pThis->ValidatorRec, hThreadSelf, pSrcPos, cMillies);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+
+ /*
+ * Convert timeout value.
+ */
+ struct timespec ts;
+ struct timespec *pTimeout = NULL;
+ uint64_t u64End = 0; /* shut up gcc */
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ ts.tv_sec = cMillies / 1000;
+ ts.tv_nsec = (cMillies % 1000) * UINT32_C(1000000);
+ u64End = RTTimeSystemNanoTS() + cMillies * UINT64_C(1000000);
+ pTimeout = &ts;
+ }
+
+ /*
+ * Lock the mutex.
+ * Optimize for the uncontended case (makes 1-2 ns difference).
+ */
+ if (RT_UNLIKELY(!ASMAtomicCmpXchgS32(&pThis->iState, 1, 0)))
+ {
+ for (;;)
+ {
+ int32_t iOld = ASMAtomicXchgS32(&pThis->iState, 2);
+
+ /*
+ * Was the lock released in the meantime? This is unlikely (but possible)
+ */
+ if (RT_UNLIKELY(iOld == 0))
+ break;
+
+ /*
+ * Go to sleep.
+ */
+ if (pTimeout && ( pTimeout->tv_sec || pTimeout->tv_nsec ))
+ {
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclCheckBlocking(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true,
+ cMillies, RTTHREADSTATE_MUTEX, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#else
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_MUTEX, true);
+#endif
+ }
+
+ long rc = sys_futex(&pThis->iState, FUTEX_WAIT, 2, pTimeout, NULL, 0);
+
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX);
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMMUTEX_MAGIC))
+ return VERR_SEM_DESTROYED;
+
+ /*
+ * Act on the wakup code.
+ */
+ if (rc == -ETIMEDOUT)
+ {
+ Assert(pTimeout);
+ return VERR_TIMEOUT;
+ }
+ if (rc == 0)
+ /* we'll leave the loop now unless another thread is faster */;
+ else if (rc == -EWOULDBLOCK)
+ /* retry with new value. */;
+ else if (rc == -EINTR)
+ {
+ if (!fAutoResume)
+ return VERR_INTERRUPTED;
+ }
+ else
+ {
+ /* this shouldn't happen! */
+ AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ /* adjust the relative timeout */
+ if (pTimeout)
+ {
+ int64_t i64Diff = u64End - RTTimeSystemNanoTS();
+ if (i64Diff < 1000)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ ts.tv_sec = (uint64_t)i64Diff / UINT32_C(1000000000);
+ ts.tv_nsec = (uint64_t)i64Diff % UINT32_C(1000000000);
+ }
+ }
+
+ /*
+ * When leaving this loop, iState is set to 2. This means that we gained the
+ * lock and there are _possibly_ some waiters. We don't know exactly as another
+ * thread might entered this loop at nearly the same time. Therefore we will
+ * call futex_wakeup once too often (if _no_ other thread entered this loop).
+ * The key problem is the simple futex_wait test for x != y (iState != 2) in
+ * our case).
+ */
+ }
+
+ /*
+ * Set the owner and nesting.
+ */
+ pThis->Owner = Self;
+ ASMAtomicWriteU32(&pThis->cNestings, 1);
+#ifdef RTSEMMUTEX_STRICT
+ RTLockValidatorRecExclSetOwner(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true);
+#endif
+ return VINF_SUCCESS;
+}
+
+
+#undef RTSemMutexRequest
+RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMMUTEX_STRICT
+ int rc = rtSemMutexRequest(hMutexSem, cMillies, true, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ int rc = rtSemMutexRequest(hMutexSem, cMillies, true, &SrcPos);
+#endif
+ Assert(rc != VERR_INTERRUPTED);
+ return rc;
+}
+
+
+RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ int rc = rtSemMutexRequest(hMutexSem, cMillies, true, &SrcPos);
+ Assert(rc != VERR_INTERRUPTED);
+ return rc;
+}
+
+
+#undef RTSemMutexRequestNoResume
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMMUTEX_STRICT
+ return rtSemMutexRequest(hMutexSem, cMillies, false, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemMutexRequest(hMutexSem, cMillies, false, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemMutexRequest(hMutexSem, cMillies, false, &SrcPos);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorRec, pThis->cNestings == 1);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+
+ /*
+ * Check if nested.
+ */
+ pthread_t Self = pthread_self();
+ if (RT_UNLIKELY( pThis->Owner != Self
+ || pThis->cNestings == 0))
+ {
+ AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNestings=%d\n",
+ pThis, Self, pThis->Owner, pThis->cNestings));
+ return VERR_NOT_OWNER;
+ }
+
+ /*
+ * If nested we'll just pop a nesting.
+ */
+ if (pThis->cNestings > 1)
+ {
+ ASMAtomicDecU32(&pThis->cNestings);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Clear the state. (cNestings == 1)
+ */
+ pThis->Owner = (pthread_t)~0;
+ ASMAtomicWriteU32(&pThis->cNestings, 0);
+
+ /*
+ * Release the mutex.
+ */
+ int32_t iNew = ASMAtomicDecS32(&pThis->iState);
+ if (RT_UNLIKELY(iNew != 0))
+ {
+ /* somebody is waiting, try wake up one of them. */
+ ASMAtomicXchgS32(&pThis->iState, 0);
+ (void)sys_futex(&pThis->iState, FUTEX_WAKE, 1, NULL, NULL, 0);
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false);
+
+ return pThis->Owner != (pthread_t)~0;
+}
+
diff --git a/src/VBox/Runtime/r3/linux/semwait-linux.h b/src/VBox/Runtime/r3/linux/semwait-linux.h
new file mode 100644
index 00000000..0f533845
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/semwait-linux.h
@@ -0,0 +1,233 @@
+/* $Id: semwait-linux.h $ */
+/** @file
+ * IPRT - Common semaphore wait code, Linux.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_linux_semwait_linux_h
+#define IPRT_INCLUDED_SRC_r3_linux_semwait_linux_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/* With 2.6.17 futex.h has become C++ unfriendly, so define the bits we need. */
+#define FUTEX_WAIT 0
+#define FUTEX_WAKE 1
+#define FUTEX_WAIT_BITSET 9 /**< @since 2.6.25 - uses absolute timeout. */
+
+
+/**
+ * Wrapper for the futex syscall.
+ */
+DECLINLINE(long) sys_futex(uint32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
+{
+ errno = 0;
+ long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
+ if (rc < 0)
+ {
+ Assert(rc == -1);
+ rc = -errno;
+ }
+ return rc;
+}
+
+
+DECL_NO_INLINE(static, void) rtSemLinuxCheckForFutexWaitBitSetSlow(int volatile *pfCanUseWaitBitSet)
+{
+ uint32_t uTestVar = UINT32_MAX;
+ long rc = sys_futex(&uTestVar, FUTEX_WAIT_BITSET, UINT32_C(0xf0f0f0f0), NULL, NULL, UINT32_MAX);
+ *pfCanUseWaitBitSet = rc == -EAGAIN;
+ AssertMsg(rc == -ENOSYS || rc == -EAGAIN, ("%d\n", rc));
+}
+
+
+DECLINLINE(void) rtSemLinuxCheckForFutexWaitBitSet(int volatile *pfCanUseWaitBitSet)
+{
+ if (*pfCanUseWaitBitSet != -1)
+ { /* likely */ }
+ else
+ rtSemLinuxCheckForFutexWaitBitSetSlow(pfCanUseWaitBitSet);
+}
+
+
+/**
+ * Converts a extended wait timeout specification to an timespec and
+ * corresponding futex operation, as well as an approximate relative nanosecond
+ * interval.
+ *
+ * @note This does not check for RTSEMWAIT_FLAGS_INDEFINITE, caller should've
+ * done that already.
+ *
+ * @returns The relative wait in nanoseconds. 0 for a poll call, UINT64_MAX for
+ * an effectively indefinite wait.
+ * @param fFlags RTSEMWAIT_FLAGS_XXX.
+ * @param fCanUseWaitBitSet Whether we can use FUTEX_WAIT_BITMSET or not.
+ * @param uTimeout The timeout.
+ * @param pDeadline Where to return the deadline.
+ * @param piWaitOp Where to return the FUTEX wait operation number.
+ * @param puWaitVal3 Where to return the FUTEX wait value 3.
+ * @param pnsAbsTimeout Where to return the absolute timeout in case of
+ * a resuming relative call (i.e. FUTEX_WAIT).
+ */
+DECL_FORCE_INLINE(uint64_t)
+rtSemLinuxCalcDeadline(uint32_t fFlags, uint64_t uTimeout, int fCanUseWaitBitSet,
+ struct timespec *pDeadline, int *piWaitOp, uint32_t *puWaitVal3, uint64_t *pnsAbsTimeout)
+{
+ Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE));
+
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
+ {
+ Assert(!(fFlags & RTSEMWAIT_FLAGS_ABSOLUTE));
+
+ /*
+ * Polling call?
+ */
+ if (uTimeout == 0)
+ return 0;
+
+ /*
+ * We use FUTEX_WAIT here as it takes a relative timespec.
+ *
+ * Note! For non-resuming waits, we can skip calculating the absolute
+ * time ASSUMING it is only needed for timeout adjustments
+ * after an -EINTR return.
+ */
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ {
+ if ( sizeof(pDeadline->tv_sec) >= sizeof(uint64_t)
+ || uTimeout < (uint64_t)UINT32_MAX * RT_MS_1SEC)
+ {
+ pDeadline->tv_sec = uTimeout / RT_MS_1SEC;
+ pDeadline->tv_nsec = (uTimeout % RT_MS_1SEC) & RT_NS_1MS;
+ uTimeout *= RT_NS_1MS;
+ }
+ else
+ return UINT64_MAX;
+ }
+ else
+ {
+ Assert(fFlags & RTSEMWAIT_FLAGS_NANOSECS);
+ if ( sizeof(pDeadline->tv_sec) >= sizeof(uint64_t)
+ || uTimeout < (uint64_t)UINT32_MAX * RT_NS_1SEC)
+ {
+ pDeadline->tv_sec = uTimeout / RT_NS_1SEC;
+ pDeadline->tv_nsec = uTimeout % RT_NS_1SEC;
+ }
+ else
+ return UINT64_MAX;
+ }
+
+#ifdef RT_STRICT
+ if (!(fFlags & RTSEMWAIT_FLAGS_RESUME))
+ *pnsAbsTimeout = uTimeout;
+ else
+#endif
+ *pnsAbsTimeout = RTTimeNanoTS() + uTimeout; /* Note! only relevant for relative waits (FUTEX_WAIT). */
+ }
+ else
+ {
+ /* Absolute deadline: */
+ Assert(fFlags & RTSEMWAIT_FLAGS_ABSOLUTE);
+ if (fCanUseWaitBitSet == true)
+ {
+ /*
+ * Use FUTEX_WAIT_BITSET as it takes an absolute deadline.
+ */
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ {
+ if ( sizeof(pDeadline->tv_sec) >= sizeof(uint64_t)
+ || uTimeout < (uint64_t)UINT32_MAX * RT_MS_1SEC)
+ {
+ pDeadline->tv_sec = uTimeout / RT_MS_1SEC;
+ pDeadline->tv_nsec = (uTimeout % RT_MS_1SEC) & RT_NS_1MS;
+ }
+ else
+ return UINT64_MAX;
+ }
+ else
+ {
+ Assert(fFlags & RTSEMWAIT_FLAGS_NANOSECS);
+ if ( sizeof(pDeadline->tv_sec) >= sizeof(uint64_t)
+ || uTimeout < (uint64_t)UINT32_MAX * RT_NS_1SEC)
+ {
+ pDeadline->tv_sec = uTimeout / RT_NS_1SEC;
+ pDeadline->tv_nsec = uTimeout % RT_NS_1SEC;
+ }
+ else
+ return UINT64_MAX;
+ }
+ *pnsAbsTimeout = uTimeout;
+ *piWaitOp = FUTEX_WAIT_BITSET;
+ *puWaitVal3 = UINT32_MAX;
+ return RT_MS_1SEC; /* Whatever non-zero; Whole point is not calling RTTimeNanoTS() in this path. */
+ }
+
+ /*
+ * FUTEX_WAIT_BITSET is not available, so use FUTEX_WAIT with a
+ * relative timeout.
+ */
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ {
+ if (uTimeout < UINT64_MAX / RT_NS_1MS)
+ uTimeout *= RT_NS_1MS;
+ else
+ return UINT64_MAX;
+ }
+
+ uint64_t const u64Now = RTTimeNanoTS();
+ if (u64Now < uTimeout)
+ {
+ *pnsAbsTimeout = uTimeout;
+ uTimeout -= u64Now;
+ }
+ else
+ return 0;
+
+ if ( sizeof(pDeadline->tv_sec) >= sizeof(uint64_t)
+ || uTimeout < (uint64_t)UINT32_MAX * RT_NS_1SEC)
+ {
+ pDeadline->tv_sec = uTimeout / RT_NS_1SEC;
+ pDeadline->tv_nsec = uTimeout % RT_NS_1SEC;
+ }
+ else
+ return UINT64_MAX;
+ }
+
+ *piWaitOp = FUTEX_WAIT;
+ *puWaitVal3 = 0;
+ return uTimeout;
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r3_linux_semwait_linux_h */
+
diff --git a/src/VBox/Runtime/r3/linux/sysfs.cpp b/src/VBox/Runtime/r3/linux/sysfs.cpp
new file mode 100644
index 00000000..6324fe00
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/sysfs.cpp
@@ -0,0 +1,736 @@
+/* $Id: sysfs.cpp $ */
+/** @file
+ * IPRT - Linux sysfs access.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYSTEM
+#include <iprt/assert.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/fs.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/symlink.h>
+
+#include <iprt/linux/sysfs.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/sysmacros.h>
+#include <errno.h>
+
+
+
+/**
+ * Constructs the path of a sysfs file from the format parameters passed,
+ * prepending a prefix if the path is relative.
+ *
+ * @returns IPRT status code.
+ * @param pszPrefix The prefix to prepend if the path is relative. Must end
+ * in '/'.
+ * @param pszBuf Where to write the path. Must be at least
+ * sizeof(@a pszPrefix) characters long
+ * @param cchBuf The size of the buffer pointed to by @a pszBuf.
+ * @param pszFormat The name format, either absolute or relative to the
+ * prefix specified by @a pszPrefix.
+ * @param va The format args.
+ */
+static int rtLinuxConstructPathV(char *pszBuf, size_t cchBuf,
+ const char *pszPrefix,
+ const char *pszFormat, va_list va)
+{
+ size_t const cchPrefix = strlen(pszPrefix);
+ AssertReturn(pszPrefix[cchPrefix - 1] == '/', VERR_INVALID_PARAMETER);
+ AssertReturn(cchBuf > cchPrefix + 1, VERR_INVALID_PARAMETER);
+
+ ssize_t cch = RTStrPrintf2V(pszBuf, cchBuf, pszFormat, va);
+ AssertReturn(cch >= 0, VERR_BUFFER_OVERFLOW);
+
+ if (*pszBuf != '/')
+ {
+ AssertReturn(cchBuf >= (size_t)cch + cchPrefix + 1, VERR_BUFFER_OVERFLOW);
+ memmove(pszBuf + cchPrefix, pszBuf, (size_t)cch + 1);
+ memcpy(pszBuf, pszPrefix, cchPrefix);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Constructs the path of a sysfs file from the format parameters passed,
+ * prepending a prefix if the path is relative.
+ *
+ * @returns IPRT status code.
+ * @param pszPrefix The prefix to prepend if the path is relative. Must end
+ * in '/'.
+ * @param pszBuf Where to write the path. Must be at least
+ * sizeof(@a pszPrefix) characters long
+ * @param cchBuf The size of the buffer pointed to by @a pszBuf.
+ * @param pszFormat The name format, either absolute or relative to "/sys/".
+ * @param ... The format args.
+ */
+DECLINLINE(int) rtLinuxConstructPath(char *pszBuf, size_t cchBuf,
+ const char *pszPrefix,
+ const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = rtLinuxConstructPathV(pszBuf, cchBuf, pszPrefix, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+/**
+ * Constructs the path of a sysfs file from the format parameters passed,
+ * prepending "/sys/" if the path is relative.
+ *
+ * @returns IPRT status code.
+ * @param pszBuf Where to write the path. Must be at least
+ * sizeof("/sys/") characters long
+ * @param cchBuf The size of the buffer pointed to by @a pszBuf.
+ * @param pszFormat The name format, either absolute or relative to "/sys/".
+ * @param va The format args.
+ */
+DECLINLINE(int) rtLinuxSysFsConstructPath(char *pszBuf, size_t cchBuf, const char *pszFormat, va_list va)
+{
+ return rtLinuxConstructPathV(pszBuf, cchBuf, "/sys/", pszFormat, va);
+}
+
+
+RTDECL(int) RTLinuxConstructPathV(char *pszPath, size_t cbPath, const char *pszFormat, va_list va)
+{
+ return rtLinuxSysFsConstructPath(pszPath, cbPath, pszFormat, va);
+}
+
+
+RTDECL(int) RTLinuxConstructPath(char *pszPath, size_t cbPath, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = rtLinuxSysFsConstructPath(pszPath, cbPath, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsExistsExV(const char *pszFormat, va_list va)
+{
+ int iSavedErrno = errno;
+
+ /*
+ * Construct the filename and call stat.
+ */
+ char szFilename[RTPATH_MAX];
+ int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat st;
+ int rcStat = stat(szFilename, &st);
+ if (rcStat != 0)
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ errno = iSavedErrno;
+ return rc;
+}
+
+
+RTDECL(bool) RTLinuxSysFsExistsV(const char *pszFormat, va_list va)
+{
+ return RT_SUCCESS(RTLinuxSysFsExistsExV(pszFormat, va));
+}
+
+
+RTDECL(int) RTLinuxSysFsExistsEx(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsExistsExV(pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(bool) RTLinuxSysFsExists(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ bool fRet = RTLinuxSysFsExistsV(pszFormat, va);
+ va_end(va);
+ return fRet;
+}
+
+
+RTDECL(int) RTLinuxSysFsOpenV(PRTFILE phFile, const char *pszFormat, va_list va)
+{
+ /*
+ * Construct the filename and call open.
+ */
+ char szFilename[RTPATH_MAX];
+ int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
+ if (RT_SUCCESS(rc))
+ rc = RTFileOpen(phFile, szFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsOpenExV(PRTFILE phFile, uint64_t fOpen, const char *pszFormat, va_list va)
+{
+ /*
+ * Construct the filename and call open.
+ */
+ char szFilename[RTPATH_MAX];
+ int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
+ if (RT_SUCCESS(rc))
+ rc = RTFileOpen(phFile, szFilename, fOpen);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsOpen(PRTFILE phFile, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsOpenV(phFile, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsOpenEx(PRTFILE phFile, uint64_t fOpen, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsOpenExV(phFile, fOpen, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsReadStr(RTFILE hFile, char *pszBuf, size_t cchBuf, size_t *pcchRead)
+{
+ Assert(cchBuf > 1); /* not mandatory */
+
+ int rc;
+ size_t cchRead;
+ rc = RTFileRead(hFile, pszBuf, cchBuf, &cchRead);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * ASSUME that if we've read less than we asked for, we've reached the
+ * end of the file. Otherwise, we've been given a buffer too small for
+ * the entire remainder of the file.
+ */
+ if (cchRead < cchBuf)
+ pszBuf[cchRead] = '\0';
+ else if (cchBuf)
+ {
+ rc = RTFileSeek(hFile, -1, RTFILE_SEEK_CURRENT, NULL);
+ if (RT_SUCCESS(rc))
+ rc = VERR_BUFFER_OVERFLOW;
+ cchRead = cchBuf - 1;
+ pszBuf[cchRead] = '\0';
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ else
+ {
+ if (cchBuf > 0)
+ *pszBuf = '\0';
+ cchRead = 0;
+ }
+
+ if (pcchRead)
+ *pcchRead = cchRead;
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteStr(RTFILE hFile, const char *pszBuf, size_t cchBuf, size_t *pcchWritten)
+{
+ if (!cchBuf)
+ cchBuf = strlen(pszBuf) + 1; /* Include the terminator */
+ return RTFileWrite(hFile, pszBuf, cchBuf, pcchWritten);
+}
+
+
+RTDECL(int) RTLinuxSysFsReadFile(RTFILE hFile, void *pvBuf, size_t cbBuf, size_t *pcbRead)
+{
+ int rc;
+ size_t cbRead = 0;
+
+ rc = RTFileRead(hFile, pvBuf, cbBuf, &cbRead);
+ if (RT_SUCCESS(rc))
+ {
+ if (pcbRead)
+ *pcbRead = cbRead;
+ if (cbRead < cbBuf)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /* Check for EOF */
+ uint64_t offCur = 0;
+ uint8_t bRead;
+ rc = RTFileSeek(hFile, 0, RTFILE_SEEK_CURRENT, &offCur);
+ if (RT_SUCCESS(rc))
+ {
+ int rc2 = RTFileRead(hFile, &bRead, 1, NULL);
+ if (RT_SUCCESS(rc2))
+ {
+ rc = VERR_BUFFER_OVERFLOW;
+
+ rc2 = RTFileSeek(hFile, offCur, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_FAILURE(rc2))
+ rc = rc2;
+ }
+ else if (rc2 != VERR_EOF)
+ rc = rc2;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteFile(RTFILE hFile, void *pvBuf, size_t cbBuf, size_t *pcbWritten)
+{
+ return RTFileWrite(hFile, pvBuf, cbBuf, pcbWritten);
+}
+
+
+RTDECL(int) RTLinuxSysFsReadIntFileV(unsigned uBase, int64_t *pi64, const char *pszFormat, va_list va)
+{
+ RTFILE hFile;
+
+ AssertPtrReturn(pi64, VERR_INVALID_POINTER);
+
+ int rc = RTLinuxSysFsOpenV(&hFile, pszFormat, va);
+ if (RT_SUCCESS(rc))
+ {
+ char szNum[128];
+ size_t cchNum;
+ rc = RTLinuxSysFsReadStr(hFile, szNum, sizeof(szNum), &cchNum);
+ if (RT_SUCCESS(rc))
+ {
+ if (cchNum > 0)
+ {
+ int64_t i64Ret = -1;
+ rc = RTStrToInt64Ex(szNum, NULL, uBase, &i64Ret);
+ if (RT_SUCCESS(rc))
+ *pi64 = i64Ret;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ RTFileClose(hFile);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsReadIntFile(unsigned uBase, int64_t *pi64, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsReadIntFileV(uBase, pi64, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteU8FileV(unsigned uBase, uint8_t u8, const char *pszFormat, va_list va)
+{
+ return RTLinuxSysFsWriteU64FileV(uBase, u8, pszFormat, va);
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteU8File(unsigned uBase, uint8_t u8, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsWriteU64FileV(uBase, u8, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteU16FileV(unsigned uBase, uint16_t u16, const char *pszFormat, va_list va)
+{
+ return RTLinuxSysFsWriteU64FileV(uBase, u16, pszFormat, va);
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteU16File(unsigned uBase, uint16_t u16, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsWriteU64FileV(uBase, u16, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteU32FileV(unsigned uBase, uint32_t u32, const char *pszFormat, va_list va)
+{
+ return RTLinuxSysFsWriteU64FileV(uBase, u32, pszFormat, va);
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteU32File(unsigned uBase, uint32_t u32, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsWriteU64FileV(uBase, u32, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteU64FileV(unsigned uBase, uint64_t u64, const char *pszFormat, va_list va)
+{
+ RTFILE hFile;
+
+ const char *pszFmt = NULL;
+ switch (uBase)
+ {
+ case 8:
+ pszFmt = "%#llo";
+ break;
+ case 10:
+ pszFmt = "%llu";
+ break;
+ case 16:
+ pszFmt = "%#llx";
+ break;
+ default:
+ return VERR_INVALID_PARAMETER;
+ }
+
+ int rc = RTLinuxSysFsOpenExV(&hFile, RTFILE_O_OPEN | RTFILE_O_WRITE | RTFILE_O_DENY_NONE, pszFormat, va);
+ if (RT_SUCCESS(rc))
+ {
+ char szNum[128];
+ size_t cchNum = RTStrPrintf(szNum, sizeof(szNum), pszFmt, u64);
+ if (cchNum > 0)
+ {
+ size_t cbWritten = 0;
+ rc = RTLinuxSysFsWriteStr(hFile, &szNum[0], cchNum, &cbWritten);
+ if ( RT_SUCCESS(rc)
+ && cbWritten != cchNum)
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+
+ RTFileClose(hFile);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteU64File(unsigned uBase, uint32_t u64, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsWriteU64FileV(uBase, u64, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsReadDevNumFileV(dev_t *pDevNum, const char *pszFormat, va_list va)
+{
+ RTFILE hFile;
+
+ AssertPtrReturn(pDevNum, VERR_INVALID_POINTER);
+
+ int rc = RTLinuxSysFsOpenV(&hFile, pszFormat, va);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchNum = 0;
+ char szNum[128];
+ rc = RTLinuxSysFsReadStr(hFile, szNum, sizeof(szNum), &cchNum);
+ if (RT_SUCCESS(rc))
+ {
+ if (cchNum > 0)
+ {
+ uint32_t u32Maj = 0;
+ uint32_t u32Min = 0;
+ char *pszNext = NULL;
+ rc = RTStrToUInt32Ex(szNum, &pszNext, 10, &u32Maj);
+ if (RT_FAILURE(rc) || (rc != VWRN_TRAILING_CHARS) || (*pszNext != ':'))
+ rc = VERR_INVALID_PARAMETER;
+ else
+ {
+ rc = RTStrToUInt32Ex(pszNext + 1, NULL, 10, &u32Min);
+ if ( rc != VINF_SUCCESS
+ && rc != VWRN_TRAILING_CHARS
+ && rc != VWRN_TRAILING_SPACES)
+ rc = VERR_INVALID_PARAMETER;
+ else
+ *pDevNum = makedev(u32Maj, u32Min);
+ }
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ RTFileClose(hFile);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsReadDevNumFile(dev_t *pDevNum, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsReadDevNumFileV(pDevNum, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsReadStrFileV(char *pszBuf, size_t cchBuf, size_t *pcchRead, const char *pszFormat, va_list va)
+{
+ RTFILE hFile;
+
+ AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
+
+ int rc = RTLinuxSysFsOpenV(&hFile, pszFormat, va);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Note! We cannot use RTLinuxSysFsReadStr here as it has different
+ * semantics wrt to newline characters. It is not known why
+ * the semantics has to differ... Michael, any clues?
+ */
+ size_t cchRead;
+ rc = RTFileRead(hFile, pszBuf, cchBuf, &cchRead);
+ if (RT_SUCCESS(rc))
+ {
+ char *pchNewLine = (char *)memchr(pszBuf, '\n', cchRead);
+ if (pchNewLine)
+ {
+ *pchNewLine = '\0';
+ cchRead = pchNewLine - pszBuf;
+ }
+ else if (cchRead < cchBuf)
+ pszBuf[cchRead] = '\0';
+ else
+ {
+ if (cchBuf)
+ {
+ cchRead = cchBuf - 1;
+ pszBuf[cchRead] = '\0';
+ }
+ else
+ cchRead = 0;
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ cchRead = 0;
+
+ RTFileClose(hFile);
+
+ if (pcchRead)
+ *pcchRead = cchRead;
+ }
+ else
+ {
+ if (cchBuf)
+ *pszBuf = '\0';
+ if (pcchRead)
+ *pcchRead = 0;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsReadStrFile(char *pszBuf, size_t cchBuf, size_t *pcchRead, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsReadStrFileV(pszBuf, cchBuf, pcchRead, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteStrFileV(const char *pszBuf, size_t cchBuf, size_t *pcchWritten, const char *pszFormat, va_list va)
+{
+ RTFILE hFile;
+
+ AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
+
+ int rc = RTLinuxSysFsOpenExV(&hFile, RTFILE_O_OPEN | RTFILE_O_WRITE | RTFILE_O_DENY_NONE, pszFormat, va);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLinuxSysFsWriteStr(hFile, pszBuf, cchBuf, pcchWritten);
+ RTFileClose(hFile);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsWriteStrFile(const char *pszBuf, size_t cchBuf, size_t *pcchWritten, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsWriteStrFileV(pszBuf, cchBuf, pcchWritten, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+RTDECL(int) RTLinuxSysFsGetLinkDestV(char *pszBuf, size_t cchBuf, size_t *pchBuf, const char *pszFormat, va_list va)
+{
+ AssertReturn(cchBuf >= 2, VERR_INVALID_PARAMETER);
+
+ /*
+ * Construct the filename and read the link.
+ */
+ char szFilename[RTPATH_MAX];
+ int rc = rtLinuxSysFsConstructPath(szFilename, sizeof(szFilename), pszFormat, va);
+ if (RT_SUCCESS(rc))
+ {
+ char szLink[RTPATH_MAX];
+ rc = RTSymlinkRead(szFilename, szLink, sizeof(szLink), 0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Extract the file name component and copy it into the return buffer.
+ */
+ size_t cchName;
+ const char *pszName = RTPathFilename(szLink);
+ if (pszName)
+ {
+ cchName = strlen(pszName);
+ if (cchName < cchBuf)
+ memcpy(pszBuf, pszName, cchName + 1);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ else
+ {
+ *pszBuf = '\0';
+ cchName = 0;
+ }
+
+ if (pchBuf)
+ *pchBuf = cchName;
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxSysFsGetLinkDest(char *pszBuf, size_t cchBuf, size_t *pchBuf, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTLinuxSysFsGetLinkDestV(pszBuf, cchBuf, pchBuf, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxCheckDevicePathV(dev_t DevNum, RTFMODE fMode, char *pszBuf,
+ size_t cchBuf, const char *pszPattern,
+ va_list va)
+{
+ AssertReturn(cchBuf >= 2, VERR_INVALID_PARAMETER);
+ AssertReturn( fMode == RTFS_TYPE_DEV_CHAR
+ || fMode == RTFS_TYPE_DEV_BLOCK,
+ VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszPattern, VERR_INVALID_PARAMETER);
+
+ /*
+ * Construct the filename and read the link.
+ */
+ char szFilename[RTPATH_MAX];
+ int rc = rtLinuxConstructPathV(szFilename, sizeof(szFilename), "/dev/",
+ pszPattern, va);
+ if (RT_SUCCESS(rc))
+ {
+ RTFSOBJINFO Info;
+ rc = RTPathQueryInfo(szFilename, &Info, RTFSOBJATTRADD_UNIX);
+ if ( rc == VERR_PATH_NOT_FOUND
+ || ( RT_SUCCESS(rc)
+ && ( Info.Attr.u.Unix.Device != DevNum
+ || (Info.Attr.fMode & RTFS_TYPE_MASK) != fMode)))
+ rc = VERR_FILE_NOT_FOUND;
+
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchPath = strlen(szFilename);
+ if (cchPath < cchBuf)
+ memcpy(pszBuf, szFilename, cchPath + 1);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLinuxCheckDevicePath(dev_t DevNum, RTFMODE fMode, char *pszBuf,
+ size_t cchBuf, const char *pszPattern,
+ ...)
+{
+ va_list va;
+ va_start(va, pszPattern);
+ int rc = RTLinuxCheckDevicePathV(DevNum, fMode, pszBuf, cchBuf,
+ pszPattern, va);
+ va_end(va);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/linux/systemmem-linux.cpp b/src/VBox/Runtime/r3/linux/systemmem-linux.cpp
new file mode 100644
index 00000000..11764ab7
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/systemmem-linux.cpp
@@ -0,0 +1,119 @@
+/* $Id: systemmem-linux.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryTotalRam, Linux ring-3.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+/* Satisfy compiller warning */
+#define __EXPORTED_HEADERS__
+#include <sys/sysinfo.h>
+#undef __EXPORTED_HEADERS__
+
+
+RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ struct sysinfo info;
+ int rc = sysinfo(&info);
+ if (rc == 0)
+ {
+ *pcb = (uint64_t)info.totalram * info.mem_unit;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ FILE *pFile = fopen("/proc/meminfo", "r");
+ if (pFile)
+ {
+ int rc = VERR_NOT_FOUND;
+ uint64_t cbTotal = 0;
+ uint64_t cbFree = 0;
+ uint64_t cbBuffers = 0;
+ uint64_t cbCached = 0;
+ char sz[256];
+ while (fgets(sz, sizeof(sz), pFile))
+ {
+ if (!strncmp(sz, RT_STR_TUPLE("MemTotal:")))
+ rc = RTStrToUInt64Ex(RTStrStripL(&sz[sizeof("MemTotal:")]), NULL, 0, &cbTotal);
+ else if (!strncmp(sz, RT_STR_TUPLE("MemFree:")))
+ rc = RTStrToUInt64Ex(RTStrStripL(&sz[sizeof("MemFree:")]), NULL, 0, &cbFree);
+ else if (!strncmp(sz, RT_STR_TUPLE("Buffers:")))
+ rc = RTStrToUInt64Ex(RTStrStripL(&sz[sizeof("Buffers:")]), NULL, 0, &cbBuffers);
+ else if (!strncmp(sz, RT_STR_TUPLE("Cached:")))
+ rc = RTStrToUInt64Ex(RTStrStripL(&sz[sizeof("Cached:")]), NULL, 0, &cbCached);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ fclose(pFile);
+ if (RT_SUCCESS(rc))
+ {
+ *pcb = (cbFree + cbBuffers + cbCached) * _1K;
+ return VINF_SUCCESS;
+ }
+ }
+ /*
+ * Fallback (e.g. /proc not mapped) to sysinfo. Less accurat because there
+ * is no information about the cached memory. 'Cached:' from above is only
+ * accessible through proc :-(
+ */
+ struct sysinfo info;
+ int rc = sysinfo(&info);
+ if (rc == 0)
+ {
+ *pcb = ((uint64_t)info.freeram + info.bufferram) * info.mem_unit;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
diff --git a/src/VBox/Runtime/r3/linux/thread-affinity-linux.cpp b/src/VBox/Runtime/r3/linux/thread-affinity-linux.cpp
new file mode 100644
index 00000000..2726e716
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/thread-affinity-linux.cpp
@@ -0,0 +1,105 @@
+/* $Id: thread-affinity-linux.cpp $ */
+/** @file
+ * IPRT - Thread Affinity, Linux ring-3 implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <features.h>
+#if __GLIBC_PREREQ(2,4)
+
+#include <sched.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+#include <iprt/mp.h>
+
+
+
+RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet)
+{
+ /* convert */
+ cpu_set_t LnxCpuSet;
+ CPU_ZERO(&LnxCpuSet);
+ if (!pCpuSet)
+ for (unsigned iCpu = 0; iCpu < CPU_SETSIZE; iCpu++)
+ CPU_SET(iCpu, &LnxCpuSet);
+ else
+ for (unsigned iCpu = 0; iCpu < RT_MIN(CPU_SETSIZE, RTCPUSET_MAX_CPUS); iCpu++)
+ if (RTCpuSetIsMemberByIndex(pCpuSet, iCpu))
+ CPU_SET(iCpu, &LnxCpuSet);
+
+ int rc = pthread_setaffinity_np(pthread_self(), sizeof(LnxCpuSet), &LnxCpuSet);
+ if (!rc)
+ return VINF_SUCCESS;
+ rc = errno;
+ if (rc == ENOENT)
+ return VERR_CPU_NOT_FOUND;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet)
+{
+ cpu_set_t LnxCpuSet;
+ int rc = pthread_getaffinity_np(pthread_self(), sizeof(LnxCpuSet), &LnxCpuSet);
+ if (rc != 0)
+ return RTErrConvertFromErrno(errno);
+
+ /* convert */
+ RTCpuSetEmpty(pCpuSet);
+ for (unsigned iCpu = 0; iCpu < RT_MIN(CPU_SETSIZE, RTCPUSET_MAX_CPUS); iCpu++)
+ if (CPU_ISSET(iCpu, &LnxCpuSet))
+ RTCpuSetAddByIndex(pCpuSet, iCpu);
+
+ return VINF_SUCCESS;
+}
+
+#else
+# include "../../generic/RTThreadGetAffinity-stub-generic.cpp"
+# include "../../generic/RTThreadSetAffinity-stub-generic.cpp"
+#endif
+
diff --git a/src/VBox/Runtime/r3/linux/time-linux.cpp b/src/VBox/Runtime/r3/linux/time-linux.cpp
new file mode 100644
index 00000000..6ceac2de
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/time-linux.cpp
@@ -0,0 +1,169 @@
+/* $Id: time-linux.cpp $ */
+/** @file
+ * IPRT - Time, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#define RTTIME_INCL_TIMEVAL
+#include <sys/time.h>
+#include <time.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#ifndef __NR_clock_gettime
+# define __NR_timer_create 259
+# define __NR_clock_gettime (__NR_timer_create+6)
+#endif
+
+#include <iprt/time.h>
+#include "internal/time.h"
+
+
+DECLINLINE(int) sys_clock_gettime(clockid_t id, struct timespec *ts)
+{
+ int rc = syscall(__NR_clock_gettime, id, ts);
+ if (rc >= 0)
+ return rc;
+ return -1;
+}
+
+
+/**
+ * Wrapper around various monotone time sources.
+ */
+DECLINLINE(int) mono_clock(struct timespec *ts)
+{
+ static int iWorking = -1;
+ switch (iWorking)
+ {
+#ifdef CLOCK_MONOTONIC
+ /*
+ * Standard clock_gettime()
+ */
+ case 0:
+ return clock_gettime(CLOCK_MONOTONIC, ts);
+
+ /*
+ * Syscall clock_gettime().
+ */
+ case 1:
+ return sys_clock_gettime(CLOCK_MONOTONIC, ts);
+
+#endif /* CLOCK_MONOTONIC */
+
+
+ /*
+ * Figure out what's working.
+ */
+ case -1:
+ {
+#ifdef CLOCK_MONOTONIC
+ /*
+ * Real-Time API.
+ */
+ int rc = clock_gettime(CLOCK_MONOTONIC, ts);
+ if (!rc)
+ {
+ iWorking = 0;
+ return 0;
+ }
+
+ rc = sys_clock_gettime(CLOCK_MONOTONIC, ts);
+ if (!rc)
+ {
+ iWorking = 1;
+ return 0;
+ }
+#endif /* CLOCK_MONOTONIC */
+
+ /* give up */
+ iWorking = -2;
+ break;
+ }
+ }
+ return -1;
+}
+
+
+DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void)
+{
+ /* check monotonic clock first. */
+ static bool fMonoClock = true;
+ if (fMonoClock)
+ {
+ struct timespec ts;
+ if (!mono_clock(&ts))
+ return (uint64_t)ts.tv_sec * RT_NS_1SEC_64
+ + ts.tv_nsec;
+ fMonoClock = false;
+ }
+
+ /* fallback to gettimeofday(). */
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (uint64_t)tv.tv_sec * RT_NS_1SEC_64
+ + (uint64_t)(tv.tv_usec * RT_NS_1US);
+}
+
+
+/**
+ * Gets the current nanosecond timestamp.
+ *
+ * This differs from RTTimeNanoTS in that it will use system APIs and not do any
+ * resolution or performance optimizations.
+ *
+ * @returns nanosecond timestamp.
+ */
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+/**
+ * Gets the current millisecond timestamp.
+ *
+ * This differs from RTTimeNanoTS in that it will use system APIs and not do any
+ * resolution or performance optimizations.
+ *
+ * @returns millisecond timestamp.
+ */
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
diff --git a/src/VBox/Runtime/r3/linux/tpm-linux.cpp b/src/VBox/Runtime/r3/linux/tpm-linux.cpp
new file mode 100644
index 00000000..4851eabc
--- /dev/null
+++ b/src/VBox/Runtime/r3/linux/tpm-linux.cpp
@@ -0,0 +1,229 @@
+/* $Id: tpm-linux.cpp $ */
+/** @file
+ * IPRT - Trusted Platform Module (TPM) access, Linux variant.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/tpm.h>
+
+#include <iprt/assertcompile.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/linux/sysfs.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal TPM instance data.
+ */
+typedef struct RTTPMINT
+{
+ /** Handle to the /dev/tpmX device. */
+ RTFILE hTpmDev;
+ /** Handle to the sysfs cancel interface. */
+ RTFILE hTpmCancel;
+ /** The deduced TPM version. */
+ RTTPMVERSION enmTpmVers;
+ /** Flag whether a request is currently being executed. */
+ volatile bool fReqExec;
+} RTTPMINT;
+/** Pointer to the internal TPM instance data. */
+typedef RTTPMINT *PRTTPMINT;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+RTDECL(int) RTTpmOpen(PRTTPM phTpm, uint32_t idTpm)
+{
+ AssertPtrReturn(phTpm, VERR_INVALID_POINTER);
+ if (idTpm == RTTPM_ID_DEFAULT)
+ idTpm = 0;
+
+ int rc = VINF_SUCCESS;
+ PRTTPMINT pThis = (PRTTPMINT)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->hTpmDev = NIL_RTFILE;
+ pThis->hTpmCancel = NIL_RTFILE;
+ pThis->enmTpmVers = RTTPMVERSION_UNKNOWN;
+ pThis->fReqExec = false;
+
+ rc = RTFileOpenF(&pThis->hTpmDev, RTFILE_O_OPEN | RTFILE_O_READWRITE | RTFILE_O_DENY_NONE,
+ "/dev/tpm%u", idTpm);
+ if (RT_SUCCESS(rc))
+ {
+ /* Open the sysfs path to cancel a request, either /sys/class/tpm/tpmX/device/cancel or /sys/class/misc/tpmX/device/cancel. */
+ rc = RTFileOpenF(&pThis->hTpmCancel, RTFILE_O_OPEN | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
+ "/sys/class/tpm/tpm%u/device/cancel", idTpm);
+ if (rc == VERR_FILE_NOT_FOUND)
+ rc = RTFileOpenF(&pThis->hTpmCancel, RTFILE_O_OPEN | RTFILE_O_WRITE | RTFILE_O_DENY_NONE,
+ "/sys/class/misc/tpm%u/device/cancel", idTpm);
+ if ( RT_SUCCESS(rc)
+ || rc == VERR_FILE_NOT_FOUND)
+ {
+ /* Try to figure out the TPM version. */
+ int64_t iVersion = 0;
+ rc = RTLinuxSysFsReadIntFile(10 /*uBase*/, &iVersion, "/sys/class/tpm/tpm%u/tpm_version_major", idTpm);
+ if (rc == VERR_FILE_NOT_FOUND)
+ rc = RTLinuxSysFsReadIntFile(10 /*uBase*/, &iVersion, "/sys/class/misc/tpm%u/tpm_version_major", idTpm);
+ if (RT_SUCCESS(rc))
+ {
+ if (iVersion == 1)
+ pThis->enmTpmVers = RTTPMVERSION_1_2;
+ else if (iVersion == 2)
+ pThis->enmTpmVers = RTTPMVERSION_2_0;
+ }
+
+ *phTpm = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTFileClose(pThis->hTpmDev);
+ pThis->hTpmDev = NIL_RTFILE;
+ }
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+RTDECL(int) RTTpmClose(RTTPM hTpm)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ RTFileClose(pThis->hTpmDev);
+ if (pThis->hTpmCancel != NIL_RTFILE)
+ RTFileClose(pThis->hTpmCancel);
+
+ pThis->hTpmDev = NIL_RTFILE;
+ pThis->hTpmCancel = NIL_RTFILE;
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTTPMVERSION) RTTpmGetVersion(RTTPM hTpm)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, RTTPMVERSION_INVALID);
+ return pThis->enmTpmVers;
+}
+
+
+RTDECL(uint32_t) RTTpmGetLocalityMax(RTTPM hTpm)
+{
+ RT_NOREF(hTpm);
+ return 0; /* On Linux only TPM locality 0 is supported. */
+}
+
+
+RTDECL(int) RTTpmReqCancel(RTTPM hTpm)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ if (pThis->hTpmCancel == NIL_RTFILE)
+ return VERR_NOT_SUPPORTED;
+
+ if (ASMAtomicReadBool(&pThis->fReqExec))
+ {
+ uint8_t bCancel = '-';
+ return RTFileWrite(pThis->hTpmCancel, &bCancel, sizeof(bCancel), NULL /*pcbWritten*/);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTpmReqExec(RTTPM hTpm, uint8_t bLoc, const void *pvReq, size_t cbReq,
+ void *pvResp, size_t cbRespMax, size_t *pcbResp)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvReq, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvResp, VERR_INVALID_POINTER);
+ AssertReturn(cbReq && cbRespMax, VERR_INVALID_PARAMETER);
+ AssertReturn(bLoc == 0, VERR_NOT_SUPPORTED); /** @todo There doesn't seem to be a way to use a different locality. */
+
+ /* The request has to be supplied by a single blocking write. */
+ ASMAtomicXchgBool(&pThis->fReqExec, true);
+ int rc = RTFileWrite(pThis->hTpmDev, pvReq, cbReq, NULL /*pcbWritten*/);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbResp = 0;
+ /* The response has to be retrieved in a single read as well. */
+ rc = RTFileRead(pThis->hTpmDev, pvResp, cbRespMax, &cbResp);
+ ASMAtomicXchgBool(&pThis->fReqExec, false);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check whether the response is complete. */
+ if ( cbResp >= sizeof(TPMRESPHDR)
+ && RTTpmRespGetSz((PCTPMRESPHDR)pvResp) == cbResp)
+ {
+ if (pcbResp)
+ *pcbResp = cbResp;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/memsafer-r3.cpp b/src/VBox/Runtime/r3/memsafer-r3.cpp
new file mode 100644
index 00000000..bafeed96
--- /dev/null
+++ b/src/VBox/Runtime/r3/memsafer-r3.cpp
@@ -0,0 +1,691 @@
+/* $Id: memsafer-r3.cpp $ */
+/** @file
+ * IPRT - Memory Allocate for Sensitive Data, generic heap-based implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/memsafer.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/avl.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/rand.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#ifdef IN_SUP_R3
+# include <VBox/sup.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Allocation size alignment (power of two). */
+#define RTMEMSAFER_ALIGN 16
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Allocators.
+ */
+typedef enum RTMEMSAFERALLOCATOR
+{
+ /** Invalid method. */
+ RTMEMSAFERALLOCATOR_INVALID = 0,
+ /** RTMemPageAlloc. */
+ RTMEMSAFERALLOCATOR_RTMEMPAGE,
+ /** SUPR3PageAllocEx. */
+ RTMEMSAFERALLOCATOR_SUPR3
+} RTMEMSAFERALLOCATOR;
+
+/**
+ * Tracking node (lives on normal heap).
+ */
+typedef struct RTMEMSAFERNODE
+{
+ /** Node core.
+ * The core key is a scrambled pointer the user memory. */
+ AVLPVNODECORE Core;
+ /** The allocation flags. */
+ uint32_t fFlags;
+ /** The offset into the allocation of the user memory. */
+ uint32_t offUser;
+ /** The requested allocation size. */
+ size_t cbUser;
+ /** The allocation size in pages, this includes the two guard pages. */
+ uint32_t cPages;
+ /** The allocator used for this node. */
+ RTMEMSAFERALLOCATOR enmAllocator;
+ /** XOR scrambler value for memory. */
+ uintptr_t uScramblerXor;
+} RTMEMSAFERNODE;
+/** Pointer to an allocation tracking node. */
+typedef RTMEMSAFERNODE *PRTMEMSAFERNODE;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Init once structure for this module. */
+static RTONCE g_MemSaferOnce = RTONCE_INITIALIZER;
+/** Critical section protecting the allocation tree. */
+static RTCRITSECTRW g_MemSaferCritSect;
+/** Tree of allocation nodes. */
+static AVLPVTREE g_pMemSaferTree;
+/** XOR scrambler value pointers. */
+static uintptr_t g_uMemSaferPtrScramblerXor;
+/** Pointer rotate shift count.*/
+static uintptr_t g_cMemSaferPtrScramblerRotate;
+
+
+/**
+ * @callback_method_impl{FNRTONCE, Inits globals.}
+ */
+static DECLCALLBACK(int32_t) rtMemSaferOnceInit(void *pvUserIgnore)
+{
+ RT_NOREF_PV(pvUserIgnore);
+
+ g_uMemSaferPtrScramblerXor = (uintptr_t)RTRandU64();
+ g_cMemSaferPtrScramblerRotate = RTRandU32Ex(0, ARCH_BITS - 1);
+ return RTCritSectRwInit(&g_MemSaferCritSect);
+}
+
+
+/**
+ * @callback_method_impl{PFNRTONCECLEANUP, Cleans up globals.}
+ */
+static DECLCALLBACK(void) rtMemSaferOnceTerm(void *pvUser, bool fLazyCleanUpOk)
+{
+ RT_NOREF_PV(pvUser);
+
+ if (!fLazyCleanUpOk)
+ {
+ RTCritSectRwDelete(&g_MemSaferCritSect);
+ Assert(!g_pMemSaferTree);
+ }
+}
+
+
+
+DECLINLINE(void *) rtMemSaferScramblePointer(void *pvUser)
+{
+ uintptr_t uPtr = (uintptr_t)pvUser;
+ uPtr ^= g_uMemSaferPtrScramblerXor;
+#if ARCH_BITS == 64
+ uPtr = ASMRotateRightU64(uPtr, g_cMemSaferPtrScramblerRotate);
+#elif ARCH_BITS == 32
+ uPtr = ASMRotateRightU32(uPtr, g_cMemSaferPtrScramblerRotate);
+#else
+# error "Unsupported/missing ARCH_BITS."
+#endif
+ return (void *)uPtr;
+}
+
+
+/**
+ * Inserts a tracking node into the tree.
+ *
+ * @param pThis The allocation tracking node to insert.
+ */
+static void rtMemSaferNodeInsert(PRTMEMSAFERNODE pThis)
+{
+ RTCritSectRwEnterExcl(&g_MemSaferCritSect);
+ pThis->Core.Key = rtMemSaferScramblePointer(pThis->Core.Key);
+ bool fRc = RTAvlPVInsert(&g_pMemSaferTree, &pThis->Core);
+ RTCritSectRwLeaveExcl(&g_MemSaferCritSect);
+ Assert(fRc); NOREF(fRc);
+}
+
+
+/**
+ * Finds a tracking node into the tree.
+ *
+ * @returns The allocation tracking node for @a pvUser. NULL if not found.
+ * @param pvUser The user pointer to the allocation.
+ */
+static PRTMEMSAFERNODE rtMemSaferNodeLookup(void *pvUser)
+{
+ void *pvKey = rtMemSaferScramblePointer(pvUser);
+ RTCritSectRwEnterShared(&g_MemSaferCritSect);
+ PRTMEMSAFERNODE pThis = (PRTMEMSAFERNODE)RTAvlPVGet(&g_pMemSaferTree, pvKey);
+ RTCritSectRwLeaveShared(&g_MemSaferCritSect);
+ return pThis;
+}
+
+
+/**
+ * Removes a tracking node from the tree.
+ *
+ * @returns The allocation tracking node for @a pvUser. NULL if not found.
+ * @param pvUser The user pointer to the allocation.
+ */
+static PRTMEMSAFERNODE rtMemSaferNodeRemove(void *pvUser)
+{
+ void *pvKey = rtMemSaferScramblePointer(pvUser);
+ RTCritSectRwEnterExcl(&g_MemSaferCritSect);
+ PRTMEMSAFERNODE pThis = (PRTMEMSAFERNODE)RTAvlPVRemove(&g_pMemSaferTree, pvKey);
+ RTCritSectRwLeaveExcl(&g_MemSaferCritSect);
+ return pThis;
+}
+
+
+RTDECL(int) RTMemSaferScramble(void *pv, size_t cb)
+{
+ PRTMEMSAFERNODE pThis = rtMemSaferNodeLookup(pv);
+ AssertReturn(pThis, VERR_INVALID_POINTER);
+ AssertMsgReturn(cb == pThis->cbUser, ("cb=%#zx != %#zx\n", cb, pThis->cbUser), VERR_INVALID_PARAMETER);
+
+ /* First time we get a new xor value. */
+ if (!pThis->uScramblerXor)
+ pThis->uScramblerXor = (uintptr_t)RTRandU64();
+
+ /* Note! This isn't supposed to be safe, just less obvious. */
+ uintptr_t *pu = (uintptr_t *)pv;
+ cb = RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN);
+ while (cb > 0)
+ {
+ *pu ^= pThis->uScramblerXor;
+ pu++;
+ cb -= sizeof(*pu);
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMemSaferScramble);
+
+
+RTDECL(int) RTMemSaferUnscramble(void *pv, size_t cb)
+{
+ PRTMEMSAFERNODE pThis = rtMemSaferNodeLookup(pv);
+ AssertReturn(pThis, VERR_INVALID_POINTER);
+ AssertMsgReturn(cb == pThis->cbUser, ("cb=%#zx != %#zx\n", cb, pThis->cbUser), VERR_INVALID_PARAMETER);
+
+ /* Note! This isn't supposed to be safe, just less obvious. */
+ uintptr_t *pu = (uintptr_t *)pv;
+ cb = RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN);
+ while (cb > 0)
+ {
+ *pu ^= pThis->uScramblerXor;
+ pu++;
+ cb -= sizeof(*pu);
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMemSaferUnscramble);
+
+
+/**
+ * Initializes the pages.
+ *
+ * Fills the memory with random bytes in order to make it less obvious where the
+ * secret data starts and ends. We also zero the user memory in case the
+ * allocator does not do this.
+ *
+ * @param pThis The allocation tracer node. The Core.Key member
+ * will be set.
+ * @param pvPages The pages to initialize.
+ */
+static void rtMemSaferInitializePages(PRTMEMSAFERNODE pThis, void *pvPages)
+{
+ RTRandBytes(pvPages, PAGE_SIZE + pThis->offUser);
+
+ uint8_t *pbUser = (uint8_t *)pvPages + PAGE_SIZE + pThis->offUser;
+ pThis->Core.Key = pbUser;
+ RT_BZERO(pbUser, pThis->cbUser); /* paranoia */
+
+ RTRandBytes(pbUser + pThis->cbUser, (size_t)pThis->cPages * PAGE_SIZE - PAGE_SIZE - pThis->offUser - pThis->cbUser);
+}
+
+
+/**
+ * Allocates and initializes pages from the support driver and initializes it.
+ *
+ * @returns VBox status code.
+ * @param pThis The allocator node. Core.Key will be set on successful
+ * return (unscrambled).
+ */
+static int rtMemSaferSupR3AllocPages(PRTMEMSAFERNODE pThis)
+{
+#ifdef IN_SUP_R3
+ /*
+ * Try allocate the memory.
+ */
+ void *pvPages;
+ int rc = SUPR3PageAllocEx(pThis->cPages, 0 /* fFlags */, &pvPages, NULL /* pR0Ptr */, NULL /* paPages */);
+ if (RT_SUCCESS(rc))
+ {
+ rtMemSaferInitializePages(pThis, pvPages);
+
+ /*
+ * On darwin we cannot allocate pages without an R0 mapping and
+ * SUPR3PageAllocEx falls back to another method which is incompatible with
+ * the way SUPR3PageProtect works. Ignore changing the protection of the guard
+ * pages.
+ */
+#ifdef RT_OS_DARWIN
+ return VINF_SUCCESS;
+#else
+ /*
+ * Configure the guard pages.
+ * SUPR3PageProtect isn't supported on all hosts, we ignore that.
+ */
+ rc = SUPR3PageProtect(pvPages, NIL_RTR0PTR, 0, PAGE_SIZE, RTMEM_PROT_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = SUPR3PageProtect(pvPages, NIL_RTR0PTR, (pThis->cPages - 1) * PAGE_SIZE, PAGE_SIZE, RTMEM_PROT_NONE);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ SUPR3PageProtect(pvPages, NIL_RTR0PTR, 0, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ }
+ else if (rc == VERR_NOT_SUPPORTED)
+ return VINF_SUCCESS;
+
+ /* failed. */
+ int rc2 = SUPR3PageFreeEx(pvPages, pThis->cPages); AssertRC(rc2);
+#endif
+ }
+ return rc;
+
+#else /* !IN_SUP_R3 */
+ RT_NOREF_PV(pThis);
+ return VERR_NOT_SUPPORTED;
+#endif /* !IN_SUP_R3 */
+}
+
+
+/**
+ * Allocates and initializes pages using the IPRT page allocator API.
+ *
+ * @returns VBox status code.
+ * @param pThis The allocator node. Core.Key will be set on successful
+ * return (unscrambled).
+ */
+static int rtMemSaferMemAllocPages(PRTMEMSAFERNODE pThis)
+{
+ /*
+ * Try allocate the memory.
+ */
+ int rc = VINF_SUCCESS;
+ void *pvPages = RTMemPageAllocEx((size_t)pThis->cPages * PAGE_SIZE,
+ RTMEMPAGEALLOC_F_ADVISE_LOCKED | RTMEMPAGEALLOC_F_ADVISE_NO_DUMP | RTMEMPAGEALLOC_F_ZERO);
+ if (pvPages)
+ {
+ rtMemSaferInitializePages(pThis, pvPages);
+
+ /*
+ * Configure the guard pages.
+ */
+ rc = RTMemProtect(pvPages, PAGE_SIZE, RTMEM_PROT_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTMemProtect((uint8_t *)pvPages + (size_t)(pThis->cPages - 1U) * PAGE_SIZE, PAGE_SIZE, RTMEM_PROT_NONE);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ rc = RTMemProtect(pvPages, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ }
+
+ /* failed. */
+ RTMemPageFree(pvPages, (size_t)pThis->cPages * PAGE_SIZE);
+ }
+ else
+ rc = VERR_NO_PAGE_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTMemSaferAllocZExTag(void **ppvNew, size_t cb, uint32_t fFlags, const char *pszTag) RT_NO_THROW_DEF
+{
+ RT_NOREF_PV(pszTag);
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(ppvNew, VERR_INVALID_PARAMETER);
+ *ppvNew = NULL;
+ AssertReturn(cb, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= 32U*_1M - PAGE_SIZE * 3U, VERR_ALLOCATION_TOO_BIG); /* Max 32 MB minus padding and guard pages. */
+ AssertReturn(!(fFlags & ~RTMEMSAFER_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+ /*
+ * Initialize globals.
+ */
+ int rc = RTOnceEx(&g_MemSaferOnce, rtMemSaferOnceInit, rtMemSaferOnceTerm, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate a tracker node first.
+ */
+ PRTMEMSAFERNODE pThis = (PRTMEMSAFERNODE)RTMemAllocZ(sizeof(RTMEMSAFERNODE));
+ if (pThis)
+ {
+ /*
+ * Prepare the allocation.
+ */
+ pThis->cbUser = cb;
+ pThis->offUser = (RTRandU32Ex(0, 128) * RTMEMSAFER_ALIGN) & PAGE_OFFSET_MASK;
+
+ size_t cbNeeded = pThis->offUser + pThis->cbUser;
+ cbNeeded = RT_ALIGN_Z(cbNeeded, PAGE_SIZE);
+
+ pThis->cPages = (uint32_t)(cbNeeded / PAGE_SIZE) + 2; /* +2 for guard pages */
+
+ /*
+ * Try allocate the memory, using the best allocator by default and
+ * falling back on the less safe one.
+ */
+ rc = rtMemSaferSupR3AllocPages(pThis);
+ if (RT_SUCCESS(rc))
+ pThis->enmAllocator = RTMEMSAFERALLOCATOR_SUPR3;
+ else if (!(fFlags & RTMEMSAFER_F_REQUIRE_NOT_PAGABLE))
+ {
+ rc = rtMemSaferMemAllocPages(pThis);
+ if (RT_SUCCESS(rc))
+ pThis->enmAllocator = RTMEMSAFERALLOCATOR_RTMEMPAGE;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Insert the node.
+ */
+ *ppvNew = pThis->Core.Key;
+ rtMemSaferNodeInsert(pThis); /* (Scrambles Core.Key) */
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTMemSaferAllocZExTag);
+
+
+RTDECL(void) RTMemSaferFree(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ if (pv)
+ {
+ PRTMEMSAFERNODE pThis = rtMemSaferNodeRemove(pv);
+ AssertReturnVoid(pThis);
+ if (cb == 0) /* for openssl use */
+ cb = pThis->cbUser;
+ else
+ AssertMsg(cb == pThis->cbUser, ("cb=%#zx != %#zx\n", cb, pThis->cbUser));
+
+ /*
+ * Wipe the user memory first.
+ */
+ RTMemWipeThoroughly(pv, RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN), 3);
+
+ /*
+ * Free the pages.
+ */
+ uint8_t *pbPages = (uint8_t *)pv - pThis->offUser - PAGE_SIZE;
+ size_t cbPages = (size_t)pThis->cPages * PAGE_SIZE;
+ switch (pThis->enmAllocator)
+ {
+#ifdef IN_SUP_R3
+ case RTMEMSAFERALLOCATOR_SUPR3:
+ SUPR3PageProtect(pbPages, NIL_RTR0PTR, 0, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ SUPR3PageProtect(pbPages, NIL_RTR0PTR, (uint32_t)(cbPages - PAGE_SIZE), PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ SUPR3PageFreeEx(pbPages, pThis->cPages);
+ break;
+#endif
+ case RTMEMSAFERALLOCATOR_RTMEMPAGE:
+ RTMemProtect(pbPages, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ RTMemProtect(pbPages + cbPages - PAGE_SIZE, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ RTMemPageFree(pbPages, cbPages);
+ break;
+
+ default:
+ AssertFailed();
+ }
+
+ /*
+ * Free the tracking node.
+ */
+ pThis->Core.Key = NULL;
+ pThis->offUser = 0;
+ pThis->cbUser = 0;
+ RTMemFree(pThis);
+ }
+ else
+ Assert(cb == 0);
+}
+RT_EXPORT_SYMBOL(RTMemSaferFree);
+
+
+RTDECL(size_t) RTMemSaferGetSize(void *pv) RT_NO_THROW_DEF
+{
+ size_t cbRet = 0;
+ if (pv)
+ {
+ /*
+ * We use this API for testing whether pv is a safer allocation or not,
+ * so we may be called before the allocators. Thus, it's prudent to
+ * make sure initialization has taken place before attempting to enter
+ * the critical section and such.
+ */
+ int rc = RTOnceEx(&g_MemSaferOnce, rtMemSaferOnceInit, rtMemSaferOnceTerm, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ void *pvKey = rtMemSaferScramblePointer(pv);
+ RTCritSectRwEnterShared(&g_MemSaferCritSect);
+ PRTMEMSAFERNODE pThis = (PRTMEMSAFERNODE)RTAvlPVGet(&g_pMemSaferTree, pvKey);
+ if (pThis)
+ cbRet = pThis->cbUser;
+ RTCritSectRwLeaveShared(&g_MemSaferCritSect);
+ }
+ }
+ return cbRet;
+}
+RT_EXPORT_SYMBOL(RTMemSaferGetSize);
+
+
+/**
+ * The simplest reallocation method: allocate new block, copy over the data,
+ * free old block.
+ */
+static int rtMemSaferReallocSimpler(size_t cbOld, void *pvOld, size_t cbNew, void **ppvNew, uint32_t fFlags, const char *pszTag)
+{
+ void *pvNew;
+ int rc = RTMemSaferAllocZExTag(&pvNew, cbNew, fFlags, pszTag);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(pvNew, pvOld, RT_MIN(cbNew, cbOld));
+ RTMemSaferFree(pvOld, cbOld);
+ *ppvNew = pvNew;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTMemSaferReallocZExTag(size_t cbOld, void *pvOld, size_t cbNew, void **ppvNew, uint32_t fFlags, const char *pszTag) RT_NO_THROW_DEF
+{
+ int rc;
+ /* Real realloc. */
+ if (cbNew && cbOld)
+ {
+ PRTMEMSAFERNODE pThis = rtMemSaferNodeLookup(pvOld);
+ AssertReturn(pThis, VERR_INVALID_POINTER);
+ AssertMsgStmt(cbOld == pThis->cbUser, ("cbOld=%#zx != %#zx\n", cbOld, pThis->cbUser), cbOld = pThis->cbUser);
+
+ if (pThis->fFlags == fFlags)
+ {
+ if (cbNew > cbOld)
+ {
+ /*
+ * Is the enough room for us to grow?
+ */
+ size_t cbMax = (size_t)(pThis->cPages - 2) * PAGE_SIZE;
+ if (cbNew <= cbMax)
+ {
+ size_t const cbAdded = (cbNew - cbOld);
+ size_t const cbAfter = cbMax - pThis->offUser - cbOld;
+ if (cbAfter >= cbAdded)
+ {
+ /*
+ * Sufficient space after the current allocation.
+ */
+ uint8_t *pbNewSpace = (uint8_t *)pvOld + cbOld;
+ RT_BZERO(pbNewSpace, cbAdded);
+ *ppvNew = pvOld;
+ }
+ else
+ {
+ /*
+ * Have to move the allocation to make enough room at the
+ * end. In order to make it a little less predictable and
+ * maybe avoid a relocation or two in the next call, divide
+ * the page offset by four until it it fits.
+ */
+ AssertReturn(rtMemSaferNodeRemove(pvOld) == pThis, VERR_INTERNAL_ERROR_3);
+ uint32_t offNewUser = pThis->offUser;
+ do
+ offNewUser = offNewUser / 2;
+ while ((pThis->offUser - offNewUser) + cbAfter < cbAdded);
+ offNewUser &= ~(RTMEMSAFER_ALIGN - 1U);
+
+ uint32_t const cbMove = pThis->offUser - offNewUser;
+ uint8_t *pbNew = (uint8_t *)pvOld - cbMove;
+ memmove(pbNew, pvOld, cbOld);
+
+ RT_BZERO(pbNew + cbOld, cbAdded);
+ if (cbMove > cbAdded)
+ RTMemWipeThoroughly(pbNew + cbNew, cbMove - cbAdded, 3);
+
+ pThis->offUser = offNewUser;
+ pThis->Core.Key = pbNew;
+ *ppvNew = pbNew;
+
+ rtMemSaferNodeInsert(pThis);
+ }
+ Assert(((uintptr_t)*ppvNew & PAGE_OFFSET_MASK) == pThis->offUser);
+ pThis->cbUser = cbNew;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ /*
+ * Not enough space, allocate a new block and copy over the data.
+ */
+ rc = rtMemSaferReallocSimpler(cbOld, pvOld, cbNew, ppvNew, fFlags, pszTag);
+ }
+ }
+ else
+ {
+ /*
+ * Shrinking the allocation, just wipe the memory that is no longer
+ * being used.
+ */
+ if (cbNew != cbOld)
+ {
+ uint8_t *pbAbandond = (uint8_t *)pvOld + cbNew;
+ RTMemWipeThoroughly(pbAbandond, cbOld - cbNew, 3);
+ }
+ pThis->cbUser = cbNew;
+ *ppvNew = pvOld;
+ rc = VINF_SUCCESS;
+ }
+ }
+ else if (!pThis->fFlags)
+ {
+ /*
+ * New flags added. Allocate a new block and copy over the old one.
+ */
+ rc = rtMemSaferReallocSimpler(cbOld, pvOld, cbNew, ppvNew, fFlags, pszTag);
+ }
+ else
+ {
+ /* Compatible flags. */
+ AssertMsgFailed(("fFlags=%#x old=%#x\n", fFlags, pThis->fFlags));
+ rc = VERR_INVALID_FLAGS;
+ }
+ }
+ /*
+ * First allocation. Pass it on.
+ */
+ else if (!cbOld)
+ {
+ Assert(pvOld == NULL);
+ rc = RTMemSaferAllocZExTag(ppvNew, cbNew, fFlags, pszTag);
+ }
+ /*
+ * Free operation. Pass it on.
+ */
+ else
+ {
+ RTMemSaferFree(pvOld, cbOld);
+ *ppvNew = NULL;
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTMemSaferReallocZExTag);
+
+
+RTDECL(void *) RTMemSaferAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ void *pvNew = NULL;
+ int rc = RTMemSaferAllocZExTag(&pvNew, cb, 0 /*fFlags*/, pszTag);
+ if (RT_SUCCESS(rc))
+ return pvNew;
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTMemSaferAllocZTag);
+
+
+RTDECL(void *) RTMemSaferReallocZTag(size_t cbOld, void *pvOld, size_t cbNew, const char *pszTag) RT_NO_THROW_DEF
+{
+ void *pvNew = NULL;
+ int rc = RTMemSaferReallocZExTag(cbOld, pvOld, cbNew, &pvNew, 0 /*fFlags*/, pszTag);
+ if (RT_SUCCESS(rc))
+ return pvNew;
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTMemSaferReallocZTag);
+
diff --git a/src/VBox/Runtime/r3/netbsd/Makefile.kup b/src/VBox/Runtime/r3/netbsd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/netbsd/Makefile.kup
diff --git a/src/VBox/Runtime/r3/netbsd/rtProcInitExePath-netbsd.cpp b/src/VBox/Runtime/r3/netbsd/rtProcInitExePath-netbsd.cpp
new file mode 100644
index 00000000..90566b7a
--- /dev/null
+++ b/src/VBox/Runtime/r3/netbsd/rtProcInitExePath-netbsd.cpp
@@ -0,0 +1,110 @@
+/* $Id: rtProcInitExePath-netbsd.cpp $ */
+/** @file
+ * IPRT - rtProcInitName, NetBSD.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <link.h>
+
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include "internal/process.h"
+#include "internal/path.h"
+
+
+DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath)
+{
+ /*
+ * Read the /proc/curproc/file link, convert to native and return it.
+ */
+ int cchLink = readlink("/proc/curproc/exe", pszPath, cchPath - 1);
+ if (cchLink > 0 && (size_t)cchLink <= cchPath - 1)
+ {
+ pszPath[cchLink] = '\0';
+
+ char const *pszTmp;
+ int rc = rtPathFromNative(&pszTmp, pszPath, NULL);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, cchLink, pszPath), rc);
+ if (pszTmp != pszPath)
+ {
+ rc = RTStrCopy(pszPath, cchPath, pszTmp);
+ rtPathFreeIprt(pszTmp, pszPath);
+ }
+ return rc;
+ }
+
+ int err = errno;
+
+ /*
+ * Fall back on the dynamic linker since /proc is optional.
+ */
+ void *hExe = dlopen(NULL, 0);
+ if (hExe)
+ {
+ struct link_map const *pLinkMap = 0;
+ if (dlinfo(hExe, RTLD_DI_LINKMAP, &pLinkMap) == 0)
+ {
+ const char *pszImageName = pLinkMap->l_name;
+ if (*pszImageName == '/') /* this may not always be absolute, despite the docs. :-( */
+ {
+ char const *pszTmp;
+ int rc = rtPathFromNative(&pszTmp, pszImageName, NULL);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszImageName=\"%s\"\n", rc, pszImageName), rc);
+ if (pszTmp != pszPath)
+ {
+ rc = RTStrCopy(pszPath, cchPath, pszTmp);
+ rtPathFreeIprt(pszTmp, pszPath);
+ }
+ return rc;
+ }
+ /** @todo Try search the PATH for the file name or append the current
+ * directory, which ever makes sense... */
+ }
+ }
+
+ int rc = RTErrConvertFromErrno(err);
+ AssertMsgFailed(("rc=%Rrc err=%d cchLink=%d hExe=%p\n", rc, err, cchLink, hExe));
+ return rc;
+}
diff --git a/src/VBox/Runtime/r3/nocrt-cerr.cpp b/src/VBox/Runtime/r3/nocrt-cerr.cpp
new file mode 100644
index 00000000..71c294f8
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-cerr.cpp
@@ -0,0 +1,52 @@
+/* $Id: nocrt-cerr.cpp $ */
+/** @file
+ * IPRT - No-CRT - std::cerr.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+#include <iprt/nocrt/iostream>
+#include <iprt/nocrt/fstream>
+#include <iprt/stream.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+std::basic_filebuf<char, std::char_traits<char> > g_rtNoCrtCerrBuf(g_pStdErr, true /*a_fStdStream*/);
+std::ostream std::cerr(&g_rtNoCrtCerrBuf, &std::cout /*a_pTiedStream*/, true /*a_fUnbuffered*/);
+
diff --git a/src/VBox/Runtime/r3/nocrt-clearerr.cpp b/src/VBox/Runtime/r3/nocrt-clearerr.cpp
new file mode 100644
index 00000000..ecd3ce69
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-clearerr.cpp
@@ -0,0 +1,53 @@
+/* $Id: nocrt-clearerr.cpp $ */
+/** @file
+ * IPRT - No-CRT - clearerr().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/stream.h>
+
+
+#undef clearerr
+void RT_NOCRT(clearerr)(FILE *pFile)
+{
+ RTStrmClearError(pFile);
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(clearerr);
+
diff --git a/src/VBox/Runtime/r3/nocrt-cout.cpp b/src/VBox/Runtime/r3/nocrt-cout.cpp
new file mode 100644
index 00000000..5323359f
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-cout.cpp
@@ -0,0 +1,52 @@
+/* $Id: nocrt-cout.cpp $ */
+/** @file
+ * IPRT - No-CRT - std::cout.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+#include <iprt/nocrt/iostream>
+#include <iprt/nocrt/fstream>
+#include <iprt/stream.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+std::basic_filebuf<char, std::char_traits<char> > g_rtNoCrtCoutBuf(g_pStdOut, true /*a_fStdStream*/);
+std::ostream std::cout(&g_rtNoCrtCoutBuf, NULL /*a_pTiedStream*/, false /*a_fUnbuffered*/);
+
diff --git a/src/VBox/Runtime/r3/nocrt-errno.cpp b/src/VBox/Runtime/r3/nocrt-errno.cpp
new file mode 100644
index 00000000..7a931a0f
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-errno.cpp
@@ -0,0 +1,55 @@
+/* $Id: nocrt-errno.cpp $ */
+/** @file
+ * IPRT - No-CRT - Per-thread errno variable.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/errno.h>
+
+
+RTDECL(int *) rtNoCrtGetErrnoPtr(void)
+{
+ PRTNOCRTTHREADDATA pNoCrtData = rtNoCrtThreadDataGet();
+ if (pNoCrtData)
+ return &pNoCrtData->iErrno;
+
+ static int s_iFallbackErrno;
+ return &s_iFallbackErrno;
+}
+
diff --git a/src/VBox/Runtime/r3/nocrt-fclose.cpp b/src/VBox/Runtime/r3/nocrt-fclose.cpp
new file mode 100644
index 00000000..8cd90a79
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fclose.cpp
@@ -0,0 +1,59 @@
+/* $Id: nocrt-fclose.cpp $ */
+/** @file
+ * IPRT - No-CRT - fclose().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef fclose
+int RT_NOCRT(fclose)(FILE *pFile)
+{
+ int rc = RTStrmClose(pFile);
+ if (RT_SUCCESS(rc))
+ return 0;
+ errno = RTErrConvertToErrno(rc);
+ return -1;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fclose);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fdopen.cpp b/src/VBox/Runtime/r3/nocrt-fdopen.cpp
new file mode 100644
index 00000000..8ecef72f
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fdopen.cpp
@@ -0,0 +1,66 @@
+/* $Id: nocrt-fdopen.cpp $ */
+/** @file
+ * IPRT - No-CRT - fdopen().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+#include <iprt/stream.h>
+
+
+#undef fdopen
+FILE *RT_NOCRT(fdopen)(int fd, const char *pszMode)
+{
+ RTFILE hFile = NIL_RTFILE;
+ int rc = RTFileFromNative(&hFile, fd);
+ if (RT_SUCCESS(rc))
+ {
+ PRTSTREAM pStrm = NULL;
+ rc = RTStrmOpenFileHandle(hFile, pszMode, 0, &pStrm);
+ if (RT_SUCCESS(rc))
+ return pStrm;
+ }
+ errno = RTErrConvertToErrno(rc);
+ return NULL;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fdopen);
+
diff --git a/src/VBox/Runtime/r3/nocrt-ferror.cpp b/src/VBox/Runtime/r3/nocrt-ferror.cpp
new file mode 100644
index 00000000..b566cd87
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-ferror.cpp
@@ -0,0 +1,57 @@
+/* $Id: nocrt-ferror.cpp $ */
+/** @file
+ * IPRT - No-CRT - ferror().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef ferror
+int RT_NOCRT(ferror)(FILE *pFile)
+{
+ int rc = RTStrmError(pFile);
+ if (RT_SUCCESS(rc))
+ return 0;
+ return 1;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(ferror);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fflush.cpp b/src/VBox/Runtime/r3/nocrt-fflush.cpp
new file mode 100644
index 00000000..2535da4b
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fflush.cpp
@@ -0,0 +1,59 @@
+/* $Id: nocrt-fflush.cpp $ */
+/** @file
+ * IPRT - No-CRT - fflush().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef fflush
+int RT_NOCRT(fflush)(FILE *pFile)
+{
+ int rc = RTStrmFlush(pFile);
+ if (RT_SUCCESS(rc))
+ return 0;
+ errno = RTErrConvertToErrno(rc);
+ return -1;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fflush);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fgetc.cpp b/src/VBox/Runtime/r3/nocrt-fgetc.cpp
new file mode 100644
index 00000000..cbb0cfdc
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fgetc.cpp
@@ -0,0 +1,61 @@
+/* $Id: nocrt-fgetc.cpp $ */
+/** @file
+ * IPRT - No-CRT - fgetc().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+/* Note! This is identical to the getc implemention. */
+#undef fgetc
+int RT_NOCRT(fgetc)(FILE *pFile)
+{
+ unsigned char ch;
+ int rc = RTStrmReadEx(pFile, &ch, 1, NULL);
+ if (RT_SUCCESS(rc))
+ return ch;
+ errno = RTErrConvertToErrno(rc);
+ return RT_NOCRT_EOF;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fgetc);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fileno.cpp b/src/VBox/Runtime/r3/nocrt-fileno.cpp
new file mode 100644
index 00000000..506606a5
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fileno.cpp
@@ -0,0 +1,61 @@
+/* $Id: nocrt-fileno.cpp $ */
+/** @file
+ * IPRT - No-CRT - fileno().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/file.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef fileno
+int RT_NOCRT(fileno)(FILE *pFile)
+{
+ RTFILE hFile;
+ int rc = RTStrmQueryFileHandle(pFile, &hFile);
+ if (RT_SUCCESS(rc))
+ return (int)RTFileToNative(hFile);
+ errno = EINVAL;
+ return -1;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fileno);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fopen.cpp b/src/VBox/Runtime/r3/nocrt-fopen.cpp
new file mode 100644
index 00000000..3231a765
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fopen.cpp
@@ -0,0 +1,60 @@
+/* $Id: nocrt-fopen.cpp $ */
+/** @file
+ * IPRT - No-CRT - fopen().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef fopen
+FILE *RT_NOCRT(fopen)(const char *pszFilename, const char *pszMode)
+{
+ PRTSTREAM pStrm = NULL;
+ int rc = RTStrmOpen(pszFilename, pszMode, &pStrm);
+ if (RT_SUCCESS(rc))
+ return pStrm;
+ errno = RTErrConvertToErrno(rc);
+ return NULL;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fopen);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fputc.cpp b/src/VBox/Runtime/r3/nocrt-fputc.cpp
new file mode 100644
index 00000000..5e3e57c6
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fputc.cpp
@@ -0,0 +1,61 @@
+/* $Id: nocrt-fputc.cpp $ */
+/** @file
+ * IPRT - No-CRT - fputc().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+/* Note! This is identical to the putc implemention. */
+#undef fputc
+int RT_NOCRT(fputc)(int iChar, FILE *pFile)
+{
+ unsigned char ch = (unsigned char)iChar;
+ int rc = RTStrmWriteEx(pFile, &ch, 1, NULL);
+ if (RT_SUCCESS(rc))
+ return ch;
+ errno = RTErrConvertToErrno(rc);
+ return RT_NOCRT_EOF;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fputc);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fputs.cpp b/src/VBox/Runtime/r3/nocrt-fputs.cpp
new file mode 100644
index 00000000..64656496
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fputs.cpp
@@ -0,0 +1,63 @@
+/* $Id: nocrt-fputs.cpp $ */
+/** @file
+ * IPRT - No-CRT - fputs().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/nocrt/limits.h>
+#include <iprt/nocrt/string.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef fputs
+int RT_NOCRT(fputs)(const char *psz, FILE *pFile)
+{
+ /* We're using RTStrmPutStr here for the benefit of console output optimization. */
+ size_t cch = strlen(psz);
+ int rc = RTStrmPutStr(pFile, psz);
+ if (RT_SUCCESS(rc))
+ return (int)RT_MIN(cch, (size_t)INT_MAX);
+ errno = RTErrConvertToErrno(rc);
+ return RT_NOCRT_EOF;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fputs);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fread.cpp b/src/VBox/Runtime/r3/nocrt-fread.cpp
new file mode 100644
index 00000000..9c0e8b47
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fread.cpp
@@ -0,0 +1,63 @@
+/* $Id: nocrt-fread.cpp $ */
+/** @file
+ * IPRT - No-CRT - fread().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef fread
+size_t RT_NOCRT(fread)(void *pvBuf, size_t cbItem, size_t cItems, FILE *pFile)
+{
+ size_t cbToRead = cbItem * cItems;
+ AssertReturnStmt(cbToRead >= cbItem && cbItem >= cItems, errno = ERANGE, 0);
+ size_t cbRead = 0;
+ int rc = RTStrmReadEx(pFile, pvBuf, cbToRead, &cbRead);
+ if (RT_SUCCESS(rc))
+ return cbRead;
+ errno = RTErrConvertToErrno(rc);
+ return 0;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fread);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fseek.cpp b/src/VBox/Runtime/r3/nocrt-fseek.cpp
new file mode 100644
index 00000000..2548c926
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fseek.cpp
@@ -0,0 +1,59 @@
+/* $Id: nocrt-fseek.cpp $ */
+/** @file
+ * IPRT - No-CRT - fseek().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef fseek
+int RT_NOCRT(fseek)(FILE *pFile, long off, int iMethod)
+{
+ int rc = RTStrmSeek(pFile, off, (uint32_t)iMethod);
+ if (RT_SUCCESS(rc))
+ return 0;
+ errno = RTErrConvertToErrno(rc);
+ return -1;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fseek);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fseeko.cpp b/src/VBox/Runtime/r3/nocrt-fseeko.cpp
new file mode 100644
index 00000000..bdeb6a9e
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fseeko.cpp
@@ -0,0 +1,59 @@
+/* $Id: nocrt-fseeko.cpp $ */
+/** @file
+ * IPRT - No-CRT - fseeko().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef fseeko
+int RT_NOCRT(fseeko)(FILE *pFile, off_t off, int iMethod)
+{
+ int rc = RTStrmSeek(pFile, off, (uint32_t)iMethod);
+ if (RT_SUCCESS(rc))
+ return 0;
+ errno = RTErrConvertToErrno(rc);
+ return -1;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fseeko);
+
diff --git a/src/VBox/Runtime/r3/nocrt-ftell.cpp b/src/VBox/Runtime/r3/nocrt-ftell.cpp
new file mode 100644
index 00000000..c8acc950
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-ftell.cpp
@@ -0,0 +1,65 @@
+/* $Id: nocrt-ftell.cpp $ */
+/** @file
+ * IPRT - No-CRT - ftell().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/nocrt/limits.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef ftell
+long RT_NOCRT(ftell)(FILE *pFile)
+{
+ RTFOFF off = RTStrmTell(pFile);
+ if (off >= 0)
+ {
+ if (off < LONG_MAX)
+ return (long)off;
+ errno = EOVERFLOW;
+ }
+ else
+ errno = RTErrConvertToErrno((int)off);
+ return -1;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(ftell);
+
diff --git a/src/VBox/Runtime/r3/nocrt-ftello.cpp b/src/VBox/Runtime/r3/nocrt-ftello.cpp
new file mode 100644
index 00000000..0cf2b6d8
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-ftello.cpp
@@ -0,0 +1,60 @@
+/* $Id: nocrt-ftello.cpp $ */
+/** @file
+ * IPRT - No-CRT - ftello().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef ftello
+off_t RT_NOCRT(ftello)(FILE *pFile)
+{
+ RTFOFF off = RTStrmTell(pFile);
+ AssertCompile(sizeof(off_t) == sizeof(off));
+ if (off >= 0)
+ return off;
+ errno = RTErrConvertToErrno((int)off);
+ return -1;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(ftello);
+
diff --git a/src/VBox/Runtime/r3/nocrt-fwrite.cpp b/src/VBox/Runtime/r3/nocrt-fwrite.cpp
new file mode 100644
index 00000000..05d108d2
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-fwrite.cpp
@@ -0,0 +1,63 @@
+/* $Id: nocrt-fwrite.cpp $ */
+/** @file
+ * IPRT - No-CRT - fwrite().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef fwrite
+size_t RT_NOCRT(fwrite)(void const *pvBuf, size_t cbItem, size_t cItems, FILE *pFile)
+{
+ size_t cbToWrite = cbItem * cItems;
+ AssertReturnStmt(cbToWrite >= cbItem && cbItem >= cItems, errno = ERANGE, 0);
+ size_t cbWritten = 0;
+ int rc = RTStrmWriteEx(pFile, pvBuf, cbToWrite, &cbWritten);
+ if (RT_SUCCESS(rc))
+ return cbWritten;
+ errno = RTErrConvertToErrno(rc);
+ return 0;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(fwrite);
+
diff --git a/src/VBox/Runtime/r3/nocrt-getc.cpp b/src/VBox/Runtime/r3/nocrt-getc.cpp
new file mode 100644
index 00000000..4f94cbdb
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-getc.cpp
@@ -0,0 +1,61 @@
+/* $Id: nocrt-getc.cpp $ */
+/** @file
+ * IPRT - No-CRT - getc().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+/* Note! This is identical to the fgetc implemention. */
+#undef getc
+int RT_NOCRT(getc)(FILE *pFile)
+{
+ unsigned char ch;
+ int rc = RTStrmReadEx(pFile, &ch, 1, NULL);
+ if (RT_SUCCESS(rc))
+ return ch;
+ errno = RTErrConvertToErrno(rc);
+ return RT_NOCRT_EOF;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(getc);
+
diff --git a/src/VBox/Runtime/r3/nocrt-per-thread-1.cpp b/src/VBox/Runtime/r3/nocrt-per-thread-1.cpp
new file mode 100644
index 00000000..52bccab8
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-per-thread-1.cpp
@@ -0,0 +1,58 @@
+/* $Id: nocrt-per-thread-1.cpp $ */
+/** @file
+ * IPRT - No-Crt - Per Thread Data, TLS index.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The TLS index for the no-CRT per thread data. */
+RTTLS volatile g_iTlsRtNoCrtPerThread = NIL_RTTLS;
+
+/** The TLS entry of an IPRT thread points to this during the cleanup (paranoia). */
+RTNOCRTTHREADDATA g_RtNoCrtPerThreadDummy =
+{
+ { NULL, NULL },
+ RTNOCRTTHREADDATA::kAllocType_CleanupDummy,
+ 0,
+ NULL
+};
+
diff --git a/src/VBox/Runtime/r3/nocrt-per-thread-2.cpp b/src/VBox/Runtime/r3/nocrt-per-thread-2.cpp
new file mode 100644
index 00000000..9179d33a
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-per-thread-2.cpp
@@ -0,0 +1,238 @@
+/* $Id: nocrt-per-thread-2.cpp $ */
+/** @file
+ * IPRT - No-Crt - Per Thread Data, Managment code
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+#include <iprt/asm.h>
+#include <iprt/critsect.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/thread.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Init/term once state. */
+static RTONCE g_NoCrtPerThreadOnce = RTONCE_INITIALIZER;
+
+/** List of heap allocations (PRTNOCRTTHREADDATA). */
+static RTLISTANCHOR g_NoCrtPerThreadHeapList;
+/** Critical section protecting g_NoCrtPerThreadHeapList. */
+static RTCRITSECT g_NoCrtPerThreadCritSect;
+
+/** Allocation bitmap for g_aNoCrtPerThreadStatic.
+ *
+ * In debug builds we only have one slot here, so we have a better chance of
+ * testing the heap code path. */
+#ifdef DEBUG
+static uint32_t volatile g_fNoCrtPerThreadStaticAlloc = UINT32_C(0xffffefff);
+#else
+static uint32_t volatile g_fNoCrtPerThreadStaticAlloc = 0;
+#endif
+/* Static allocations to avoid the heap and associate slowness. */
+static RTNOCRTTHREADDATA g_aNoCrtPerThreadStatic[32];
+
+
+/**
+ * @callback_method_impl{FNRTTLSDTOR}
+ */
+static DECLCALLBACK(void) rtNoCrtPerThreadDtor(void *pvValue)
+{
+ PRTNOCRTTHREADDATA pNoCrtData = (PRTNOCRTTHREADDATA)pvValue;
+ if (pNoCrtData->enmAllocType == RTNOCRTTHREADDATA::kAllocType_Heap)
+ {
+ AssertReturnVoid(RTOnceWasInitialized(&g_NoCrtPerThreadOnce));
+
+ RTCritSectEnter(&g_NoCrtPerThreadCritSect); /* timeout? */
+
+ RTListNodeRemove(&pNoCrtData->ListEntry);
+ pNoCrtData->enmAllocType = RTNOCRTTHREADDATA::kAllocType_End;
+
+ RTCritSectLeave(&g_NoCrtPerThreadCritSect);
+
+ RTMemFree(pNoCrtData);
+ }
+ else if (pNoCrtData->enmAllocType == RTNOCRTTHREADDATA::kAllocType_Static)
+ {
+ size_t iSlot = (size_t)(pNoCrtData - &g_aNoCrtPerThreadStatic[0]);
+ AssertReturnVoid(iSlot < RT_ELEMENTS(g_aNoCrtPerThreadStatic));
+
+ pNoCrtData->enmAllocType = RTNOCRTTHREADDATA::kAllocType_Invalid;
+ ASMAtomicAndU32(&g_fNoCrtPerThreadStaticAlloc, ~(uint32_t)iSlot);
+ }
+}
+
+
+/**
+ * @callback_method_impl{FNRTONCE}
+ */
+static DECLCALLBACK(int32_t) rtNoCrtPerThreadInit(void *pvUser)
+{
+ RTListInit(&g_NoCrtPerThreadHeapList);
+
+ RTTLS iTls = NIL_RTTLS;
+ int rc = RTTlsAllocEx(&iTls, rtNoCrtPerThreadDtor);
+ if (iTls != NIL_RTTLS)
+ {
+ rc = RTCritSectInit(&g_NoCrtPerThreadCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ g_iTlsRtNoCrtPerThread = iTls;
+ return VINF_SUCCESS;
+ }
+ RTTlsFree(iTls);
+ }
+ RT_NOREF(pvUser);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNRTONCECLEANUP}
+ */
+static DECLCALLBACK(void) rtNoCrtPerThreadCleanup(void *pvUser, bool fLazyCleanUpOk)
+{
+ RT_NOREF(pvUser);
+ if (fLazyCleanUpOk)
+ return;
+
+ /*
+ * First destroy the TLS entry.
+ */
+ RTTLS iTls = g_iTlsRtNoCrtPerThread;
+ g_iTlsRtNoCrtPerThread = NIL_RTTLS;
+ int rc = RTTlsFree(iTls);
+ AssertRC(rc);
+
+ /*
+ * Then destroy the critical section and free all entries in the list.
+ */
+ RTCritSectDelete(&g_NoCrtPerThreadCritSect);
+
+ PRTNOCRTTHREADDATA pNoCrtData;
+ while ((pNoCrtData = RTListRemoveFirst(&g_NoCrtPerThreadHeapList, RTNOCRTTHREADDATA, ListEntry)) != NULL)
+ {
+ AssertContinue(pNoCrtData->enmAllocType == RTNOCRTTHREADDATA::kAllocType_Heap);
+ pNoCrtData->enmAllocType = RTNOCRTTHREADDATA::kAllocType_End;
+ RTMemFree(pNoCrtData);
+ }
+}
+
+PRTNOCRTTHREADDATA rtNoCrtThreadDataGet(void)
+{
+ int rc = RTOnceEx(&g_NoCrtPerThreadOnce, rtNoCrtPerThreadInit, rtNoCrtPerThreadCleanup, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We typically have an entry already.
+ */
+ PRTNOCRTTHREADDATA pNoCrtData = (PRTNOCRTTHREADDATA)RTTlsGet(g_iTlsRtNoCrtPerThread);
+ if (pNoCrtData)
+ {
+ AssertReturn( pNoCrtData->enmAllocType > RTNOCRTTHREADDATA::kAllocType_Invalid
+ && pNoCrtData->enmAllocType < RTNOCRTTHREADDATA::kAllocType_End,
+ NULL);
+ return pNoCrtData;
+ }
+
+ /*
+ * Okay, allocate a new entry first using some of the statically allocated
+ * ones then falling back on heap allocations.
+ */
+ for (;;)
+ {
+ uint32_t const fAlloc = ASMAtomicUoReadU32(&g_fNoCrtPerThreadStaticAlloc);
+ uint32_t iSlot = ASMBitFirstSetU32(~fAlloc);
+ if (iSlot != 0)
+ iSlot--;
+ else
+ break;
+ if (ASMAtomicCmpXchgU32(&g_fNoCrtPerThreadStaticAlloc, fAlloc | RT_BIT_32(iSlot), fAlloc))
+ {
+ pNoCrtData = &g_aNoCrtPerThreadStatic[iSlot];
+
+ /* Init the entry in case it's being re-used: */
+ Assert(pNoCrtData->enmAllocType == RTNOCRTTHREADDATA::kAllocType_Invalid);
+ rc = RTTlsSet(g_iTlsRtNoCrtPerThread, pNoCrtData);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ pNoCrtData->enmAllocType = RTNOCRTTHREADDATA::kAllocType_Static;
+ RTListInit(&pNoCrtData->ListEntry);
+ pNoCrtData->iErrno = 0;
+ pNoCrtData->pszStrToken = NULL;
+ return pNoCrtData;
+ }
+
+ ASMAtomicOrU32(&g_fNoCrtPerThreadStaticAlloc, RT_BIT_32(iSlot));
+ return NULL;
+ }
+ ASMNopPause();
+ }
+
+ /*
+ * Heap.
+ */
+ pNoCrtData = (PRTNOCRTTHREADDATA)RTMemAllocZ(sizeof(*pNoCrtData));
+ if (pNoCrtData)
+ {
+ pNoCrtData->enmAllocType = RTNOCRTTHREADDATA::kAllocType_Heap;
+
+ RTCritSectEnter(&g_NoCrtPerThreadCritSect);
+ RTListAppend(&g_NoCrtPerThreadHeapList, &pNoCrtData->ListEntry);
+ RTCritSectLeave(&g_NoCrtPerThreadCritSect);
+
+ rc = RTTlsSet(g_iTlsRtNoCrtPerThread, pNoCrtData);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ return pNoCrtData;
+
+ RTCritSectEnter(&g_NoCrtPerThreadCritSect);
+ RTListNodeRemove(&pNoCrtData->ListEntry);
+ RTCritSectLeave(&g_NoCrtPerThreadCritSect);
+
+ pNoCrtData->enmAllocType = RTNOCRTTHREADDATA::kAllocType_End;
+ RTMemFree(pNoCrtData);
+ }
+ }
+ return NULL;
+}
+
diff --git a/src/VBox/Runtime/r3/nocrt-putc.cpp b/src/VBox/Runtime/r3/nocrt-putc.cpp
new file mode 100644
index 00000000..8f334057
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-putc.cpp
@@ -0,0 +1,61 @@
+/* $Id: nocrt-putc.cpp $ */
+/** @file
+ * IPRT - No-CRT - putc().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+/* Note! This is identical to the fputc implemention. */
+#undef putc
+int RT_NOCRT(putc)(int iChar, FILE *pFile)
+{
+ unsigned char ch = (unsigned char)iChar;
+ int rc = RTStrmWriteEx(pFile, &ch, 1, NULL);
+ if (RT_SUCCESS(rc))
+ return ch;
+ errno = RTErrConvertToErrno(rc);
+ return RT_NOCRT_EOF;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(putc);
+
diff --git a/src/VBox/Runtime/r3/nocrt-puts.cpp b/src/VBox/Runtime/r3/nocrt-puts.cpp
new file mode 100644
index 00000000..11f90e52
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-puts.cpp
@@ -0,0 +1,68 @@
+/* $Id: nocrt-puts.cpp $ */
+/** @file
+ * IPRT - No-CRT - puts().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/nocrt/string.h>
+#include <iprt/nocrt/limits.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef puts
+int RT_NOCRT(puts)(const char *psz)
+{
+ /* We're using RTStrmPutStr here for the benefit of console output optimization. */
+ size_t cch = strlen(psz);
+ int rc = RTStrmPutStr(g_pStdOut, psz);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo This isn't entirely perfect wrt multithreading. */
+ rc = RTStrmPutStr(g_pStdOut, "\n");
+ if (RT_SUCCESS(rc))
+ return (int)RT_MIN(cch + 1, (size_t)INT_MAX);
+ }
+ errno = RTErrConvertToErrno(rc);
+ return RT_NOCRT_EOF;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(puts);
+
diff --git a/src/VBox/Runtime/r3/nocrt-setvbuf.cpp b/src/VBox/Runtime/r3/nocrt-setvbuf.cpp
new file mode 100644
index 00000000..76e0e46c
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-setvbuf.cpp
@@ -0,0 +1,72 @@
+/* $Id: nocrt-setvbuf.cpp $ */
+/** @file
+ * IPRT - No-CRT - setvbuf().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/stream.h>
+
+
+#undef setvbuf
+int RT_NOCRT(setvbuf)(FILE *pFile, char *pchBuf, int iBufferingType, size_t cbBuf)
+{
+ Assert(!pchBuf); RT_NOREF(pchBuf); /* ignored */
+ Assert(!cbBuf); RT_NOREF(cbBuf); /* ignored */
+
+ RTSTRMBUFMODE enmBufMode;
+ switch (iBufferingType)
+ {
+ case _IOFBF: enmBufMode = RTSTRMBUFMODE_FULL; break;
+ case _IOLBF: enmBufMode = RTSTRMBUFMODE_LINE; break;
+ case _IONBF: enmBufMode = RTSTRMBUFMODE_UNBUFFERED; break;
+ default: AssertFailedReturnStmt(errno = EINVAL, -1);
+ }
+
+ int rc = RTStrmSetBufferingMode(pFile, enmBufMode);
+ if (RT_SUCCESS(rc))
+ return 0;
+ errno = RTErrConvertToErrno(rc);
+ return -1;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(setvbuf);
+
diff --git a/src/VBox/Runtime/r3/nocrt-tmpfile.cpp b/src/VBox/Runtime/r3/nocrt-tmpfile.cpp
new file mode 100644
index 00000000..f8064326
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-tmpfile.cpp
@@ -0,0 +1,59 @@
+/* $Id: nocrt-tmpfile.cpp $ */
+/** @file
+ * IPRT - No-CRT - tmpfile().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+
+
+#undef tmpfile
+FILE *RT_NOCRT(tmpfile)(void)
+{
+ FILE *pFile = NULL;
+ errno_t rc = tmpfile_s(&pFile);
+ if (rc == 0)
+ return pFile;
+ errno = rc;
+ return NULL;
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(tmpfile);
+
diff --git a/src/VBox/Runtime/r3/nocrt-tmpfile_s.cpp b/src/VBox/Runtime/r3/nocrt-tmpfile_s.cpp
new file mode 100644
index 00000000..dad6ee7a
--- /dev/null
+++ b/src/VBox/Runtime/r3/nocrt-tmpfile_s.cpp
@@ -0,0 +1,72 @@
+/* $Id: nocrt-tmpfile_s.cpp $ */
+/** @file
+ * IPRT - No-CRT - tmpfile_s().
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IPRT_NO_CRT_FOR_3RD_PARTY
+#include "internal/nocrt.h"
+#include <iprt/nocrt/stdio.h>
+#include <iprt/nocrt/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+
+
+#undef tmpfile_s
+errno_t RT_NOCRT(tmpfile_s)(FILE **ppFile)
+{
+ char szPath[RTPATH_MAX]; /* The file is inaccessible by this name, it's just for RTFileOpenTemp's use really. */
+ RTFILE hFile = NIL_RTFILE;
+ int rc = RTFileOpenTemp(&hFile, szPath, sizeof(szPath),
+ RTFILE_O_CREATE | RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_TEMP_AUTO_DELETE);
+ if (RT_SUCCESS(rc))
+ {
+ PRTSTREAM pFile = NULL;
+ rc = RTStrmOpenFileHandle(hFile, "w+b", 0 /*fFlags*/, &pFile);
+ if (RT_SUCCESS(rc))
+ {
+ *ppFile = pFile;
+ return 0;
+ }
+ RTFileClose(hFile);
+ }
+ return RTErrConvertToErrno(rc);
+}
+RT_ALIAS_AND_EXPORT_NOCRT_SYMBOL(tmpfile_s);
+
diff --git a/src/VBox/Runtime/r3/nt/Makefile.kup b/src/VBox/Runtime/r3/nt/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/Makefile.kup
diff --git a/src/VBox/Runtime/r3/nt/RTFileQueryFsSizes-nt.cpp b/src/VBox/Runtime/r3/nt/RTFileQueryFsSizes-nt.cpp
new file mode 100644
index 00000000..63a88211
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/RTFileQueryFsSizes-nt.cpp
@@ -0,0 +1,102 @@
+/* $Id: RTFileQueryFsSizes-nt.cpp $ */
+/** @file
+ * IPRT - RTFileQueryFsSizes, Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include "internal-r3-nt.h"
+
+#include <iprt/file.h>
+#include <iprt/errcore.h>
+
+
+
+RTR3DECL(int) RTFileQueryFsSizes(RTFILE hFile, PRTFOFF pcbTotal, RTFOFF *pcbFree,
+ uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ int rc;
+
+ /*
+ * Get the volume information.
+ */
+ FILE_FS_SIZE_INFORMATION FsSizeInfo;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile((HANDLE)RTFileToNative(hFile), &Ios,
+ &FsSizeInfo, sizeof(FsSizeInfo), FileFsSizeInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Calculate the return values.
+ */
+ if (pcbTotal)
+ {
+ *pcbTotal = FsSizeInfo.TotalAllocationUnits.QuadPart
+ * FsSizeInfo.SectorsPerAllocationUnit
+ * FsSizeInfo.BytesPerSector;
+ if ( *pcbTotal / FsSizeInfo.SectorsPerAllocationUnit / FsSizeInfo.BytesPerSector
+ != FsSizeInfo.TotalAllocationUnits.QuadPart)
+ *pcbTotal = UINT64_MAX;
+ }
+
+ if (pcbFree)
+ {
+ *pcbFree = FsSizeInfo.AvailableAllocationUnits.QuadPart
+ * FsSizeInfo.SectorsPerAllocationUnit
+ * FsSizeInfo.BytesPerSector;
+ if ( *pcbFree / FsSizeInfo.SectorsPerAllocationUnit / FsSizeInfo.BytesPerSector
+ != FsSizeInfo.AvailableAllocationUnits.QuadPart)
+ *pcbFree = UINT64_MAX;
+ }
+
+ rc = VINF_SUCCESS;
+ if (pcbBlock)
+ {
+ *pcbBlock = FsSizeInfo.SectorsPerAllocationUnit * FsSizeInfo.BytesPerSector;
+ if (*pcbBlock / FsSizeInfo.BytesPerSector != FsSizeInfo.SectorsPerAllocationUnit)
+ rc = VERR_OUT_OF_RANGE;
+ }
+
+ if (pcbSector)
+ *pcbSector = FsSizeInfo.BytesPerSector;
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/nt/RTFileSetMode-r3-nt.cpp b/src/VBox/Runtime/r3/nt/RTFileSetMode-r3-nt.cpp
new file mode 100644
index 00000000..2ec31d14
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/RTFileSetMode-r3-nt.cpp
@@ -0,0 +1,92 @@
+/* $Id: RTFileSetMode-r3-nt.cpp $ */
+/** @file
+ * IPRT - RTFileSetMode, Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include "internal-r3-nt.h"
+
+#include <iprt/file.h>
+#include <iprt/err.h>
+
+#include "internal/fs.h"
+
+
+/**
+ * Common worker for RTFileSetMode, RTPathSetMode and RTDirRelPathSetMode.
+ *
+ * @returns IPRT status code.
+ * @param hNativeFile The NT handle to the file system object.
+ * @param fMode Valid and normalized file mode mask to set.
+ */
+DECLHIDDEN(int) rtNtFileSetModeWorker(HANDLE hNativeFile, RTFMODE fMode)
+{
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ FILE_BASIC_INFORMATION BasicInfo;
+ BasicInfo.CreationTime.QuadPart = 0;
+ BasicInfo.ChangeTime.QuadPart = 0;
+ BasicInfo.LastAccessTime.QuadPart = 0;
+ BasicInfo.LastWriteTime.QuadPart = 0;
+ BasicInfo.FileAttributes = (fMode & ~( RTFS_DOS_NT_ENCRYPTED
+ | RTFS_DOS_NT_COMPRESSED
+ | RTFS_DOS_NT_REPARSE_POINT
+ | RTFS_DOS_NT_SPARSE_FILE
+ | RTFS_DOS_NT_DEVICE
+ | RTFS_DOS_DIRECTORY)
+ & RTFS_DOS_MASK_NT)
+ >> RTFS_DOS_SHIFT;
+ Assert(!(BasicInfo.FileAttributes & ~0x31a7U /* FILE_ATTRIBUTE_VALID_SET_FLAGS */));
+ if (!BasicInfo.FileAttributes)
+ BasicInfo.FileAttributes |= FILE_ATTRIBUTE_NORMAL;
+
+ NTSTATUS rcNt = NtSetInformationFile(hNativeFile, &Ios, &BasicInfo, sizeof(BasicInfo), FileBasicInformation);
+ if (NT_SUCCESS(rcNt))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+RTDECL(int) RTFileSetMode(RTFILE hFile, RTFMODE fMode)
+{
+ HANDLE hNative = (HANDLE)RTFileToNative(hFile);
+ AssertReturn(hNative != RTNT_INVALID_HANDLE_VALUE, VERR_INVALID_HANDLE);
+ fMode = rtFsModeNormalize(fMode, NULL, 0, RTFS_TYPE_FILE);
+ AssertReturn(rtFsModeIsValidPermissions(fMode), VERR_INVALID_FMODE);
+
+ return rtNtFileSetModeWorker(hNative, fMode);
+}
diff --git a/src/VBox/Runtime/r3/nt/RTPathQueryInfo-nt.cpp b/src/VBox/Runtime/r3/nt/RTPathQueryInfo-nt.cpp
new file mode 100644
index 00000000..a7b6459b
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/RTPathQueryInfo-nt.cpp
@@ -0,0 +1,680 @@
+/* $Id: RTPathQueryInfo-nt.cpp $ */
+/** @file
+ * IPRT - RTPathQueryInfo[Ex], Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include "internal-r3-nt.h"
+
+#include <iprt/path.h>
+#include <iprt/err.h>
+#include <iprt/time.h>
+#include "internal/fs.h"
+#include "internal/path.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Helper for comparing a UNICODE_STRING with a string litteral. */
+#define ARE_UNICODE_STRINGS_EQUAL(a_UniStr, a_wszType) \
+ ( (a_UniStr)->Length == sizeof(a_wszType) - sizeof(RTUTF16) \
+ && memcmp((a_UniStr)->Buffer, a_wszType, sizeof(a_wszType) - sizeof(RTUTF16)) == 0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+typedef NTSTATUS (NTAPI *PFNNTQUERYFULLATTRIBUTESFILE)(struct _OBJECT_ATTRIBUTES *, struct _FILE_NETWORK_OPEN_INFORMATION *);
+extern PFNNTQUERYFULLATTRIBUTESFILE g_pfnNtQueryFullAttributesFile; /* init-win.cpp */
+
+
+/* ASSUMES FileID comes after ShortName and the structs are identical up to that point. */
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, NextEntryOffset, FILE_ID_BOTH_DIR_INFORMATION, NextEntryOffset);
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileIndex , FILE_ID_BOTH_DIR_INFORMATION, FileIndex );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, CreationTime , FILE_ID_BOTH_DIR_INFORMATION, CreationTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastAccessTime , FILE_ID_BOTH_DIR_INFORMATION, LastAccessTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastWriteTime , FILE_ID_BOTH_DIR_INFORMATION, LastWriteTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ChangeTime , FILE_ID_BOTH_DIR_INFORMATION, ChangeTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EndOfFile , FILE_ID_BOTH_DIR_INFORMATION, EndOfFile );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, AllocationSize , FILE_ID_BOTH_DIR_INFORMATION, AllocationSize );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileAttributes , FILE_ID_BOTH_DIR_INFORMATION, FileAttributes );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileNameLength , FILE_ID_BOTH_DIR_INFORMATION, FileNameLength );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EaSize , FILE_ID_BOTH_DIR_INFORMATION, EaSize );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortNameLength, FILE_ID_BOTH_DIR_INFORMATION, ShortNameLength);
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortName , FILE_ID_BOTH_DIR_INFORMATION, ShortName );
+
+
+
+/**
+ * Splits up an NT path into directory and filename.
+ *
+ * @param pNtName The path to split.
+ * @param pNtParent Where to return the directory path.
+ * @param pNtFilename Where to return the filename part.
+ * @param fNoParentDirSlash Whether to make sure the directory path doesn't
+ * end with a slash (except root).
+ */
+static void ntPathNtSplitName(UNICODE_STRING const *pNtName, UNICODE_STRING *pNtParent, UNICODE_STRING *pNtFilename,
+ bool fNoParentDirSlash)
+{
+ PRTUTF16 pwszBuffer = pNtName->Buffer;
+ size_t off = pNtName->Length / sizeof(RTUTF16);
+
+ /* Skip trailing slash if present. */
+ if ( off > 0
+ && pwszBuffer[off - 1] == '\\')
+ off--;
+
+ /* Find the slash before that. */
+ RTUTF16 wc;
+ while ( off > 0
+ && (wc = pwszBuffer[off - 1]) != '\\'
+ && wc != '/')
+ off--;
+ if (off != 0)
+ {
+ pNtParent->Buffer = pwszBuffer;
+ pNtParent->MaximumLength = pNtParent->Length = (USHORT)(off * sizeof(RTUTF16));
+ }
+ else
+ {
+ AssertFailed(); /* This is impossible and won't work (NT doesn't know '.' or '..'). */
+ /** @todo query the whole path as it is possible relative. Use the buffer for
+ * temporary name storage. */
+ pNtParent->Buffer = L".";
+ pNtParent->Length = 1 * sizeof(RTUTF16);
+ pNtParent->MaximumLength = 2 * sizeof(RTUTF16);
+ }
+
+ pNtFilename->Buffer = &pwszBuffer[off];
+ pNtFilename->Length = pNtName->Length - (USHORT)(off * sizeof(RTUTF16));
+ pNtFilename->MaximumLength = pNtName->MaximumLength - (USHORT)(off * sizeof(RTUTF16));
+
+ while ( fNoParentDirSlash
+ && pNtParent->Length > sizeof(RTUTF16)
+ && pNtParent->Buffer[pNtParent->Length / sizeof(RTUTF16) - 1] == '\\')
+ pNtParent->Length -= sizeof(RTUTF16);
+}
+
+
+/**
+ * Deals with enmAddAttr != RTFSOBJATTRADD_UNIX.
+ *
+ * @returns IPRT status code (usually @a rc).
+ * @param rc The return code.
+ * @param pObjInfo The info to complete.
+ * @param enmAddAttr What to complete it with. Caller should fill in
+ * RTFSOBJATTRADD_UNIX.
+ */
+static int rtPathNtQueryInfoFillInDummyData(int rc, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ switch (enmAddAttr)
+ {
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ break;
+
+ case RTFSOBJATTRADD_NOTHING:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ rc = VERR_INTERNAL_ERROR;
+ }
+ return rc;
+}
+
+
+/**
+ * Deal with getting info about something that could be in a directory object.
+ *
+ * @returns IPRT status code
+ * @param pObjAttr The NT object attribute.
+ * @param pObjInfo Where to return the info.
+ * @param enmAddAttr Which extra attributes to get (/fake).
+ * @param fFlags The flags.
+ * @param pvBuf Query buffer space.
+ * @param cbBuf Size of the buffer. ASSUMES lots of space.
+ * @param rcNtCaller The status code that got us here.
+ */
+static int rtPathNtQueryInfoInDirectoryObject(OBJECT_ATTRIBUTES *pObjAttr, PRTFSOBJINFO pObjInfo,
+ RTFSOBJATTRADD enmAddAttr, uint32_t fFlags,
+ void *pvBuf, size_t cbBuf, NTSTATUS rcNtCaller)
+{
+ RT_NOREF(fFlags);
+
+ /*
+ * Special case: Root dir.
+ */
+ if ( pObjAttr->RootDirectory == NULL
+ && pObjAttr->ObjectName->Length == sizeof(RTUTF16)
+ && pObjAttr->ObjectName->Buffer[0] == '\\')
+ {
+ pObjInfo->cbObject = 0;
+ pObjInfo->cbAllocated = 0;
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, 0);
+ pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
+ return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
+ }
+
+ /*
+ * We must open and scan the parent directory object.
+ */
+ UNICODE_STRING NtDirName;
+ UNICODE_STRING NtDirEntry;
+ ntPathNtSplitName(pObjAttr->ObjectName, &NtDirName, &NtDirEntry, true /*fNoParentDirSlash*/);
+
+ while ( NtDirEntry.Length > sizeof(RTUTF16)
+ && NtDirEntry.Buffer[NtDirEntry.Length / sizeof(RTUTF16) - 1] == '\\')
+ NtDirEntry.Length -= sizeof(RTUTF16);
+
+ pObjAttr->ObjectName = &NtDirName;
+ HANDLE hDir = RTNT_INVALID_HANDLE_VALUE;
+ NTSTATUS rcNt = NtOpenDirectoryObject(&hDir, DIRECTORY_QUERY | DIRECTORY_TRAVERSE, pObjAttr);
+ if (NT_SUCCESS(rcNt))
+ {
+ ULONG uObjDirCtx = 0;
+ for (;;)
+ {
+ ULONG cbReturned = 0;
+ rcNt = NtQueryDirectoryObject(hDir,
+ pvBuf,
+ (ULONG)cbBuf,
+ FALSE /*ReturnSingleEntry */,
+ FALSE /*RestartScan*/,
+ &uObjDirCtx,
+ &cbReturned);
+ if (!NT_SUCCESS(rcNt))
+ break;
+
+ for (POBJECT_DIRECTORY_INFORMATION pObjDir = (POBJECT_DIRECTORY_INFORMATION)pvBuf;
+ pObjDir->Name.Length != 0;
+ pObjDir++)
+ {
+ if ( pObjDir->Name.Length == NtDirEntry.Length
+ && memcmp(pObjDir->Name.Buffer, NtDirEntry.Buffer, NtDirEntry.Length) == 0)
+ {
+ /*
+ * Find it. Fill in the info we've got and return (see similar code in direnum-r3-nt.cpp).
+ */
+ NtClose(hDir);
+
+ pObjInfo->cbObject = 0;
+ pObjInfo->cbAllocated = 0;
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, 0);
+
+ if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"Directory"))
+ pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
+ else if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"SymbolicLink"))
+ pObjInfo->Attr.fMode = RTFS_DOS_NT_REPARSE_POINT | RTFS_TYPE_SYMLINK | 0777;
+ else if (ARE_UNICODE_STRINGS_EQUAL(&pObjDir->TypeName, L"Device"))
+ pObjInfo->Attr.fMode = RTFS_DOS_NT_DEVICE | RTFS_TYPE_DEV_CHAR | 0666;
+ else
+ pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0666;
+
+ pObjInfo->Attr.enmAdditional = enmAddAttr;
+ return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
+ }
+ }
+ }
+
+ NtClose(hDir);
+ if (rcNt == STATUS_NO_MORE_FILES || rcNt == STATUS_NO_MORE_ENTRIES || rcNt == STATUS_NO_SUCH_FILE)
+ return VERR_FILE_NOT_FOUND;
+ }
+ else
+ return RTErrConvertFromNtStatus(rcNtCaller);
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+/**
+ * Queries information from a file or directory handle.
+ *
+ * This is shared between the RTPathQueryInfo, RTFileQueryInfo and
+ * RTDirQueryInfo code.
+ *
+ * @returns IPRT status code.
+ * @param hFile The handle to query information from. Must have
+ * the necessary privileges.
+ * @param pvBuf Pointer to a scratch buffer.
+ * @param cbBuf The size of the buffer. This must be large
+ * enough to hold a FILE_ALL_INFORMATION struct.
+ * @param pObjInfo Where to return information about the handle.
+ * @param enmAddAttr What extra info to return.
+ * @param pszPath The path if this is a file (for exe detect).
+ * @param uReparseTag The reparse tag number (0 if not applicable) for
+ * symlink detection/whatnot.
+ */
+DECLHIDDEN(int) rtPathNtQueryInfoFromHandle(HANDLE hFile, void *pvBuf, size_t cbBuf, PRTFSOBJINFO pObjInfo,
+ RTFSOBJATTRADD enmAddAttr, const char *pszPath, ULONG uReparseTag)
+{
+ Assert(cbBuf >= sizeof(FILE_ALL_INFORMATION));
+
+ /** @todo Try optimize this for when RTFSOBJATTRADD_UNIX isn't set? */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pvBuf, sizeof(FILE_ALL_INFORMATION), FileAllInformation);
+ if ( NT_SUCCESS(rcNt)
+ || rcNt == STATUS_BUFFER_OVERFLOW)
+ {
+ FILE_ALL_INFORMATION *pAllInfo = (FILE_ALL_INFORMATION *)pvBuf;
+ pObjInfo->cbObject = pAllInfo->StandardInformation.EndOfFile.QuadPart;
+ pObjInfo->cbAllocated = pAllInfo->StandardInformation.AllocationSize.QuadPart;
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, pAllInfo->BasicInformation.CreationTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, pAllInfo->BasicInformation.LastAccessTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, pAllInfo->BasicInformation.LastWriteTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, pAllInfo->BasicInformation.ChangeTime.QuadPart);
+ pObjInfo->Attr.fMode = rtFsModeFromDos( (pAllInfo->BasicInformation.FileAttributes << RTFS_DOS_SHIFT)
+ & RTFS_DOS_MASK_NT,
+ pszPath, pszPath ? strlen(pszPath) : 0, uReparseTag, 0);
+ pObjInfo->Attr.enmAdditional = enmAddAttr;
+ if (enmAddAttr == RTFSOBJATTRADD_UNIX)
+ {
+ pObjInfo->Attr.u.Unix.uid = ~0U;
+ pObjInfo->Attr.u.Unix.gid = ~0U;
+ pObjInfo->Attr.u.Unix.cHardlinks = RT_MAX(1, pAllInfo->StandardInformation.NumberOfLinks);
+ pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /* below */
+ pObjInfo->Attr.u.Unix.INodeId = pAllInfo->InternalInformation.IndexNumber.QuadPart;
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+
+ /* Get the serial number. */
+ rcNt = NtQueryVolumeInformationFile(hFile, &Ios, pvBuf, (ULONG)RT_MIN(cbBuf, _2K), FileFsVolumeInformation);
+ if (NT_SUCCESS(rcNt) || rcNt == STATUS_BUFFER_OVERFLOW)
+ {
+ FILE_FS_VOLUME_INFORMATION *pVolInfo = (FILE_FS_VOLUME_INFORMATION *)pvBuf;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = pVolInfo->VolumeSerialNumber;
+ }
+ }
+
+ return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
+ }
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+/**
+ * Worker for RTPathQueryInfoEx and RTDirRelPathQueryInfo.
+ *
+ * @returns IPRT status code.
+ * @param hRootDir The root directory that pNtName is relative to.
+ * @param pNtName The NT path which we want to query info for.
+ * @param pObjInfo Where to return the info.
+ * @param enmAddAttr What additional info to get/fake.
+ * @param fFlags Query flags (RTPATH_F_XXX).
+ * @param pszPath The path for detecting executables and such.
+ * Pass empty string if not applicable/available.
+ */
+DECLHIDDEN(int) rtPathNtQueryInfoWorker(HANDLE hRootDir, UNICODE_STRING *pNtName, PRTFSOBJINFO pObjInfo,
+ RTFSOBJATTRADD enmAddAttr, uint32_t fFlags, const char *pszPath)
+{
+ /*
+ * There are a three different ways of doing this:
+ * 1. Use NtQueryFullAttributesFile to the get basic file info.
+ * 2. Open whatever the path points to and use NtQueryInformationFile.
+ * 3. Open the parent directory and use NtQueryDirectoryFile like RTDirReadEx.
+ *
+ * The first two options may fail with sharing violations or access denied,
+ * in which case we must use the last one as fallback.
+ */
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt;
+ OBJECT_ATTRIBUTES ObjAttr;
+ union
+ {
+ FILE_NETWORK_OPEN_INFORMATION NetOpenInfo;
+ FILE_ALL_INFORMATION AllInfo;
+ FILE_FS_VOLUME_INFORMATION VolInfo;
+ FILE_BOTH_DIR_INFORMATION Both;
+ FILE_ID_BOTH_DIR_INFORMATION BothId;
+ uint8_t abPadding[sizeof(FILE_ID_BOTH_DIR_INFORMATION) + RTPATH_MAX * sizeof(wchar_t)];
+ } uBuf;
+
+ /*
+ * We can only use the first option if no additional UNIX attribs are
+ * requested and it isn't a symbolic link. NT directory object
+ */
+ int rc = VINF_TRY_AGAIN;
+ if ( enmAddAttr != RTFSOBJATTRADD_UNIX
+ && g_pfnNtQueryFullAttributesFile)
+ {
+ InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
+ rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &uBuf.NetOpenInfo);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (!(uBuf.NetOpenInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ {
+ pObjInfo->cbObject = uBuf.NetOpenInfo.EndOfFile.QuadPart;
+ pObjInfo->cbAllocated = uBuf.NetOpenInfo.AllocationSize.QuadPart;
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, uBuf.NetOpenInfo.CreationTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, uBuf.NetOpenInfo.LastAccessTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, uBuf.NetOpenInfo.LastWriteTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, uBuf.NetOpenInfo.ChangeTime.QuadPart);
+ pObjInfo->Attr.fMode = rtFsModeFromDos((uBuf.NetOpenInfo.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszPath, strlen(pszPath), 0 /*uReparseTag*/, 0);
+ pObjInfo->Attr.enmAdditional = enmAddAttr;
+
+ return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
+ }
+ }
+ else if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
+ || rcNt == STATUS_OBJECT_NAME_INVALID
+ || rcNt == STATUS_INVALID_PARAMETER)
+ {
+ rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ else if ( rcNt != STATUS_ACCESS_DENIED
+ && rcNt != STATUS_SHARING_VIOLATION)
+ rc = RTErrConvertFromNtStatus(rcNt);
+ else
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ }
+
+ /*
+ * Try the 2nd option. We might have to redo this if not following symbolic
+ * links and the reparse point isn't a symbolic link but a mount point or similar.
+ * We want to return information about the mounted root directory if we can, not
+ * the directory in which it was mounted.
+ */
+ if (rc == VINF_TRY_AGAIN)
+ {
+ static int volatile g_fReparsePoints = -1;
+ uint32_t fOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT;
+ int fReparsePoints = g_fReparsePoints;
+ if (fReparsePoints != 0 && !(fFlags & RTPATH_F_FOLLOW_LINK))
+ fOptions |= FILE_OPEN_REPARSE_POINT;
+
+ InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
+ rcNt = NtCreateFile(&hFile,
+ FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*pcbFile*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ fOptions,
+ NULL /*pvEaBuffer*/,
+ 0 /*cbEa*/);
+ if ( ( rcNt == STATUS_INVALID_PARAMETER
+ || rcNt == STATUS_INVALID_PARAMETER_9)
+ && fReparsePoints == -1
+ && (fOptions & FILE_OPEN_REPARSE_POINT))
+ {
+ fOptions &= ~FILE_OPEN_REPARSE_POINT;
+ rcNt = NtCreateFile(&hFile,
+ FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*pcbFile*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ fOptions,
+ NULL /*pvEaBuffer*/,
+ 0 /*cbEa*/);
+ if (rcNt != STATUS_INVALID_PARAMETER)
+ g_fReparsePoints = fReparsePoints = 0;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ /* Query tag information first in order to try re-open non-symlink reparse points. */
+ FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
+ rcNt = NtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), FileAttributeTagInformation);
+ if (!NT_SUCCESS(rcNt))
+ TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
+ if ( !(TagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ || TagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK
+ || (fFlags & RTPATH_F_FOLLOW_LINK))
+ { /* likely */ }
+ else
+ {
+ /* Reparse point that isn't a symbolic link, try follow the reparsing. */
+ HANDLE hFile2;
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtCreateFile(&hFile2,
+ FILE_READ_ATTRIBUTES | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*pcbFile*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*pvEaBuffer*/,
+ 0 /*cbEa*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ NtClose(hFile);
+ hFile = hFile2;
+ TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
+ }
+ }
+
+ /*
+ * Get the information we need and convert it.
+ */
+ rc = rtPathNtQueryInfoFromHandle(hFile, &uBuf, sizeof(uBuf), pObjInfo, enmAddAttr, pszPath, TagInfo.ReparseTag);
+ NtClose(hFile);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ if (RT_FAILURE(rc))
+ rc = VINF_TRY_AGAIN;
+ }
+ else if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH
+ || rcNt == STATUS_OBJECT_NAME_INVALID
+ /*|| rcNt == STATUS_INVALID_PARAMETER*/)
+ {
+ rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ else if ( rcNt != STATUS_ACCESS_DENIED
+ && rcNt != STATUS_SHARING_VIOLATION)
+ rc = RTErrConvertFromNtStatus(rcNt);
+ else
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ }
+
+ /*
+ * Try the 3rd option if none of the other worked.
+ * If none of the above worked, try open the directory and enumerate
+ * the file we're after. This
+ */
+ if (rc == VINF_TRY_AGAIN)
+ {
+ /* Split up the name into parent directory path and filename. */
+ UNICODE_STRING NtDirName;
+ UNICODE_STRING NtFilter;
+ ntPathNtSplitName(pNtName, &NtDirName, &NtFilter, false /*fNoParentDirSlash*/);
+
+ /* Try open the directory. */
+ InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
+ rcNt = NtCreateFile(&hFile,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*pcbFile*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*pvEaBuffer*/,
+ 0 /*cbEa*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ FILE_INFORMATION_CLASS enmInfoClass;
+ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) > RT_MAKE_U64(0,5) /* > W2K */)
+ enmInfoClass = FileIdBothDirectoryInformation; /* Introduced in XP, from I can tell. */
+ else
+ enmInfoClass = FileBothDirectoryInformation;
+ rcNt = NtQueryDirectoryFile(hFile,
+ NULL /* Event */,
+ NULL /* ApcRoutine */,
+ NULL /* ApcContext */,
+ &Ios,
+ &uBuf,
+ RT_MIN(sizeof(uBuf), 0xfff0),
+ enmInfoClass,
+ TRUE /*ReturnSingleEntry */,
+ &NtFilter,
+ FALSE /*RestartScan */);
+ if (NT_SUCCESS(rcNt))
+ {
+ pObjInfo->cbObject = uBuf.Both.EndOfFile.QuadPart;
+ pObjInfo->cbAllocated = uBuf.Both.AllocationSize.QuadPart;
+
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, uBuf.Both.CreationTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, uBuf.Both.LastAccessTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, uBuf.Both.LastWriteTime.QuadPart);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, uBuf.Both.ChangeTime.QuadPart);
+
+ pObjInfo->Attr.fMode = rtFsModeFromDos((uBuf.Both.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszPath, strlen(pszPath), uBuf.Both.EaSize, 0);
+
+ pObjInfo->Attr.enmAdditional = enmAddAttr;
+ if (enmAddAttr == RTFSOBJATTRADD_UNIX)
+ {
+ pObjInfo->Attr.u.Unix.uid = ~0U;
+ pObjInfo->Attr.u.Unix.gid = ~0U;
+ pObjInfo->Attr.u.Unix.cHardlinks = 1;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /* below */
+ pObjInfo->Attr.u.Unix.INodeId = enmInfoClass == FileIdBothDirectoryInformation
+ ? uBuf.BothId.FileId.QuadPart : 0;
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+
+ /* Get the serial number. */
+ rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &uBuf, RT_MIN(sizeof(uBuf), _2K),
+ FileFsVolumeInformation);
+ if (NT_SUCCESS(rcNt))
+ pObjInfo->Attr.u.Unix.INodeIdDevice = uBuf.VolInfo.VolumeSerialNumber;
+ }
+
+ rc = rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ NtClose(hFile);
+ }
+ /*
+ * Quite possibly a object directory.
+ */
+ else if ( rcNt == STATUS_OBJECT_NAME_INVALID /* with trailing slash */
+ || rcNt == STATUS_OBJECT_TYPE_MISMATCH /* without trailing slash */ )
+ {
+ InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL);
+ rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt);
+ if (RT_FAILURE(rc))
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
+ AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
+ && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+ ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+
+ /*
+ * Convert the input path and call common worker.
+ */
+ HANDLE hRootDir;
+ UNICODE_STRING NtName;
+ int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPathNtQueryInfoWorker(hRootDir, &NtName, pObjInfo, enmAdditionalAttribs, fFlags, pszPath);
+ RTNtPathFree(&NtName, &hRootDir);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
+}
+
diff --git a/src/VBox/Runtime/r3/nt/RTPathSetMode-r3-nt.cpp b/src/VBox/Runtime/r3/nt/RTPathSetMode-r3-nt.cpp
new file mode 100644
index 00000000..d6fb0444
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/RTPathSetMode-r3-nt.cpp
@@ -0,0 +1,98 @@
+/* $Id: RTPathSetMode-r3-nt.cpp $ */
+/** @file
+ * IPRT - RTPathSetMode, Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include "internal-r3-nt.h"
+
+#include <iprt/path.h>
+#include <iprt/err.h>
+
+#include "internal/fs.h"
+
+
+
+RTDECL(int) RTPathSetMode(const char *pszPath, RTFMODE fMode)
+{
+ fMode = rtFsModeNormalize(fMode, pszPath, 0, 0);
+ AssertReturn(rtFsModeIsValidPermissions(fMode), VERR_INVALID_FMODE);
+
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRootDir;
+ int rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hPath = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, 0 /*fAttrib*/, hRootDir, NULL);
+
+ ULONG fOpenOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT;
+ //if (fFlags & RTPATH_F_ON_LINK)
+ // fOpenOptions |= FILE_OPEN_REPARSE_POINT;
+ NTSTATUS rcNt = NtCreateFile(&hPath,
+ FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ fOpenOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ rc = rtNtFileSetModeWorker(hPath, fMode);
+
+ rcNt = NtClose(hPath);
+ if (!NT_SUCCESS(rcNt) && RT_SUCCESS(rc))
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ RTNtPathFree(&NtName, &hRootDir);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/nt/RTProcQueryParent-r3-nt.cpp b/src/VBox/Runtime/r3/nt/RTProcQueryParent-r3-nt.cpp
new file mode 100644
index 00000000..650131d1
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/RTProcQueryParent-r3-nt.cpp
@@ -0,0 +1,103 @@
+/* $Id: RTProcQueryParent-r3-nt.cpp $ */
+/** @file
+ * IPRT - Process, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <iprt/nt/nt.h>
+
+#include <iprt/process.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+
+RTR3DECL(int) RTProcQueryParent(RTPROCESS hProcess, PRTPROCESS phParent)
+{
+ NTSTATUS rcNt;
+ HANDLE hClose = RTNT_INVALID_HANDLE_VALUE;
+ HANDLE hNtProc;
+
+ /*
+ * Open the process. We take a shortcut if it's the current process.
+ */
+ if (hProcess == RTProcSelf())
+ hNtProc = NtCurrentProcess();
+ else
+ {
+ CLIENT_ID ClientId;
+ ClientId.UniqueProcess = (HANDLE)(uintptr_t)hProcess;
+ ClientId.UniqueThread = NULL;
+
+ OBJECT_ATTRIBUTES ObjAttrs;
+ InitializeObjectAttributes(&ObjAttrs, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ rcNt = NtOpenProcess(&hClose, PROCESS_QUERY_LIMITED_INFORMATION, &ObjAttrs, &ClientId);
+ if (!NT_SUCCESS(rcNt))
+ rcNt = NtOpenProcess(&hClose, PROCESS_QUERY_INFORMATION, &ObjAttrs, &ClientId);
+ if (!NT_SUCCESS(rcNt))
+ return RTErrConvertFromNtStatus(rcNt);
+ hNtProc = hClose;
+ }
+
+ /*
+ * Query the information.
+ */
+ int rc;
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ ULONG cbIgn;
+ rcNt = NtQueryInformationProcess(hNtProc, ProcessBasicInformation, &BasicInfo, sizeof(BasicInfo), &cbIgn);
+ if (NT_SUCCESS(rcNt))
+ {
+ *phParent = BasicInfo.InheritedFromUniqueProcessId;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ /*
+ * Clean up.
+ */
+ if (hClose != RTNT_INVALID_HANDLE_VALUE)
+ NtClose(hClose);
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp b/src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp
new file mode 100644
index 00000000..0325e842
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/direnum-r3-nt.cpp
@@ -0,0 +1,1011 @@
+/* $Id: direnum-r3-nt.cpp $ */
+/** @file
+ * IPRT - Directory Enumeration, Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include "internal-r3-nt.h"
+
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/utf16.h>
+#include "internal/fs.h"
+#include "internal/dir.h"
+#include "internal/path.h"
+#include "../win/internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Whether to return a single record (TRUE) or multiple (FALSE). */
+#define RTDIR_NT_SINGLE_RECORD FALSE
+
+/** Go hard on record chaining (has slight performance impact). */
+#ifdef RT_STRICT
+# define RTDIR_NT_STRICT
+#endif
+
+
+/* ASSUMES FileID comes after ShortName and the structs are identical up to that point. */
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, NextEntryOffset, FILE_ID_BOTH_DIR_INFORMATION, NextEntryOffset);
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileIndex , FILE_ID_BOTH_DIR_INFORMATION, FileIndex );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, CreationTime , FILE_ID_BOTH_DIR_INFORMATION, CreationTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastAccessTime , FILE_ID_BOTH_DIR_INFORMATION, LastAccessTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, LastWriteTime , FILE_ID_BOTH_DIR_INFORMATION, LastWriteTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ChangeTime , FILE_ID_BOTH_DIR_INFORMATION, ChangeTime );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EndOfFile , FILE_ID_BOTH_DIR_INFORMATION, EndOfFile );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, AllocationSize , FILE_ID_BOTH_DIR_INFORMATION, AllocationSize );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileAttributes , FILE_ID_BOTH_DIR_INFORMATION, FileAttributes );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, FileNameLength , FILE_ID_BOTH_DIR_INFORMATION, FileNameLength );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, EaSize , FILE_ID_BOTH_DIR_INFORMATION, EaSize );
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortNameLength, FILE_ID_BOTH_DIR_INFORMATION, ShortNameLength);
+AssertCompileMembersSameSizeAndOffset(FILE_BOTH_DIR_INFORMATION, ShortName , FILE_ID_BOTH_DIR_INFORMATION, ShortName );
+
+
+
+size_t rtDirNativeGetStructSize(const char *pszPath)
+{
+ NOREF(pszPath);
+ return sizeof(RTDIRINTERNAL);
+}
+
+
+int rtDirNativeOpen(PRTDIRINTERNAL pDir, uintptr_t hRelativeDir, void *pvNativeRelative)
+{
+ /*
+ * Convert the filter to UTF-16.
+ */
+ int rc;
+ pDir->pNtFilterStr = NULL;
+ if ( pDir->cchFilter > 0
+ && pDir->enmFilter == RTDIRFILTER_WINNT)
+ {
+ PRTUTF16 pwszTmp;
+ rc = RTStrToUtf16(pDir->pszFilter, &pwszTmp);
+ if (RT_FAILURE(rc))
+ return rc;
+ pDir->NtFilterStr.Buffer = pwszTmp;
+ pDir->NtFilterStr.Length = pDir->NtFilterStr.MaximumLength = (uint16_t)(RTUtf16Len(pwszTmp) * sizeof(RTUTF16));
+ pDir->pNtFilterStr = &pDir->NtFilterStr;
+ }
+
+ /*
+ * Try open the directory
+ */
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ bool fObjDir = false;
+#endif
+ if (hRelativeDir != ~(uintptr_t)0 && pvNativeRelative == NULL)
+ {
+ /* Caller already opened it, easy! */
+ pDir->hDir = (HANDLE)hRelativeDir;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ /*
+ * If we have to check for reparse points, this gets complicated!
+ */
+ static int volatile g_fReparsePoints = -1;
+ uint32_t fOptions = FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT;
+ int fReparsePoints = g_fReparsePoints;
+ if ( fReparsePoints != 0
+ && (pDir->fFlags & RTDIR_F_NO_FOLLOW)
+ && !pDir->fDirSlash)
+ fOptions |= FILE_OPEN_REPARSE_POINT;
+
+ ACCESS_MASK fDesiredAccess = FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | FILE_TRAVERSE | SYNCHRONIZE;
+ for (;;)
+ {
+ if (pvNativeRelative == NULL)
+ rc = RTNtPathOpenDir(pDir->pszPath,
+ fDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ fOptions,
+ OBJ_CASE_INSENSITIVE,
+ &pDir->hDir,
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ &fObjDir
+#else
+ NULL
+#endif
+ );
+ else
+ rc = RTNtPathOpenDirEx((HANDLE)hRelativeDir,
+ (struct _UNICODE_STRING *)pvNativeRelative,
+ fDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ fOptions,
+ OBJ_CASE_INSENSITIVE,
+ &pDir->hDir,
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ &fObjDir
+#else
+ NULL
+#endif
+ );
+ if ( rc == VERR_ACCESS_DENIED /* Seen with c:\windows\system32\com\dmp on w7 & w10 (admin mode). */
+ && (fDesiredAccess & FILE_TRAVERSE))
+ {
+ fDesiredAccess &= ~FILE_TRAVERSE;
+ continue;
+ }
+ if ( !(fOptions & FILE_OPEN_REPARSE_POINT)
+ || (rc != VINF_SUCCESS && rc != VERR_INVALID_PARAMETER) )
+ break;
+ if (rc == VINF_SUCCESS)
+ {
+ if (fReparsePoints == -1)
+ g_fReparsePoints = 1;
+
+ /*
+ * We now need to check if we opened a symbolic directory link.
+ * (These can be enumerated, but contains only '.' and '..'.)
+ */
+ FILE_ATTRIBUTE_TAG_INFORMATION TagInfo = { 0, 0 };
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryInformationFile(pDir->hDir, &Ios, &TagInfo, sizeof(TagInfo), FileAttributeTagInformation);
+ AssertMsg(NT_SUCCESS(rcNt), ("%#x\n", rcNt));
+ if (!NT_SUCCESS(rcNt))
+ TagInfo.FileAttributes = TagInfo.ReparseTag = 0;
+ if (!(TagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ break;
+
+ NtClose(pDir->hDir);
+ pDir->hDir = RTNT_INVALID_HANDLE_VALUE;
+
+ if (TagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK)
+ {
+ rc = VERR_IS_A_SYMLINK;
+ break;
+ }
+
+ /* Reparse point that isn't a symbolic link, try follow the reparsing. */
+ }
+ else if (fReparsePoints == -1)
+ g_fReparsePoints = fReparsePoints = 0;
+ fOptions &= ~FILE_OPEN_REPARSE_POINT;
+ }
+
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Init data.
+ */
+ pDir->fDataUnread = false; /* spelling it out */
+ pDir->uDirDev = 0;
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (fObjDir)
+ pDir->enmInfoClass = FileMaximumInformation; /* object directory. */
+#endif
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirClose(RTDIR hDir)
+{
+ PRTDIRINTERNAL pDir = hDir;
+
+ /*
+ * Validate input.
+ */
+ if (!pDir)
+ return VERR_INVALID_PARAMETER;
+ if (pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Close the handle.
+ */
+ pDir->u32Magic = ~RTDIR_MAGIC;
+ if (pDir->hDir != RTNT_INVALID_HANDLE_VALUE)
+ {
+ int rc = RTNtPathClose(pDir->hDir);
+ AssertRC(rc);
+ pDir->hDir = RTNT_INVALID_HANDLE_VALUE;
+ }
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+ RTUtf16Free(pDir->NtFilterStr.Buffer);
+ pDir->NtFilterStr.Buffer = NULL;
+ RTMemFree(pDir->pabBuffer);
+ pDir->pabBuffer = NULL;
+ RTMemFree(pDir);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks the validity of the current record.
+ *
+ * @returns IPRT status code
+ * @param pThis The directory instance data.
+ */
+static int rtDirNtCheckRecord(PRTDIRINTERNAL pThis)
+{
+#if defined(RTDIR_NT_STRICT) || defined(RT_ARCH_X86)
+# ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pThis->enmInfoClass != FileMaximumInformation)
+# endif
+ {
+ uintptr_t uEndAddr;
+ if (pThis->enmInfoClass == FileIdBothDirectoryInformation)
+ uEndAddr = (uintptr_t)&pThis->uCurData.pBothId->FileName[0];
+ else
+ uEndAddr = (uintptr_t)&pThis->uCurData.pBoth->FileName[0];
+
+# ifdef RT_ARCH_X86
+ /* Workaround for NT 3.1 bug where FAT returns a too short buffer length.
+ Including all NT 3.x versions in case it bug was fixed till NT 4. */
+ uintptr_t const uEndBuffer = (uintptr_t)&pThis->pabBuffer[pThis->cbBuffer];
+ if ( uEndAddr < uEndBuffer
+ && uEndAddr + pThis->uCurData.pBoth->FileNameLength <= uEndBuffer)
+ { /* likely */ }
+ else if ( ( g_enmWinVer == kRTWinOSType_NT310
+ || g_enmWinVer == kRTWinOSType_NT350 // not sure when it was fixed...
+ || g_enmWinVer == kRTWinOSType_NT351)
+ && pThis->enmInfoClass == FileBothDirectoryInformation)
+ {
+ size_t cbLeft = (uintptr_t)&pThis->pabBuffer[pThis->cbBufferAlloc] - (uintptr_t)pThis->uCurData.pBoth;
+ if ( cbLeft >= RT_UOFFSETOF(FILE_BOTH_DIR_INFORMATION, FileName)
+ && pThis->uCurData.pBoth->FileNameLength > 0
+ && cbLeft >= RT_UOFFSETOF(FILE_BOTH_DIR_INFORMATION, FileName) + pThis->uCurData.pBoth->FileNameLength)
+ {
+ pThis->cbBuffer = ((uintptr_t)&pThis->uCurData.pBoth->FileName[0] + pThis->uCurData.pBoth->FileNameLength)
+ - (uintptr_t)&pThis->pabBuffer[0];
+ }
+ }
+# endif
+
+# ifdef RTDIR_NT_STRICT
+ AssertReturn(uEndAddr < (uintptr_t)&pThis->pabBuffer[pThis->cbBuffer], VERR_IO_GEN_FAILURE);
+ AssertReturn(pThis->uCurData.pBoth->FileNameLength < _64K, VERR_FILENAME_TOO_LONG);
+ AssertReturn((pThis->uCurData.pBoth->FileNameLength & 1) == 0, VERR_IO_GEN_FAILURE);
+
+ uEndAddr += pThis->uCurData.pBoth->FileNameLength;
+ AssertReturn(uEndAddr <= (uintptr_t)&pThis->pabBuffer[pThis->cbBuffer], VERR_IO_GEN_FAILURE);
+
+ AssertReturn((unsigned)pThis->uCurData.pBoth->ShortNameLength <= sizeof(pThis->uCurData.pBoth->ShortName),
+ VERR_IO_GEN_FAILURE);
+# endif
+ }
+#else
+ RT_NOREF_PV(pThis);
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Advances the buffer pointer.
+ *
+ * @param pThis The directory instance data.
+ */
+static int rtDirNtAdvanceBuffer(PRTDIRINTERNAL pThis)
+{
+ int rc;
+
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pThis->enmInfoClass == FileMaximumInformation)
+ {
+ pThis->uCurData.pObjDir++;
+ pThis->fDataUnread = pThis->uCurData.pObjDir->Name.Length != 0;
+ return VINF_SUCCESS;
+ }
+#endif
+
+ pThis->fDataUnread = false;
+
+ uint32_t const offNext = pThis->uCurData.pBoth->NextEntryOffset;
+ if (offNext == 0)
+ return VINF_SUCCESS;
+
+#ifdef RTDIR_NT_STRICT
+ /* Make sure the next-record offset is beyond the current record. */
+ size_t cbRec;
+ if (pThis->enmInfoClass == FileIdBothDirectoryInformation)
+ cbRec = RT_UOFFSETOF(FILE_ID_BOTH_DIR_INFORMATION, FileName);
+ else
+ cbRec = RT_UOFFSETOF(FILE_BOTH_DIR_INFORMATION, FileName);
+ cbRec += pThis->uCurData.pBoth->FileNameLength;
+ AssertReturn(offNext >= cbRec, VERR_IO_GEN_FAILURE);
+#endif
+ pThis->uCurData.u += offNext;
+
+ rc = rtDirNtCheckRecord(pThis);
+ pThis->fDataUnread = RT_SUCCESS(rc);
+ return rc;
+}
+
+
+/**
+ * Fetches more data from the file system.
+ *
+ * @returns IPRT status code
+ * @param pThis The directory instance data.
+ */
+static int rtDirNtFetchMore(PRTDIRINTERNAL pThis)
+{
+ Assert(!pThis->fDataUnread);
+
+ /*
+ * Allocate the buffer the first time around.
+ * We do this in lazy fashion as some users of RTDirOpen will not actually
+ * list any files, just open it for various reasons.
+ *
+ * We also reduce the buffer size for networked devices as the windows 7-8.1,
+ * server 2012, ++ CIFS servers or/and IFSes screws up buffers larger than 64KB.
+ * There is an alternative hack below, btw. We'll leave both in for now.
+ */
+ bool fFirst = false;
+ if (!pThis->pabBuffer)
+ {
+ pThis->cbBufferAlloc = _256K;
+ if (true) /** @todo skip for known local devices, like the boot device? */
+ {
+ IO_STATUS_BLOCK Ios2 = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ FILE_FS_DEVICE_INFORMATION Info = { 0, 0 };
+ NTSTATUS rcNt2 = NtQueryVolumeInformationFile(pThis->hDir, &Ios2, &Info, sizeof(Info), FileFsDeviceInformation);
+ if ( !NT_SUCCESS(rcNt2)
+ || (Info.Characteristics & FILE_REMOTE_DEVICE)
+ || Info.DeviceType == FILE_DEVICE_NETWORK
+ || Info.DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM
+ || Info.DeviceType == FILE_DEVICE_NETWORK_REDIRECTOR
+ || Info.DeviceType == FILE_DEVICE_SMB)
+ pThis->cbBufferAlloc = _64K;
+ }
+
+ fFirst = false;
+ pThis->pabBuffer = (uint8_t *)RTMemAlloc(pThis->cbBufferAlloc);
+ if (!pThis->pabBuffer)
+ {
+ do
+ {
+ pThis->cbBufferAlloc /= 4;
+ pThis->pabBuffer = (uint8_t *)RTMemAlloc(pThis->cbBufferAlloc);
+ } while (pThis->pabBuffer == NULL && pThis->cbBufferAlloc > _4K);
+ if (!pThis->pabBuffer)
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Also try determining the device number.
+ */
+ PFILE_FS_VOLUME_INFORMATION pVolInfo = (PFILE_FS_VOLUME_INFORMATION)pThis->pabBuffer;
+ pVolInfo->VolumeSerialNumber = 0;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(pThis->hDir, &Ios,
+ pVolInfo, RT_MIN(_2K, pThis->cbBufferAlloc),
+ FileFsVolumeInformation);
+ if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status))
+ pThis->uDirDev = pVolInfo->VolumeSerialNumber;
+ else
+ pThis->uDirDev = 0;
+ AssertCompile(sizeof(pThis->uDirDev) == sizeof(pVolInfo->VolumeSerialNumber));
+ /** @todo Grow RTDEV to 64-bit and add low dword of VolumeCreationTime to the top of uDirDev. */
+ }
+
+ /*
+ * Read more.
+ */
+ NTSTATUS rcNt;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ if (pThis->enmInfoClass != (FILE_INFORMATION_CLASS)0)
+ {
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pThis->enmInfoClass == FileMaximumInformation)
+ {
+ Ios.Information = 0;
+ Ios.Status = rcNt = NtQueryDirectoryObject(pThis->hDir,
+ pThis->pabBuffer,
+ pThis->cbBufferAlloc,
+ RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
+ pThis->fRestartScan,
+ &pThis->uObjDirCtx,
+ (PULONG)&Ios.Information);
+ }
+ else
+#endif
+ rcNt = NtQueryDirectoryFile(pThis->hDir,
+ NULL /* Event */,
+ NULL /* ApcRoutine */,
+ NULL /* ApcContext */,
+ &Ios,
+ pThis->pabBuffer,
+ pThis->cbBufferAlloc,
+ pThis->enmInfoClass,
+ RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
+ pThis->pNtFilterStr,
+ pThis->fRestartScan);
+ }
+ else
+ {
+ /*
+ * The first time around we have to figure which info class we can use
+ * as well as the right buffer size. We prefer an info class which
+ * gives us file IDs (Vista+ IIRC) and we prefer large buffers (for long
+ * ReFS file names and such), but we'll settle for whatever works...
+ *
+ * The windows 7 thru 8.1 CIFS servers have been observed to have
+ * trouble with large buffers, but weirdly only when listing large
+ * directories. Seems 0x10000 is the max. (Samba does not exhibit
+ * these problems, of course.)
+ *
+ * This complicates things. The buffer size issues causes an
+ * STATUS_INVALID_PARAMETER error. Now, you would expect the lack of
+ * FileIdBothDirectoryInformation support to return
+ * STATUS_INVALID_INFO_CLASS, but I'm not entirely sure if we can 100%
+ * depend on third IFSs to get that right. Nor, am I entirely confident
+ * that we can depend on them to check the class before the buffer size.
+ *
+ * Thus the mess.
+ */
+ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) > RT_MAKE_U64(0,5) /* > W2K */)
+ pThis->enmInfoClass = FileIdBothDirectoryInformation; /* Introduced in XP, from I can tell. */
+ else
+ pThis->enmInfoClass = FileBothDirectoryInformation;
+ rcNt = NtQueryDirectoryFile(pThis->hDir,
+ NULL /* Event */,
+ NULL /* ApcRoutine */,
+ NULL /* ApcContext */,
+ &Ios,
+ pThis->pabBuffer,
+ pThis->cbBufferAlloc,
+ pThis->enmInfoClass,
+ RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
+ pThis->pNtFilterStr,
+ pThis->fRestartScan);
+ if (NT_SUCCESS(rcNt))
+ { /* likely */ }
+ else
+ {
+ bool fRestartScan = pThis->fRestartScan;
+ for (unsigned iRetry = 0; iRetry < 2; iRetry++)
+ {
+ if ( rcNt == STATUS_INVALID_INFO_CLASS
+ || rcNt == STATUS_INVALID_PARAMETER_8
+ || iRetry != 0)
+ pThis->enmInfoClass = FileBothDirectoryInformation;
+
+ uint32_t cbBuffer = pThis->cbBufferAlloc;
+ if ( rcNt == STATUS_INVALID_PARAMETER
+ || rcNt == STATUS_INVALID_PARAMETER_7
+ || rcNt == STATUS_INVALID_NETWORK_RESPONSE
+ || iRetry != 0)
+ {
+ cbBuffer = RT_MIN(cbBuffer / 2, _64K);
+ fRestartScan = true;
+ }
+
+ for (;;)
+ {
+ rcNt = NtQueryDirectoryFile(pThis->hDir,
+ NULL /* Event */,
+ NULL /* ApcRoutine */,
+ NULL /* ApcContext */,
+ &Ios,
+ pThis->pabBuffer,
+ cbBuffer,
+ pThis->enmInfoClass,
+ RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */,
+ pThis->pNtFilterStr,
+ fRestartScan);
+ if ( NT_SUCCESS(rcNt)
+ || cbBuffer == pThis->cbBufferAlloc
+ || cbBuffer <= sizeof(*pThis->uCurData.pBothId) + sizeof(WCHAR) * 260)
+ break;
+
+ /* Reduce the buffer size agressivly and try again. We fall back to
+ FindFirstFile values for the final lap. This means we'll do 4 rounds
+ with the current initial buffer size (64KB, 8KB, 1KB, 0x278/0x268). */
+ cbBuffer /= 8;
+ if (cbBuffer < 1024)
+ cbBuffer = pThis->enmInfoClass == FileIdBothDirectoryInformation
+ ? sizeof(*pThis->uCurData.pBothId) + sizeof(WCHAR) * 260
+ : sizeof(*pThis->uCurData.pBoth) + sizeof(WCHAR) * 260;
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ pThis->cbBufferAlloc = cbBuffer;
+ break;
+ }
+ }
+ }
+ }
+ if (!NT_SUCCESS(rcNt))
+ {
+ /* Note! VBoxSVR and CIFS file systems both ends up with STATUS_NO_SUCH_FILE here instead of STATUS_NO_MORE_FILES. */
+ if (rcNt == STATUS_NO_MORE_FILES || rcNt == STATUS_NO_MORE_ENTRIES || rcNt == STATUS_NO_SUCH_FILE)
+ return VERR_NO_MORE_FILES;
+ return RTErrConvertFromNtStatus(rcNt);
+ }
+ pThis->fRestartScan = false;
+ AssertMsg( Ios.Information
+ > (pThis->enmInfoClass == FileMaximumInformation ? sizeof(*pThis->uCurData.pObjDir) : sizeof(*pThis->uCurData.pBoth)),
+ ("Ios.Information=%#x\n", Ios.Information));
+
+ /*
+ * Set up the data members.
+ */
+ pThis->uCurData.u = (uintptr_t)pThis->pabBuffer;
+ pThis->cbBuffer = Ios.Information;
+
+ int rc = rtDirNtCheckRecord(pThis);
+ pThis->fDataUnread = RT_SUCCESS(rc);
+
+ return rc;
+}
+
+
+/**
+ * Converts the name from UTF-16 to UTF-8.
+ *
+ * Fortunately, the names are relative to the directory, so we won't have to do
+ * any sweaty path style coversion. :-)
+ *
+ * @returns IPRT status code
+ * @param pThis The directory instance data.
+ * @param cbName The file name length in bytes.
+ * @param pwsName The file name, not terminated.
+ */
+static int rtDirNtConvertName(PRTDIRINTERNAL pThis, uint32_t cbName, PCRTUTF16 pwsName)
+{
+ int rc = RTUtf16ToUtf8Ex(pwsName, cbName / 2, &pThis->pszName, pThis->cbNameAlloc, &pThis->cchName);
+ if (RT_SUCCESS(rc))
+ {
+ if (!pThis->cbNameAlloc)
+ pThis->cbNameAlloc = pThis->cchName + 1;
+ }
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ RTStrFree(pThis->pszName);
+ pThis->pszName = NULL;
+ pThis->cbNameAlloc = 0;
+
+ rc = RTUtf16ToUtf8Ex(pwsName, cbName / 2, &pThis->pszName, pThis->cbNameAlloc, &pThis->cchName);
+ if (RT_SUCCESS(rc))
+ pThis->cbNameAlloc = pThis->cchName + 1;
+ }
+ Assert(RT_SUCCESS(rc) ? pThis->pszName != NULL : pThis->pszName == NULL);
+ return rc;
+}
+
+
+/**
+ * Converts the name of the current record.
+ *
+ * @returns IPRT status code.
+ * @param pThis The directory instance data.
+ */
+static int rtDirNtConvertCurName(PRTDIRINTERNAL pThis)
+{
+ switch (pThis->enmInfoClass)
+ {
+ case FileIdBothDirectoryInformation:
+ return rtDirNtConvertName(pThis, pThis->uCurData.pBothId->FileNameLength, pThis->uCurData.pBothId->FileName);
+ case FileBothDirectoryInformation:
+ return rtDirNtConvertName(pThis, pThis->uCurData.pBoth->FileNameLength, pThis->uCurData.pBoth->FileName);
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ case FileMaximumInformation:
+ return rtDirNtConvertName(pThis, pThis->uCurData.pObjDir->Name.Length, pThis->uCurData.pObjDir->Name.Buffer);
+#endif
+
+ default:
+ AssertFailedReturn(VERR_INTERNAL_ERROR_3);
+ }
+}
+
+
+RTDECL(int) RTDirRead(RTDIR hDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
+{
+ PRTDIRINTERNAL pDir = hDir;
+ int rc;
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pDir, VERR_INVALID_POINTER);
+ AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ cbDirEntry = *pcbDirEntry;
+ AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRY, szName[2]),
+ ("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRY, szName[2])),
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ rc = rtDirNtFetchMore(pDir);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ rc = rtDirNtConvertCurName(pDir);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRY, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired > cbDirEntry)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Setup the returned data.
+ */
+ pDirEntry->cbName = (uint16_t)cchName; Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+
+ pDirEntry->INodeId = pDir->enmInfoClass == FileIdBothDirectoryInformation
+ ? pDir->uCurData.pBothId->FileId.QuadPart : 0;
+
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pDir->enmInfoClass != FileMaximumInformation)
+#endif
+ {
+ switch ( pDir->uCurData.pBoth->FileAttributes
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY))
+ {
+ default:
+ AssertFailed();
+ case 0:
+ pDirEntry->enmType = RTDIRENTRYTYPE_FILE;
+ break;
+
+ case FILE_ATTRIBUTE_DIRECTORY:
+ pDirEntry->enmType = RTDIRENTRYTYPE_DIRECTORY;
+ break;
+
+ case FILE_ATTRIBUTE_REPARSE_POINT:
+ case FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY:
+ /* EaSize is here reused for returning the repharse tag value. */
+ if (pDir->uCurData.pBoth->EaSize == IO_REPARSE_TAG_SYMLINK)
+ pDirEntry->enmType = RTDIRENTRYTYPE_SYMLINK;
+ break;
+ }
+ }
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ else
+ {
+ pDirEntry->enmType = RTDIRENTRYTYPE_UNKNOWN;
+ if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
+ RT_STR_TUPLE("Directory")))
+ pDirEntry->enmType = RTDIRENTRYTYPE_DIRECTORY;
+ else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
+ RT_STR_TUPLE("SymbolicLink")))
+ pDirEntry->enmType = RTDIRENTRYTYPE_SYMLINK;
+ }
+#endif
+
+ return rtDirNtAdvanceBuffer(pDir);
+}
+
+
+RTDECL(int) RTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
+ RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pDir = hDir;
+ int rc;
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pDir, VERR_INVALID_POINTER);
+ AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
+
+ AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ cbDirEntry = *pcbDirEntry;
+ AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRYEX, szName[2]),
+ ("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])),
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ rc = rtDirNtFetchMore(pDir);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ rc = rtDirNtConvertCurName(pDir);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired > cbDirEntry)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Setup the returned data.
+ */
+ PFILE_BOTH_DIR_INFORMATION pBoth = pDir->uCurData.pBoth;
+
+ pDirEntry->cbName = (uint16_t)cchName; Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+ memset(pDirEntry->wszShortName, 0, sizeof(pDirEntry->wszShortName));
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pDir->enmInfoClass != FileMaximumInformation)
+#endif
+ {
+ uint8_t cbShort = pBoth->ShortNameLength;
+ if (cbShort > 0)
+ {
+ AssertStmt(cbShort < sizeof(pDirEntry->wszShortName), cbShort = sizeof(pDirEntry->wszShortName) - 2);
+ memcpy(pDirEntry->wszShortName, pBoth->ShortName, cbShort);
+ pDirEntry->cwcShortName = cbShort / 2;
+ }
+ else
+ pDirEntry->cwcShortName = 0;
+
+ pDirEntry->Info.cbObject = pBoth->EndOfFile.QuadPart;
+ pDirEntry->Info.cbAllocated = pBoth->AllocationSize.QuadPart;
+
+ Assert(sizeof(uint64_t) == sizeof(pBoth->CreationTime));
+ RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, pBoth->CreationTime.QuadPart);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, pBoth->LastAccessTime.QuadPart);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, pBoth->LastWriteTime.QuadPart);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ChangeTime, pBoth->ChangeTime.QuadPart);
+
+ pDirEntry->Info.Attr.fMode = rtFsModeFromDos((pBoth->FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszName, cchName, pBoth->EaSize, 0);
+ }
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ else
+ {
+ pDirEntry->cwcShortName = 0;
+ pDirEntry->Info.cbObject = 0;
+ pDirEntry->Info.cbAllocated = 0;
+ RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, 0);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, 0);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, 0);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ChangeTime, 0);
+
+ if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
+ RT_STR_TUPLE("Directory")))
+ pDirEntry->Info.Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
+ else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
+ RT_STR_TUPLE("SymbolicLink")))
+ pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_REPARSE_POINT | RTFS_TYPE_SYMLINK | 0777;
+ else if (rtNtCompWideStrAndAscii(pDir->uCurData.pObjDir->TypeName.Buffer, pDir->uCurData.pObjDir->TypeName.Length,
+ RT_STR_TUPLE("Device")))
+ pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_DEVICE | RTFS_TYPE_DEV_CHAR | 0666;
+ else
+ pDirEntry->Info.Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0666;
+ }
+#endif
+
+ /*
+ * Requested attributes (we cannot provide anything actually).
+ */
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_EASIZE:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if (pDir->enmInfoClass == FileMaximumInformation)
+ pDirEntry->Info.Attr.u.EASize.cb = 0;
+ else
+#endif
+ pDirEntry->Info.Attr.u.EASize.cb = pBoth->EaSize;
+ break;
+
+ case RTFSOBJATTRADD_UNIX:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pDirEntry->Info.Attr.u.Unix.uid = NIL_RTUID;
+ pDirEntry->Info.Attr.u.Unix.gid = NIL_RTGID;
+ pDirEntry->Info.Attr.u.Unix.cHardlinks = 1;
+ pDirEntry->Info.Attr.u.Unix.INodeIdDevice = pDir->uDirDev;
+ pDirEntry->Info.Attr.u.Unix.INodeId = 0;
+ if ( pDir->enmInfoClass == FileIdBothDirectoryInformation
+ && pDir->uCurData.pBothId->FileId.QuadPart != UINT64_MAX)
+ pDirEntry->Info.Attr.u.Unix.INodeId = pDir->uCurData.pBothId->FileId.QuadPart;
+ pDirEntry->Info.Attr.u.Unix.fFlags = 0;
+ pDirEntry->Info.Attr.u.Unix.GenerationId = 0;
+ pDirEntry->Info.Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_NOTHING:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pDirEntry->Info.Attr.u.UnixOwner.uid = NIL_RTUID;
+ pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pDirEntry->Info.Attr.u.UnixGroup.gid = NIL_RTGID;
+ pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ /*
+ * Follow links if requested.
+ */
+ if ( (fFlags & RTPATH_F_FOLLOW_LINK)
+ && RTFS_IS_SYMLINK(fFlags))
+ {
+ /** @todo Symlinks: Find[First|Next]FileW will return info about
+ the link, so RTPATH_F_FOLLOW_LINK is not handled correctly. */
+ }
+
+ /*
+ * Finally advance the buffer.
+ */
+ return rtDirNtAdvanceBuffer(pDir);
+}
+
+
+RTDECL(int) RTDirRewind(RTDIR hDir)
+{
+ /*
+ * Validate and digest input.
+ */
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * The work is done on the next call to rtDirNtFetchMore.
+ */
+ pThis->fRestartScan = true;
+ pThis->fDataUnread = false;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTDirQueryInfo(RTDIR hDir, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ PRTDIRINTERNAL pDir = hDir;
+ AssertPtrReturn(pDir, VERR_INVALID_POINTER);
+ AssertReturn(pDir->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+ VERR_INVALID_PARAMETER);
+
+ if (pDir->enmInfoClass == FileMaximumInformation)
+ {
+ /*
+ * Directory object (see similar code above and rtPathNtQueryInfoInDirectoryObject).
+ */
+ pObjInfo->cbObject = 0;
+ pObjInfo->cbAllocated = 0;
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, 0);
+ RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, 0);
+ pObjInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY | 0777;
+ pObjInfo->Attr.enmAdditional = enmAdditionalAttribs;
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
+ pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
+ pObjInfo->Attr.u.Unix.cHardlinks = 1;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = pDir->uDirDev;
+ pObjInfo->Attr.u.Unix.INodeId = 0;
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR_2;
+ }
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Regular directory file.
+ */
+ uint8_t abBuf[_2K];
+ return rtPathNtQueryInfoFromHandle(pDir->hDir, abBuf, sizeof(abBuf), pObjInfo, enmAdditionalAttribs, "", 0);
+}
+
diff --git a/src/VBox/Runtime/r3/nt/dirrel-r3-nt.cpp b/src/VBox/Runtime/r3/nt/dirrel-r3-nt.cpp
new file mode 100644
index 00000000..a4130725
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/dirrel-r3-nt.cpp
@@ -0,0 +1,651 @@
+/* $Id: dirrel-r3-nt.cpp $ */
+/** @file
+ * IPRT - Directory relative base APIs, NT implementation
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/dir.h>
+#include "internal-r3-nt.h"
+
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/symlink.h>
+#include <iprt/utf16.h>
+#include "internal/dir.h"
+#include "internal/file.h"
+#include "internal/fs.h"
+#include "internal/path.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Getst the RTNTPATHRELATIVEASCENT value for RTNtPathRelativeFromUtf8. */
+#define RTDIRREL_NT_GET_ASCENT(a_pThis) \
+ ( !(pThis->fFlags & RTDIR_F_DENY_ASCENT) ? kRTNtPathRelativeAscent_Allow : kRTNtPathRelativeAscent_Fail )
+
+
+
+/**
+ * Helper that builds a full path for a directory relative path.
+ *
+ * @returns IPRT status code.
+ * @param pThis The directory.
+ * @param pszPathDst The destination buffer.
+ * @param cbPathDst The size of the destination buffer.
+ * @param pszRelPath The relative path.
+ */
+static int rtDirRelBuildFullPath(PRTDIRINTERNAL pThis, char *pszPathDst, size_t cbPathDst, const char *pszRelPath)
+{
+ AssertMsgReturn(!RTPathStartsWithRoot(pszRelPath), ("pszRelPath='%s'\n", pszRelPath), VERR_PATH_IS_NOT_RELATIVE);
+
+ /*
+ * Let's hope we can avoid checking for ascension.
+ *
+ * Note! We don't take symbolic links into account here. That can be
+ * done later if desired.
+ */
+ if ( !(pThis->fFlags & RTDIR_F_DENY_ASCENT)
+ || strstr(pszRelPath, "..") == NULL)
+ {
+ size_t const cchRelPath = strlen(pszRelPath);
+ size_t const cchDirPath = pThis->cchPath;
+ if (cchDirPath + cchRelPath < cbPathDst)
+ {
+ memcpy(pszPathDst, pThis->pszPath, cchDirPath);
+ memcpy(&pszPathDst[cchDirPath], pszRelPath, cchRelPath);
+ pszPathDst[cchDirPath + cchRelPath] = '\0';
+ return VINF_SUCCESS;
+ }
+ return VERR_FILENAME_TOO_LONG;
+ }
+
+ /*
+ * Calc the absolute path using the directory as a base, then check if the result
+ * still starts with the full directory path.
+ *
+ * This ASSUMES that pThis->pszPath is an absolute path.
+ */
+ int rc = RTPathAbsEx(pThis->pszPath, pszRelPath, RTPATH_STR_F_STYLE_HOST, pszPathDst, &cbPathDst);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTPathStartsWith(pszPathDst, pThis->pszPath))
+ return VINF_SUCCESS;
+ return VERR_PATH_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/*
+ *
+ *
+ * RTFile stuff.
+ * RTFile stuff.
+ * RTFile stuff.
+ *
+ *
+ */
+
+
+RTDECL(int) RTDirRelFileOpen(RTDIR hDir, const char *pszRelFilename, uint64_t fOpen, PRTFILE phFile)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Validate and convert flags.
+ */
+ uint32_t fDesiredAccess;
+ uint32_t fObjAttribs;
+ uint32_t fFileAttribs;
+ uint32_t fShareAccess;
+ uint32_t fCreateDisposition;
+ uint32_t fCreateOptions;
+ int rc = rtFileNtValidateAndConvertFlags(fOpen, &fDesiredAccess, &fObjAttribs, &fFileAttribs,
+ &fShareAccess, &fCreateDisposition, &fCreateOptions);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelFilename, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, fObjAttribs, hRoot, NULL /*pSecDesc*/);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ fDesiredAccess,
+ &ObjAttr,
+ &Ios,
+ NULL /* AllocationSize*/,
+ fFileAttribs,
+ fShareAccess,
+ fCreateDisposition,
+ fCreateOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ rc = RTFileFromNative(phFile, (uintptr_t)hFile);
+ if (RT_FAILURE(rc))
+ NtClose(hFile);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ RTNtPathFree(&NtName, NULL);
+ }
+ }
+ return rc;
+}
+
+
+
+/*
+ *
+ *
+ * RTDir stuff.
+ * RTDir stuff.
+ * RTDir stuff.
+ *
+ *
+ */
+
+
+/**
+ * Helper for cooking up a path string for rtDirOpenRelativeOrHandle.
+ *
+ * @returns IPRT status code.
+ * @param pszDst The destination buffer.
+ * @param cbDst The size of the destination buffer.
+ * @param pThis The directory this is relative to.
+ * @param pNtPath The NT path with a possibly relative path.
+ * @param fRelative Whether @a pNtPath is relative or not.
+ * @param pszPath The input path.
+ */
+static int rtDirRelJoinPathForDirOpen(char *pszDst, size_t cbDst, PRTDIRINTERNAL pThis,
+ PUNICODE_STRING pNtPath, bool fRelative, const char *pszPath)
+{
+ int rc;
+ if (fRelative)
+ {
+ size_t cchRel = 0;
+ rc = RTUtf16CalcUtf8LenEx(pNtPath->Buffer, pNtPath->Length / sizeof(RTUTF16), &cchRel);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->cchPath + cchRel < cbDst)
+ {
+ size_t cchBase = pThis->cchPath;
+ memcpy(pszDst, pThis->pszPath, cchBase);
+ pszDst += cchBase;
+ cbDst -= cchBase;
+ rc = RTUtf16ToUtf8Ex(pNtPath->Buffer, pNtPath->Length / sizeof(RTUTF16), &pszDst, cbDst, NULL);
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ }
+ else
+ {
+ /** @todo would be better to convert pNtName to DOS/WIN path here,
+ * as it is absolute and doesn't need stuff resolved. */
+ rc = RTPathJoin(pszDst, cbDst, pThis->pszPath, pszPath);
+ }
+ return rc;
+}
+
+RTDECL(int) RTDirRelDirOpen(RTDIR hDir, const char *pszDir, RTDIR *phDir)
+{
+ return RTDirRelDirOpenFiltered(hDir, pszDir, RTDIRFILTER_NONE, 0 /*fFlags*/, phDir);
+}
+
+
+RTDECL(int) RTDirRelDirOpenFiltered(RTDIR hDir, const char *pszDirAndFilter, RTDIRFILTER enmFilter,
+ uint32_t fFlags, RTDIR *phDir)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszDirAndFilter, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ char szAbsDirAndFilter[RTPATH_MAX];
+ rc = rtDirRelJoinPathForDirOpen(szAbsDirAndFilter, sizeof(szAbsDirAndFilter), pThis,
+ &NtName, hRoot != NULL, pszDirAndFilter);
+ if (RT_SUCCESS(rc))
+ {
+ /* Drop the filter from the NT name. */
+ switch (enmFilter)
+ {
+ case RTDIRFILTER_NONE:
+ break;
+ case RTDIRFILTER_WINNT:
+ case RTDIRFILTER_UNIX:
+ case RTDIRFILTER_UNIX_UPCASED:
+ {
+ size_t cwc = NtName.Length / sizeof(RTUTF16);
+ while ( cwc > 0
+ && NtName.Buffer[cwc - 1] != '\\')
+ cwc--;
+ NtName.Buffer[cwc] = '\0';
+ NtName.Length = (uint16_t)(cwc * sizeof(RTUTF16));
+ break;
+ }
+ default:
+ AssertFailedBreak();
+ }
+
+ rc = rtDirOpenRelativeOrHandle(phDir, szAbsDirAndFilter, enmFilter, fFlags, (uintptr_t)hRoot, &NtName);
+ }
+ RTNtPathFree(&NtName, NULL);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelDirCreate(RTDIR hDir, const char *pszRelPath, RTFMODE fMode, uint32_t fCreate, RTDIR *phSubDir)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fCreate & ~RTDIRCREATE_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
+ fMode = rtFsModeNormalize(fMode, pszRelPath, 0, RTFS_TYPE_DIRECTORY);
+ AssertReturn(rtFsModeIsValidPermissions(fMode), VERR_INVALID_FMODE);
+ AssertPtrNullReturn(phSubDir, VERR_INVALID_POINTER);
+
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelPath, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hNewDir = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, 0 /*fAttrib*/, hRoot, NULL);
+
+ ULONG fDirAttribs = (fCreate & RTFS_DOS_MASK_NT) >> RTFS_DOS_SHIFT;
+ if (!(fCreate & RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET))
+ fDirAttribs |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ if (!fDirAttribs)
+ fDirAttribs = FILE_ATTRIBUTE_NORMAL;
+
+ NTSTATUS rcNt = NtCreateFile(&hNewDir,
+ phSubDir
+ ? FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE
+ : SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*AllocationSize*/,
+ fDirAttribs,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_CREATE,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+
+ /* Just in case someone takes offence at FILE_ATTRIBUTE_NOT_CONTENT_INDEXED. */
+ if ( ( rcNt == STATUS_INVALID_PARAMETER
+ || rcNt == STATUS_INVALID_PARAMETER_7)
+ && (fDirAttribs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
+ && (fCreate & RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL) )
+ {
+ fDirAttribs &= ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ if (!fDirAttribs)
+ fDirAttribs = FILE_ATTRIBUTE_NORMAL;
+ rcNt = NtCreateFile(&hNewDir,
+ phSubDir
+ ? FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE
+ : SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*AllocationSize*/,
+ fDirAttribs,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_CREATE,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ }
+
+ if (NT_SUCCESS(rcNt))
+ {
+ if (!phSubDir)
+ {
+ NtClose(hNewDir);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ char szAbsDirAndFilter[RTPATH_MAX];
+ rc = rtDirRelJoinPathForDirOpen(szAbsDirAndFilter, sizeof(szAbsDirAndFilter), pThis,
+ &NtName, hRoot != NULL, pszRelPath);
+ if (RT_SUCCESS(rc))
+ rc = rtDirOpenRelativeOrHandle(phSubDir, pszRelPath, RTDIRFILTER_NONE, 0 /*fFlags*/,
+ (uintptr_t)hNewDir, NULL /*pvNativeRelative*/);
+ if (RT_FAILURE(rc))
+ NtClose(hNewDir);
+ }
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ RTNtPathFree(&NtName, NULL);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelDirRemove(RTDIR hDir, const char *pszRelPath)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelPath, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hSubDir = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, 0 /*fAttrib*/, hRoot, NULL);
+
+ NTSTATUS rcNt = NtCreateFile(&hSubDir,
+ DELETE | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ FILE_DISPOSITION_INFORMATION DispInfo;
+ DispInfo.DeleteFile = TRUE;
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtSetInformationFile(hSubDir, &Ios, &DispInfo, sizeof(DispInfo), FileDispositionInformation);
+
+ NTSTATUS rcNt2 = NtClose(hSubDir);
+ if (!NT_SUCCESS(rcNt2) && NT_SUCCESS(rcNt))
+ rcNt = rcNt2;
+ }
+
+ if (NT_SUCCESS(rcNt))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ RTNtPathFree(&NtName, NULL);
+ }
+ return rc;
+}
+
+
+/*
+ *
+ * RTPath stuff.
+ * RTPath stuff.
+ * RTPath stuff.
+ *
+ *
+ */
+
+
+RTDECL(int) RTDirRelPathQueryInfo(RTDIR hDir, const char *pszRelPath, PRTFSOBJINFO pObjInfo,
+ RTFSOBJATTRADD enmAddAttr, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Validate and convert flags.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelPath, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ if (NtName.Length != 0 || hRoot == NULL)
+ rc = rtPathNtQueryInfoWorker(hRoot, &NtName, pObjInfo, enmAddAttr, fFlags, pszRelPath);
+ else
+ rc = RTDirQueryInfo(hDir, pObjInfo, enmAddAttr);
+ RTNtPathFree(&NtName, NULL);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathSetMode(RTDIR hDir, const char *pszRelPath, RTFMODE fMode, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ fMode = rtFsModeNormalize(fMode, pszRelPath, 0, 0);
+ AssertReturn(rtFsModeIsValidPermissions(fMode), VERR_INVALID_FMODE);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_FLAGS);
+
+ /*
+ * Convert and normalize the path.
+ */
+ UNICODE_STRING NtName;
+ HANDLE hRoot = pThis->hDir;
+ int rc = RTNtPathRelativeFromUtf8(&NtName, &hRoot, pszRelPath, RTDIRREL_NT_GET_ASCENT(pThis),
+ pThis->enmInfoClass == FileMaximumInformation);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hSubDir = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, 0 /*fAttrib*/, hRoot, NULL);
+
+ ULONG fOpenOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT;
+ if (fFlags & RTPATH_F_ON_LINK)
+ fOpenOptions |= FILE_OPEN_REPARSE_POINT;
+ NTSTATUS rcNt = NtCreateFile(&hSubDir,
+ FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
+ &ObjAttr,
+ &Ios,
+ NULL /*AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ fOpenOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ rc = rtNtFileSetModeWorker(hSubDir, fMode);
+
+ rcNt = NtClose(hSubDir);
+ if (!NT_SUCCESS(rcNt) && RT_SUCCESS(rc))
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ RTNtPathFree(&NtName, NULL);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathSetTimes(RTDIR hDir, const char *pszRelPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ rc = RTPathSetTimesEx(szPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, fFlags);
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathSetOwner(RTDIR hDir, const char *pszRelPath, uint32_t uid, uint32_t gid, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef RT_OS_WINDOWS
+ rc = RTPathSetOwnerEx(szPath, uid, gid, fFlags);
+#else
+ rc = VERR_NOT_IMPLEMENTED;
+ RT_NOREF(uid, gid, fFlags);
+#endif
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathRename(RTDIR hDirSrc, const char *pszSrc, RTDIR hDirDst, const char *pszDst, unsigned fRename)
+{
+ PRTDIRINTERNAL pThis = hDirSrc;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ PRTDIRINTERNAL pThat = hDirDst;
+ if (pThat != pThis)
+ {
+ AssertPtrReturn(pThat, VERR_INVALID_HANDLE);
+ AssertReturn(pThat->u32Magic != RTDIR_MAGIC, VERR_INVALID_HANDLE);
+ }
+
+ char szSrcPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szSrcPath, sizeof(szSrcPath), pszSrc);
+ if (RT_SUCCESS(rc))
+ {
+ char szDstPath[RTPATH_MAX];
+ rc = rtDirRelBuildFullPath(pThis, szDstPath, sizeof(szDstPath), pszDst);
+ if (RT_SUCCESS(rc))
+ rc = RTPathRename(szSrcPath, szDstPath, fRename);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelPathUnlink(RTDIR hDir, const char *pszRelPath, uint32_t fUnlink)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszRelPath);
+ if (RT_SUCCESS(rc))
+ rc = RTPathUnlink(szPath, fUnlink);
+ return rc;
+}
+
+
+/*
+ *
+ * RTSymlink stuff.
+ * RTSymlink stuff.
+ * RTSymlink stuff.
+ *
+ *
+ */
+
+
+RTDECL(int) RTDirRelSymlinkCreate(RTDIR hDir, const char *pszSymlink, const char *pszTarget,
+ RTSYMLINKTYPE enmType, uint32_t fCreate)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszSymlink);
+ if (RT_SUCCESS(rc))
+ rc = RTSymlinkCreate(szPath, pszTarget, enmType, fCreate);
+ return rc;
+}
+
+
+RTDECL(int) RTDirRelSymlinkRead(RTDIR hDir, const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead)
+{
+ PRTDIRINTERNAL pThis = hDir;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTDIR_MAGIC, VERR_INVALID_HANDLE);
+
+ char szPath[RTPATH_MAX];
+ int rc = rtDirRelBuildFullPath(pThis, szPath, sizeof(szPath), pszSymlink);
+ if (RT_SUCCESS(rc))
+ rc = RTSymlinkRead(szPath, pszTarget, cbTarget, fRead);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/nt/fs-nt.cpp b/src/VBox/Runtime/r3/nt/fs-nt.cpp
new file mode 100644
index 00000000..70add1ee
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/fs-nt.cpp
@@ -0,0 +1,279 @@
+/* $Id: fs-nt.cpp $ */
+/** @file
+ * IPRT - File System, Native NT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include "internal-r3-nt.h"
+
+#include <iprt/fs.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/param.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include "internal/fs.h"
+
+
+
+
+RTR3DECL(int) RTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree,
+ uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+
+ /*
+ * Open the file/dir/whatever.
+ */
+ HANDLE hFile;
+ int rc = RTNtPathOpen(pszFsPath,
+ GENERIC_READ,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT,
+ OBJ_CASE_INSENSITIVE,
+ &hFile,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTFILE hIprtFile = NIL_RTFILE;
+ rc = RTFileFromNative(&hIprtFile, (RTHCINTPTR)hFile);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ rc = RTFileQueryFsSizes(hIprtFile, pcbTotal, pcbFree, pcbBlock, pcbSector);
+
+ RTNtPathClose(hFile);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial)
+{
+ /*
+ * Validate & get valid root path.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertPtrReturn(pu32Serial, VERR_INVALID_POINTER);
+
+ /*
+ * Open the file/dir/whatever.
+ */
+ HANDLE hFile;
+ int rc = RTNtPathOpen(pszFsPath,
+ GENERIC_READ,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT,
+ OBJ_CASE_INSENSITIVE,
+ &hFile,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the volume information.
+ */
+ union
+ {
+ FILE_FS_VOLUME_INFORMATION FsVolInfo;
+ uint8_t abBuf[sizeof(FILE_FS_VOLUME_INFORMATION) + 4096];
+ } u;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &u, sizeof(u), FileFsVolumeInformation);
+ if (NT_SUCCESS(rcNt))
+ *pu32Serial = u.FsVolInfo.VolumeSerialNumber;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ RTNtPathClose(hFile);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties)
+{
+ /*
+ * Validate & get valid root path.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertPtrReturn(pProperties, VERR_INVALID_POINTER);
+
+ /*
+ * Open the file/dir/whatever.
+ */
+ HANDLE hFile;
+ int rc = RTNtPathOpen(pszFsPath,
+ GENERIC_READ,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT,
+ OBJ_CASE_INSENSITIVE,
+ &hFile,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the volume information.
+ */
+ union
+ {
+ FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
+ uint8_t abBuf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 4096];
+ } u;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &u, sizeof(u), FileFsAttributeInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ FILE_FS_DEVICE_INFORMATION FsDevInfo;
+ rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &FsDevInfo, sizeof(FsDevInfo), FileFsDeviceInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Fill in the return structure.
+ */
+ memset(pProperties, 0, sizeof(*pProperties));
+ pProperties->cbMaxComponent = u.FsAttrInfo.MaximumComponentNameLength;
+ pProperties->fFileCompression = !!(u.FsAttrInfo.FileSystemAttributes & FILE_FILE_COMPRESSION);
+ pProperties->fCompressed = !!(u.FsAttrInfo.FileSystemAttributes & FILE_VOLUME_IS_COMPRESSED);
+ pProperties->fReadOnly = !!(u.FsAttrInfo.FileSystemAttributes & FILE_READ_ONLY_VOLUME);
+ pProperties->fSupportsUnicode = !!(u.FsAttrInfo.FileSystemAttributes & FILE_UNICODE_ON_DISK);
+ pProperties->fCaseSensitive = false; /* win32 is case preserving only */
+ /** @todo r=bird: What about FILE_CASE_SENSITIVE_SEARCH ? Is this set for NTFS
+ * as well perchance? If so, better mention it instead of just setting
+ * fCaseSensitive to false. */
+
+ /* figure the remote stuff */
+ pProperties->fRemote = RT_BOOL(FsDevInfo.Characteristics & FILE_REMOTE_DEVICE);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ RTNtPathClose(hFile);
+ }
+ return rc;
+}
+
+
+RTR3DECL(bool) RTFsIsCaseSensitive(const char *pszFsPath)
+{
+ RT_NOREF_PV(pszFsPath);
+ return false;
+}
+
+
+int rtNtQueryFsType(HANDLE hHandle, PRTFSTYPE penmType)
+{
+ /*
+ * Get the file system name.
+ */
+ union
+ {
+ FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo;
+ uint8_t abBuf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 4096];
+ } u;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(hHandle, &Ios, &u, sizeof(u), FileFsAttributeInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+#define IS_FS(a_szName) rtNtCompWideStrAndAscii(u.FsAttrInfo.FileSystemName, u.FsAttrInfo.FileSystemNameLength, RT_STR_TUPLE(a_szName))
+ if (IS_FS("NTFS"))
+ *penmType = RTFSTYPE_NTFS;
+ else if (IS_FS("FAT"))
+ *penmType = RTFSTYPE_FAT;
+ else if (IS_FS("FAT32"))
+ *penmType = RTFSTYPE_FAT;
+ else if (IS_FS("exFAT"))
+ *penmType = RTFSTYPE_EXFAT;
+ else if (IS_FS("UDF"))
+ *penmType = RTFSTYPE_UDF;
+ else if (IS_FS("CDFS"))
+ *penmType = RTFSTYPE_ISO9660;
+ else if (IS_FS("HPFS"))
+ *penmType = RTFSTYPE_HPFS;
+ else if (IS_FS("ReFS")) /** @todo verify ReFS signature. */
+ *penmType = RTFSTYPE_REFS;
+ else if (IS_FS("VBoxSharedFolderFS"))
+ *penmType = RTFSTYPE_VBOXSHF;
+#undef IS_FS
+ return VINF_SUCCESS;
+ }
+
+ *penmType = RTFSTYPE_UNKNOWN;
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+RTR3DECL(int) RTFsQueryType(const char *pszFsPath, PRTFSTYPE penmType)
+{
+ /*
+ * Validate input.
+ */
+ *penmType = RTFSTYPE_UNKNOWN;
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath, VERR_INVALID_PARAMETER);
+
+ /*
+ * Open the file/dir/whatever.
+ */
+ HANDLE hFile;
+ int rc = RTNtPathOpen(pszFsPath,
+ GENERIC_READ,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT,
+ OBJ_CASE_INSENSITIVE,
+ &hFile,
+ NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtNtQueryFsType(hFile, penmType);
+ RTNtPathClose(hFile);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/nt/internal-r3-nt.h b/src/VBox/Runtime/r3/nt/internal-r3-nt.h
new file mode 100644
index 00000000..c76a3f6b
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/internal-r3-nt.h
@@ -0,0 +1,92 @@
+/* $Id: internal-r3-nt.h $ */
+/** @file
+ * IPRT - Internal Header for the Native NT code.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_nt_internal_r3_nt_h
+#define IPRT_INCLUDED_SRC_r3_nt_internal_r3_nt_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef IN_SUP_HARDENED_R3
+# include <iprt/nt/nt-and-windows.h>
+#else
+# include <iprt/nt/nt.h>
+#endif
+#include "internal/iprt.h"
+
+
+#if 1
+/** Enables the "\\!\" NT path pass thru as well as hacks for listing NT object
+ * directories. */
+# define IPRT_WITH_NT_PATH_PASSTHRU 1
+#endif
+
+
+
+/**
+ * Internal helper for comparing a WCHAR string with a char string.
+ *
+ * @returns @c true if equal, @c false if not.
+ * @param pwsz1 The first string.
+ * @param cch1 The length of the first string, in bytes.
+ * @param psz2 The second string.
+ * @param cch2 The length of the second string.
+ */
+DECLINLINE(bool) rtNtCompWideStrAndAscii(WCHAR const *pwsz1, size_t cch1, const char *psz2, size_t cch2)
+{
+ if (cch1 != cch2 * 2)
+ return false;
+ while (cch2-- > 0)
+ {
+ unsigned ch1 = *pwsz1++;
+ unsigned ch2 = (unsigned char)*psz2++;
+ if (ch1 != ch2)
+ return false;
+ }
+ return true;
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r3_nt_internal_r3_nt_h */
+
+/**
+ * Common worker for RTFileSetMode, RTPathSetMode and RTDirRelPathSetMode.
+ *
+ * @returns IPRT status code.
+ * @param hNativeFile The NT handle to the file system object.
+ * @param fMode Valid and normalized file mode mask to set.
+ */
+DECLHIDDEN(int) rtNtFileSetModeWorker(HANDLE hNativeFile, RTFMODE fMode);
+
diff --git a/src/VBox/Runtime/r3/nt/pathint-nt.cpp b/src/VBox/Runtime/r3/nt/pathint-nt.cpp
new file mode 100644
index 00000000..95281ca7
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/pathint-nt.cpp
@@ -0,0 +1,1161 @@
+/* $Id: pathint-nt.cpp $ */
+/** @file
+ * IPRT - Native NT, Internal Path stuff.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include "internal-r3-nt.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static char const g_szPrefixUnc[] = "\\??\\UNC\\";
+static char const g_szPrefix[] = "\\??\\";
+static char const g_szPrefixNt3xUnc[] = "\\DosDevices\\UNC\\";
+static char const g_szPrefixNt3x[] = "\\DosDevices\\";
+
+
+/**
+ * Handles the pass thru case for UTF-8 input.
+ * Win32 path uses "\\?\" prefix which is converted to "\??\" NT prefix.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name.
+ * @param phRootDir Where to return the root handle, if applicable.
+ * @param pszPath The UTF-8 path.
+ */
+static int rtNtPathFromWinUtf8PassThru(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
+{
+ PRTUTF16 pwszPath = NULL;
+ size_t cwcLen;
+ int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, &pwszPath, 0, &cwcLen);
+ if (RT_SUCCESS(rc))
+ {
+ if (cwcLen < _32K - 1)
+ {
+ *phRootDir = NULL;
+ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) >= RT_MAKE_U64(0, 4))
+ {
+ pwszPath[0] = '\\';
+ pwszPath[1] = '?';
+ pwszPath[2] = '?';
+ pwszPath[3] = '\\';
+
+ pNtName->Buffer = pwszPath;
+ pNtName->Length = (uint16_t)(cwcLen * sizeof(RTUTF16));
+ pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
+ return VINF_SUCCESS;
+ }
+
+ rc = RTUtf16Realloc(&pwszPath, cwcLen + sizeof(g_szPrefixNt3x));
+ if (RT_SUCCESS(rc))
+ {
+ memmove(&pwszPath[sizeof(g_szPrefixNt3x) - 1], &pwszPath[4], (cwcLen - 4 + 1) * sizeof(RTUTF16));
+ for (uint32_t i = 0; i < sizeof(g_szPrefixNt3x) - 1; i++)
+ pwszPath[i] = g_szPrefixNt3x[i];
+
+ pNtName->Buffer = pwszPath;
+ pNtName->Length = (uint16_t)((cwcLen - 4 + sizeof(g_szPrefixNt3x) - 1) * sizeof(RTUTF16));
+ pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
+ return VINF_SUCCESS;
+ }
+ }
+
+ RTUtf16Free(pwszPath);
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ return rc;
+}
+
+
+/**
+ * Handles the pass thru case for UTF-16 input.
+ * Win32 path uses "\\?\" prefix which is converted to "\??\" NT prefix.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name.
+ * @param phRootDir Stores NULL here, as we don't use it.
+ * @param pwszWinPath The UTF-16 windows-style path.
+ * @param cwcWinPath The length of the windows-style input path.
+ */
+static int rtNtPathFromWinUtf16PassThru(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir,
+ PCRTUTF16 pwszWinPath, size_t cwcWinPath)
+{
+ /* Check length and allocate memory for it. */
+ int rc;
+ if (cwcWinPath < _32K - 1)
+ {
+
+ size_t const cwcExtraPrefix = RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion)
+ >= RT_MAKE_U64(0, 4)
+ ? 0 : sizeof(g_szPrefixNt3x) - 1 - 4;
+ PRTUTF16 pwszNtPath = (PRTUTF16)RTUtf16Alloc((cwcExtraPrefix + cwcWinPath + 1) * sizeof(RTUTF16));
+ if (pwszNtPath)
+ {
+ /* Intialize the path. */
+ if (!cwcExtraPrefix)
+ {
+ pwszNtPath[0] = '\\';
+ pwszNtPath[1] = '?';
+ pwszNtPath[2] = '?';
+ pwszNtPath[3] = '\\';
+ }
+ else
+ for (uint32_t i = 0; i < sizeof(g_szPrefixNt3x) - 1; i++)
+ pwszNtPath[i] = g_szPrefixNt3x[i];
+ memcpy(pwszNtPath + cwcExtraPrefix + 4, pwszWinPath + 4, (cwcWinPath - 4) * sizeof(RTUTF16));
+ pwszNtPath[cwcExtraPrefix + cwcWinPath] = '\0';
+
+ /* Initialize the return values. */
+ pNtName->Buffer = pwszNtPath;
+ pNtName->Length = (uint16_t)(cwcExtraPrefix + cwcWinPath * sizeof(RTUTF16));
+ pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
+ *phRootDir = NULL;
+
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_UTF16_MEMORY;
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ return rc;
+}
+
+
+
+
+
+/**
+ * Converts the path to UTF-16 and sets all the return values.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name.
+ * @param phRootDir Where to return the root handle, if applicable.
+ * @param pszPath The UTF-8 path.
+ */
+static int rtNtPathUtf8ToUniStr(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
+{
+ PRTUTF16 pwszPath = NULL;
+ size_t cwcLen;
+ int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, &pwszPath, 0, &cwcLen);
+ if (RT_SUCCESS(rc))
+ {
+ if (cwcLen < _32K - 1)
+ {
+ pNtName->Buffer = pwszPath;
+ pNtName->Length = (uint16_t)(cwcLen * sizeof(RTUTF16));
+ pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
+ *phRootDir = NULL;
+ return VINF_SUCCESS;
+ }
+
+ RTUtf16Free(pwszPath);
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ return rc;
+}
+
+
+/**
+ * Converts a windows-style path to NT format and encoding.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name. Free using
+ * rtTNtPathToNative.
+ * @param phRootDir Where to return the root handle, if applicable.
+ * @param pszPath The UTF-8 path.
+ */
+static int rtNtPathToNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
+{
+/** @todo This code sucks a bit performance wise, esp. calling
+ * generic RTPathAbs. Too many buffers involved, I think. */
+
+ /*
+ * Very simple conversion of a win32-like path into an NT path.
+ */
+ const char *pszPrefix;
+ size_t cchPrefix;
+ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) >= RT_MAKE_U64(0, 4))
+ {
+ pszPrefix = g_szPrefix;
+ cchPrefix = sizeof(g_szPrefix) - 1;
+ }
+ else
+ {
+ pszPrefix = g_szPrefixNt3x;
+ cchPrefix = sizeof(g_szPrefixNt3x) - 1;
+ }
+
+ size_t cchSkip = 0;
+ if ( RTPATH_IS_SLASH(pszPath[0])
+ && RTPATH_IS_SLASH(pszPath[1])
+ && !RTPATH_IS_SLASH(pszPath[2])
+ && pszPath[2])
+ {
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ /*
+ * Special trick: The path starts with RTPATH_NT_PASSTHRU_PREFIX, we will skip past the bang and pass it thru.
+ */
+ if ( pszPath[2] == ':'
+ && pszPath[3] == 'i'
+ && pszPath[4] == 'p'
+ && pszPath[5] == 'r'
+ && pszPath[6] == 't'
+ && pszPath[7] == 'n'
+ && pszPath[8] == 't'
+ && pszPath[9] == ':'
+ && RTPATH_IS_SLASH(pszPath[10]))
+ return rtNtPathUtf8ToUniStr(pNtName, phRootDir, pszPath + 10);
+#endif
+
+ if ( pszPath[2] == '?'
+ && RTPATH_IS_SLASH(pszPath[3]))
+ return rtNtPathFromWinUtf8PassThru(pNtName, phRootDir, pszPath);
+
+ if ( pszPath[2] == '.'
+ && RTPATH_IS_SLASH(pszPath[3]))
+ {
+ /*
+ * Device path.
+ * Note! I suspect \\.\stuff\..\otherstuff may be handled differently by windows.
+ */
+ cchSkip = 4;
+ }
+ else
+ {
+ /* UNC */
+ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) >= RT_MAKE_U64(0, 4))
+ {
+ pszPrefix = g_szPrefixUnc;
+ cchPrefix = sizeof(g_szPrefixUnc) - 1;
+ }
+ else
+ {
+ pszPrefix = g_szPrefixNt3xUnc;
+ cchPrefix = sizeof(g_szPrefixNt3xUnc) - 1;
+ }
+ cchSkip = 2;
+ }
+ }
+
+ /*
+ * Straighten out all .. and uncessary . references and convert slashes.
+ */
+ char szAbsPathBuf[RTPATH_MAX];
+ size_t cbAbsPath = sizeof(szAbsPathBuf) - (cchPrefix - cchSkip);
+ char *pszAbsPath = szAbsPathBuf;
+ char *pszAbsPathFree = NULL;
+ int rc = RTPathAbsEx(NULL, pszPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ unsigned cTries = 8;
+ size_t cbAbsPathBuf = RTPATH_MAX;
+ for (;;)
+ {
+ cbAbsPathBuf = RT_MAX(RT_ALIGN_Z((cchPrefix - cchSkip) + cbAbsPath + 32, 64), cbAbsPathBuf + 256);
+ if (cTries == 1)
+ cbAbsPathBuf = RT_MAX(cbAbsPathBuf, RTPATH_BIG_MAX * 2);
+ pszAbsPathFree = pszAbsPath = (char *)RTMemTmpAlloc(cbAbsPathBuf);
+ if (!pszAbsPath)
+ return VERR_NO_TMP_MEMORY;
+
+ cbAbsPath = cbAbsPathBuf - (cchPrefix - cchSkip);
+ rc = RTPathAbsEx(NULL, pszPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
+ if (RT_SUCCESS(rc))
+ break;
+ RTMemTmpFree(pszAbsPathFree);
+ pszAbsPathFree = NULL;
+ if (rc != VERR_BUFFER_OVERFLOW)
+ return rc;
+ if (--cTries == 0)
+ return VERR_FILENAME_TOO_LONG;
+ }
+ }
+ else
+ return rc;
+
+ /*
+ * Add prefix and convert it to UTF16.
+ */
+ memcpy(pszAbsPath, pszPrefix, cchPrefix);
+ rc = rtNtPathUtf8ToUniStr(pNtName, phRootDir, pszAbsPath);
+
+ if (pszAbsPathFree)
+ RTMemTmpFree(pszAbsPathFree);
+ return rc;
+}
+
+
+/**
+ * Converts a windows-style path to NT format and encoding.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name. Free using
+ * RTNtPathToNative.
+ * @param phRootDir Where to return the root handle, if applicable.
+ * @param pszPath The UTF-8 path.
+ */
+RTDECL(int) RTNtPathFromWinUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
+{
+ return rtNtPathToNative(pNtName, phRootDir, pszPath);
+}
+
+
+/**
+ * Converts a UTF-16 windows-style path to NT format.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name. Free using
+ * RTNtPathFree.
+ * @param phRootDir Where to return the root handle, if applicable.
+ * @param pwszWinPath The UTF-16 windows-style path.
+ * @param cwcWinPath The max length of the windows-style path in
+ * RTUTF16 units. Use RTSTR_MAX if unknown and @a
+ * pwszWinPath is correctly terminated.
+ */
+RTDECL(int) RTNtPathFromWinUtf16Ex(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir, PCRTUTF16 pwszWinPath, size_t cwcWinPath)
+{
+ /*
+ * Validate the input, calculating the correct length.
+ */
+ if (cwcWinPath == 0 || *pwszWinPath == '\0')
+ return VERR_INVALID_NAME;
+
+ RTUtf16NLenEx(pwszWinPath, cwcWinPath, &cwcWinPath);
+ int rc = RTUtf16ValidateEncodingEx(pwszWinPath, cwcWinPath, 0);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Very simple conversion of a win32-like path into an NT path.
+ */
+ const char *pszPrefix = g_szPrefix;
+ size_t cchPrefix = sizeof(g_szPrefix) - 1;
+ size_t cchSkip = 0;
+
+ if ( RTPATH_IS_SLASH(pwszWinPath[0])
+ && cwcWinPath >= 3
+ && RTPATH_IS_SLASH(pwszWinPath[1])
+ && !RTPATH_IS_SLASH(pwszWinPath[2]) )
+ {
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ /*
+ * Special trick: The path starts with RTPATH_NT_PASSTHRU_PREFIX, we will skip past the bang and pass it thru.
+ */
+ if ( cwcWinPath >= sizeof(RTPATH_NT_PASSTHRU_PREFIX) - 1U
+ && pwszWinPath[2] == ':'
+ && pwszWinPath[3] == 'i'
+ && pwszWinPath[4] == 'p'
+ && pwszWinPath[5] == 'r'
+ && pwszWinPath[6] == 't'
+ && pwszWinPath[7] == 'n'
+ && pwszWinPath[8] == 't'
+ && pwszWinPath[9] == ':'
+ && RTPATH_IS_SLASH(pwszWinPath[10]) )
+ {
+ pwszWinPath += 10;
+ cwcWinPath -= 10;
+ if (cwcWinPath < _32K - 1)
+ {
+ PRTUTF16 pwszNtPath = RTUtf16Alloc((cwcWinPath + 1) * sizeof(RTUTF16));
+ if (pwszNtPath)
+ {
+ memcpy(pwszNtPath, pwszWinPath, cwcWinPath * sizeof(RTUTF16));
+ pwszNtPath[cwcWinPath] = '\0';
+ pNtName->Buffer = pwszNtPath;
+ pNtName->Length = (uint16_t)(cwcWinPath * sizeof(RTUTF16));
+ pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
+ *phRootDir = NULL;
+ return VINF_SUCCESS;
+ }
+ rc = VERR_NO_UTF16_MEMORY;
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ return rc;
+ }
+#endif
+
+ if ( pwszWinPath[2] == '?'
+ && cwcWinPath >= 4
+ && RTPATH_IS_SLASH(pwszWinPath[3]))
+ return rtNtPathFromWinUtf16PassThru(pNtName, phRootDir, pwszWinPath, cwcWinPath);
+
+ if ( pwszWinPath[2] == '.'
+ && cwcWinPath >= 4
+ && RTPATH_IS_SLASH(pwszWinPath[3]))
+ {
+ /*
+ * Device path.
+ * Note! I suspect \\.\stuff\..\otherstuff may be handled differently by windows.
+ */
+ cchSkip = 4;
+ }
+ else
+ {
+ /* UNC */
+ pszPrefix = g_szPrefixUnc;
+ cchPrefix = sizeof(g_szPrefixUnc) - 1;
+ cchSkip = 2;
+ }
+ }
+
+ /*
+ * Straighten out all .. and unnecessary . references and convert slashes.
+ */
+ /* UTF-16 -> UTF-8 (relative path) */
+ char szRelPath[RTPATH_MAX];
+ char *pszRelPathFree = NULL;
+ char *pszRelPath = szRelPath;
+ size_t cchRelPath;
+ rc = RTUtf16ToUtf8Ex(pwszWinPath, cwcWinPath, &pszRelPath, sizeof(szRelPath), &cchRelPath);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ pszRelPath = NULL;
+ rc = RTUtf16ToUtf8Ex(pwszWinPath, cwcWinPath, &pszRelPath, 0, &cchRelPath);
+ if (RT_SUCCESS(rc))
+ pszRelPathFree = pszRelPath;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /* Relative -> Absolute. */
+ char szAbsPathBuf[RTPATH_MAX];
+ char *pszAbsPathFree = NULL;
+ char *pszAbsPath = szAbsPathBuf;
+ size_t cbAbsPath = sizeof(szAbsPathBuf) - (cchPrefix - cchSkip);
+ rc = RTPathAbsEx(NULL, szRelPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ unsigned cTries = 8;
+ size_t cbAbsPathBuf = RTPATH_MAX;
+ for (;;)
+ {
+ cbAbsPathBuf = RT_MAX(RT_ALIGN_Z((cchPrefix - cchSkip) + cbAbsPath + 32, 64), cbAbsPathBuf + 256);
+ if (cTries == 1)
+ cbAbsPathBuf = RT_MAX(cbAbsPathBuf, RTPATH_BIG_MAX * 2);
+ pszAbsPathFree = pszAbsPath = (char *)RTMemTmpAlloc(cbAbsPathBuf);
+ if (!pszAbsPath)
+ {
+ rc = VERR_NO_TMP_MEMORY;
+ break;
+ }
+
+ cbAbsPath = cbAbsPathBuf - (cchPrefix - cchSkip);
+ rc = RTPathAbsEx(NULL, szRelPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
+ if (RT_SUCCESS(rc))
+ break;
+
+ RTMemTmpFree(pszAbsPathFree);
+ pszAbsPathFree = NULL;
+ if (rc != VERR_BUFFER_OVERFLOW)
+ break;
+ if (--cTries == 0)
+ {
+ rc = VERR_FILENAME_TOO_LONG;
+ break;
+ }
+ }
+
+ }
+ if (pszRelPathFree)
+ RTStrFree(pszRelPathFree);
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add prefix
+ */
+ memcpy(pszAbsPath, pszPrefix, cchPrefix);
+
+ /*
+ * Remove trailing '.' that is used to specify no extension in the Win32/DOS world.
+ */
+ size_t cchAbsPath = strlen(pszAbsPath);
+ if ( cchAbsPath > 2
+ && pszAbsPath[cchAbsPath - 1] == '.')
+ {
+ char const ch = pszAbsPath[cchAbsPath - 2];
+ if ( ch != '/'
+ && ch != '\\'
+ && ch != ':'
+ && ch != '.')
+ pszAbsPath[--cchAbsPath] = '\0';
+ }
+
+ /*
+ * Finally convert to UNICODE_STRING.
+ */
+ rc = rtNtPathUtf8ToUniStr(pNtName, phRootDir, pszAbsPath);
+
+ if (pszAbsPathFree)
+ RTMemTmpFree(pszAbsPathFree);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Ensures that the NT string has sufficient storage to hold @a cwcMin RTUTF16
+ * chars plus a terminator.
+ *
+ * The NT string must have been returned by RTNtPathFromWinUtf8 or
+ * RTNtPathFromWinUtf16Ex.
+ *
+ * @returns IPRT status code.
+ * @param pNtName The NT path string.
+ * @param cwcMin The minimum number of RTUTF16 chars. Max 32767.
+ * @sa RTNtPathFree
+ */
+RTDECL(int) RTNtPathEnsureSpace(struct _UNICODE_STRING *pNtName, size_t cwcMin)
+{
+ if (pNtName->MaximumLength / sizeof(RTUTF16) > cwcMin)
+ return VINF_SUCCESS;
+
+ AssertReturn(cwcMin < _64K / sizeof(RTUTF16), VERR_OUT_OF_RANGE);
+
+ size_t const cbMin = (cwcMin + 1) * sizeof(RTUTF16);
+ int rc = RTUtf16Realloc(&pNtName->Buffer, cbMin);
+ if (RT_SUCCESS(rc))
+ pNtName->MaximumLength = (uint16_t)cbMin;
+ return rc;
+}
+
+
+/**
+ * Gets the NT path to the object represented by the given handle.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT path. Free using
+ * RTNtPathFree.
+ * @param hHandle The handle.
+ * @param cwcExtra How much extra space is needed.
+ */
+RTDECL(int) RTNtPathFromHandle(struct _UNICODE_STRING *pNtName, HANDLE hHandle, size_t cwcExtra)
+{
+ /*
+ * Query the name into a buffer.
+ */
+ ULONG cbBuf = _2K;
+ PUNICODE_STRING pUniStrBuf = (PUNICODE_STRING)RTMemTmpAllocZ(cbBuf);
+ if (!pUniStrBuf)
+ return VERR_NO_TMP_MEMORY;
+
+ ULONG cbNameBuf = cbBuf;
+ NTSTATUS rcNt = NtQueryObject(hHandle, ObjectNameInformation, pUniStrBuf, cbBuf, &cbNameBuf);
+ while ( rcNt == STATUS_BUFFER_OVERFLOW
+ || rcNt == STATUS_BUFFER_TOO_SMALL)
+ {
+ do
+ cbBuf *= 2;
+ while (cbBuf <= cbNameBuf);
+ RTMemTmpFree(pUniStrBuf);
+ pUniStrBuf = (PUNICODE_STRING)RTMemTmpAllocZ(cbBuf);
+ if (!pUniStrBuf)
+ return VERR_NO_TMP_MEMORY;
+
+ cbNameBuf = cbBuf;
+ rcNt = NtQueryObject(hHandle, ObjectNameInformation, pUniStrBuf, cbBuf, &cbNameBuf);
+ }
+ int rc;
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Copy the result into the return string.
+ */
+ size_t cbNeeded = cwcExtra * sizeof(RTUTF16) + pUniStrBuf->Length + sizeof(RTUTF16);
+ if (cbNeeded < _64K)
+ {
+ pNtName->Length = pUniStrBuf->Length;
+ pNtName->MaximumLength = (uint16_t)cbNeeded;
+ pNtName->Buffer = RTUtf16Alloc(cbNeeded);
+ if (pNtName->Buffer)
+ {
+ memcpy(pNtName->Buffer, pUniStrBuf->Buffer, pUniStrBuf->Length);
+ pNtName->Buffer[pUniStrBuf->Length / sizeof(RTUTF16)] = '\0';
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_UTF16_MEMORY;
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ RTMemTmpFree(pUniStrBuf);
+ return rc;
+}
+
+static int rtNtPathRelativeToAbs(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
+{
+ int rc;
+ if (pNtName->Length == 0)
+ {
+ RTUtf16Free(pNtName->Buffer);
+ rc = RTNtPathFromHandle(pNtName, *phRootDir, pNtName->Length / sizeof(RTUTF16) + 2);
+ if (RT_SUCCESS(rc))
+ {
+ *phRootDir = NULL;
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+
+ UNICODE_STRING RootDir;
+ size_t const cwcAppend = pNtName->Length / sizeof(RTUTF16);
+ rc = RTNtPathFromHandle(&RootDir, *phRootDir, cwcAppend + 2);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cwcRoot = RootDir.Length / sizeof(RTUTF16);
+ if (RootDir.Buffer[cwcRoot - 1] != '\\')
+ RootDir.Buffer[cwcRoot++] = '\\';
+ memcpy(&RootDir.Buffer[cwcRoot], pNtName->Buffer, cwcAppend * sizeof(RTUTF16));
+ RTUtf16Free(pNtName->Buffer);
+ pNtName->Length = (uint16_t)((cwcRoot + cwcAppend) * sizeof(RTUTF16));
+ pNtName->MaximumLength = RootDir.MaximumLength;
+ pNtName->Buffer = RootDir.Buffer;
+
+ *phRootDir = NULL;
+ return VINF_SUCCESS;
+ }
+ RTUtf16Free(pNtName->Buffer);
+ }
+ pNtName->Length = 0;
+ pNtName->MaximumLength = 0;
+ pNtName->Buffer = NULL;
+ return rc;
+}
+
+
+/**
+ * Rewinds the path back to the start of the previous component.
+ *
+ * Will preserve root slash.
+ *
+ * @returns Pointer to character after the start-of-component slash, or
+ * pwszStart.
+ * @param pwcEnd The current end of the path.
+ * @param pwszStart The start of the path.
+ */
+static PRTUTF16 rtNtPathGetPrevComponent(PRTUTF16 pwcEnd, PRTUTF16 pwszStart)
+{
+ if ((uintptr_t)pwcEnd > (uintptr_t)pwszStart)
+ {
+ RTUTF16 wc = pwcEnd[-1];
+ if ( (wc == '\\' || wc == '/')
+ && (uintptr_t)(pwcEnd - pwszStart) != 1)
+ pwcEnd--;
+
+ while ( (uintptr_t)pwcEnd > (uintptr_t)pwszStart
+ && (wc = pwcEnd[-1]) != '\\'
+ && (wc = pwcEnd[-1]) != '/')
+ pwcEnd--;
+ }
+ return pwcEnd;
+}
+
+
+/**
+ * Converts a relative windows-style path to relative NT format and encoding.
+ *
+ * @returns IPRT status code.
+ * @param pNtName Where to return the NT name. Free using
+ * rtTNtPathToNative with phRootDir set to NULL.
+ * @param phRootDir On input, the handle to the directory the path
+ * is relative to. On output, the handle to
+ * specify as root directory in the object
+ * attributes when accessing the path. If
+ * enmAscent is kRTNtPathRelativeAscent_Allow, it
+ * may have been set to NULL.
+ * @param pszPath The relative UTF-8 path.
+ * @param enmAscent How to handle ascent.
+ * @param fMustReturnAbsolute Must convert to an absolute path. This
+ * is necessary if the root dir is a NT directory
+ * object (e.g. /Devices) since they cannot parse
+ * relative paths it seems.
+ */
+RTDECL(int) RTNtPathRelativeFromUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath,
+ RTNTPATHRELATIVEASCENT enmAscent, bool fMustReturnAbsolute)
+{
+ size_t cwcMax;
+ int rc = RTStrCalcUtf16LenEx(pszPath, RTSTR_MAX, &cwcMax);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (cwcMax + 2 >= _32K)
+ return VERR_FILENAME_TOO_LONG;
+
+ PRTUTF16 pwszDst;
+ pNtName->Length = 0;
+ pNtName->MaximumLength = (uint16_t)((cwcMax + 2) * sizeof(RTUTF16));
+ pNtName->Buffer = pwszDst = RTUtf16Alloc((cwcMax + 2) * sizeof(RTUTF16));
+ if (!pwszDst)
+ return VERR_NO_UTF16_MEMORY;
+
+ PRTUTF16 pwszDstCur = pwszDst;
+ PRTUTF16 pwszDstComp = pwszDst;
+ for (;;)
+ {
+ RTUNICP uc;
+ rc = RTStrGetCpEx(&pszPath, &uc);
+ if (RT_SUCCESS(rc))
+ {
+ switch (uc)
+ {
+ default:
+ pwszDstCur = RTUtf16PutCp(pwszDstCur, uc);
+ break;
+
+ case '\\':
+ case '/':
+ if (pwszDstCur != pwszDstComp)
+ pwszDstComp = pwszDstCur = RTUtf16PutCp(pwszDstCur, '\\');
+ /* else: only one slash between components. */
+ break;
+
+ case '.':
+ if (pwszDstCur == pwszDstComp)
+ {
+ /*
+ * Single dot changes nothing.
+ */
+ char ch2 = *pszPath;
+ if (ch2 == '\0')
+ {
+ /* Trailing single dot means we need to drop trailing slash. */
+ if (pwszDstCur != pwszDst)
+ pwszDstCur--;
+ *pwszDstCur = '\0';
+ pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
+ if (!fMustReturnAbsolute || *phRootDir == NULL)
+ return VINF_SUCCESS;
+ return rtNtPathRelativeToAbs(pNtName, phRootDir);
+ }
+
+ if (ch2 == '\\' || ch2 == '/')
+ {
+ pszPath++; /* Ignore lone dot followed but another component. */
+ break;
+ }
+
+ /*
+ * Two dots drops off the last directory component. This gets complicated
+ * when we start out without any path and we need to consult enmAscent.
+ */
+ if (ch2 == '.')
+ {
+ char ch3 = pszPath[1];
+ if ( ch3 == '\\'
+ || ch3 == '/'
+ || ch3 == '\0')
+ {
+ /* Drop a path component. */
+ if (pwszDstComp != pwszDst)
+ pwszDstComp = pwszDstCur = rtNtPathGetPrevComponent(pwszDstCur, pwszDst);
+ /* Hit the start, which is a bit complicated. */
+ else
+ switch (enmAscent)
+ {
+ case kRTNtPathRelativeAscent_Allow:
+ if (*phRootDir != NULL)
+ {
+ RTUtf16Free(pwszDst);
+ rc = RTNtPathFromHandle(pNtName, *phRootDir, cwcMax + 2);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ *phRootDir = NULL;
+ pwszDst = pNtName->Buffer;
+ pwszDstCur = &pwszDst[pNtName->Length / sizeof(RTUTF16)];
+ if ( pwszDst != pwszDstCur
+ && pwszDstCur[-1] != '\\'
+ && pwszDstCur[-1] != '/')
+ *pwszDstCur++ = '\\';
+ pwszDstComp = pwszDstCur = rtNtPathGetPrevComponent(pwszDstCur, pwszDst);
+ }
+ /* else: ignore attempt to ascend beyond the NT root (won't get here). */
+ break;
+
+ case kRTNtPathRelativeAscent_Ignore:
+ /* nothing to do here */
+ break;
+
+ default:
+ case kRTNtPathRelativeAscent_Fail:
+ RTUtf16Free(pwszDst);
+ return VERR_PATH_NOT_FOUND;
+ }
+
+ if (ch3 == '\0')
+ {
+ *pwszDstCur = '\0';
+ pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
+ if (!fMustReturnAbsolute || *phRootDir == NULL)
+ return VINF_SUCCESS;
+ return rtNtPathRelativeToAbs(pNtName, phRootDir);
+ }
+ pszPath += 2;
+ break;
+ }
+ }
+ }
+
+ /* Neither '.' nor '..'. */
+ pwszDstCur = RTUtf16PutCp(pwszDstCur, '.');
+ break;
+
+ case '\0':
+ *pwszDstCur = '\0';
+ pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
+ if (!fMustReturnAbsolute || *phRootDir == NULL)
+ return VINF_SUCCESS;
+ return rtNtPathRelativeToAbs(pNtName, phRootDir);
+ }
+ }
+ }
+}
+
+
+/**
+ * Frees the native path and root handle.
+ *
+ * @param pNtName The NT path after a successful rtNtPathToNative
+ * call or RTNtPathRelativeFromUtf8.
+ * @param phRootDir The root handle variable from rtNtPathToNative,
+ * but NOT RTNtPathRelativeFromUtf8.
+ */
+static void rtNtPathFreeNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir)
+{
+ RTUtf16Free(pNtName->Buffer);
+ pNtName->Buffer = NULL;
+
+ RT_NOREF_PV(phRootDir); /* never returned by rtNtPathToNative, shouldn't be freed in connection with RTNtPathRelativeFromUtf8 */
+}
+
+
+/**
+ * Frees the native path and root handle.
+ *
+ * @param pNtName The NT path after a successful rtNtPathToNative
+ * call or RTNtPathRelativeFromUtf8.
+ * @param phRootDir The root handle variable from rtNtPathToNative,
+ */
+RTDECL(void) RTNtPathFree(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
+{
+ rtNtPathFreeNative(pNtName, phRootDir);
+}
+
+
+/**
+ * Wrapper around NtCreateFile.
+ *
+ * @returns IPRT status code.
+ * @param pszPath The UTF-8 path.
+ * @param fDesiredAccess See NtCreateFile.
+ * @param fFileAttribs See NtCreateFile.
+ * @param fShareAccess See NtCreateFile.
+ * @param fCreateDisposition See NtCreateFile.
+ * @param fCreateOptions See NtCreateFile.
+ * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
+ * NtCreateFile and InitializeObjectAttributes.
+ * @param phHandle Where to return the handle.
+ * @param puAction Where to return the action taken. Optional.
+ */
+RTDECL(int) RTNtPathOpen(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess,
+ ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
+ PHANDLE phHandle, PULONG_PTR puAction)
+{
+ *phHandle = RTNT_INVALID_HANDLE_VALUE;
+
+ HANDLE hRootDir;
+ UNICODE_STRING NtName;
+ int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, fObjAttribs, hRootDir, NULL);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ fDesiredAccess,
+ &ObjAttr,
+ &Ios,
+ NULL /* AllocationSize*/,
+ fFileAttribs,
+ fShareAccess,
+ fCreateDisposition,
+ fCreateOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (puAction)
+ *puAction = Ios.Information;
+ *phHandle = hFile;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ rtNtPathFreeNative(&NtName, &hRootDir);
+ }
+ return rc;
+}
+
+
+/**
+ * Wrapper around NtCreateFile.
+ *
+ * @returns IPRT status code.
+ * @param pszPath The UTF-8 path.
+ * @param fDesiredAccess See NtCreateFile.
+ * @param fShareAccess See NtCreateFile.
+ * @param fCreateOptions See NtCreateFile.
+ * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
+ * NtCreateFile and InitializeObjectAttributes.
+ * @param phHandle Where to return the handle.
+ * @param pfObjDir If not NULL, the variable pointed to will be set
+ * to @c true if we opened an object directory and
+ * @c false if we opened an directory file (normal
+ * directory).
+ */
+RTDECL(int) RTNtPathOpenDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fShareAccess, ULONG fCreateOptions,
+ ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
+{
+ *phHandle = RTNT_INVALID_HANDLE_VALUE;
+
+ HANDLE hRootDir;
+ UNICODE_STRING NtName;
+ int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ if (pfObjDir)
+ {
+ *pfObjDir = false;
+#ifdef IPRT_WITH_NT_PATH_PASSTHRU
+ if ( !RTPATH_IS_SLASH(pszPath[0])
+ || !RTPATH_IS_SLASH(pszPath[1])
+ || pszPath[2] != ':'
+ || pszPath[3] != 'i'
+ || pszPath[4] != 'p'
+ || pszPath[5] != 'r'
+ || pszPath[6] != 't'
+ || pszPath[7] != 'n'
+ || pszPath[8] != 't'
+ || pszPath[9] != ':'
+ || !RTPATH_IS_SLASH(pszPath[10]) )
+#endif
+ pfObjDir = NULL;
+ }
+ rc = RTNtPathOpenDirEx(hRootDir, &NtName, fDesiredAccess, fShareAccess, fCreateOptions, fObjAttribs, phHandle, pfObjDir);
+ rtNtPathFreeNative(&NtName, &hRootDir);
+ }
+ return rc;
+}
+
+
+
+/**
+ * Wrapper around NtCreateFile, extended version.
+ *
+ * @returns IPRT status code.
+ * @param hRootDir The root director the path is relative to. NULL
+ * if none.
+ * @param pNtName The NT path.
+ * @param fDesiredAccess See NtCreateFile.
+ * @param fShareAccess See NtCreateFile.
+ * @param fCreateOptions See NtCreateFile.
+ * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
+ * NtCreateFile and InitializeObjectAttributes.
+ * @param phHandle Where to return the handle.
+ * @param pfObjDir If not NULL, the variable pointed to will be set
+ * to @c true if we opened an object directory and
+ * @c false if we opened an directory file (normal
+ * directory).
+ */
+RTDECL(int) RTNtPathOpenDirEx(HANDLE hRootDir, struct _UNICODE_STRING *pNtName, ACCESS_MASK fDesiredAccess, ULONG fShareAccess,
+ ULONG fCreateOptions, ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
+{
+ *phHandle = RTNT_INVALID_HANDLE_VALUE;
+
+ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, pNtName, fObjAttribs, hRootDir, NULL);
+
+ NTSTATUS rcNt = NtCreateFile(&hFile,
+ fDesiredAccess,
+ &ObjAttr,
+ &Ios,
+ NULL /* AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ fShareAccess,
+ FILE_OPEN,
+ fCreateOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (pfObjDir)
+ *pfObjDir = false;
+ *phHandle = hFile;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Try add a slash in case this is a device object with a file system attached.
+ */
+ if ( rcNt == STATUS_INVALID_PARAMETER
+ && pNtName->Length < _64K - 4
+ && ( pNtName->Length == 0
+ || pNtName->Buffer[pNtName->Length / sizeof(RTUTF16)] != '\\') )
+ {
+ UNICODE_STRING NtTmp;
+ NtTmp.Length = pNtName->Length + 2;
+ NtTmp.MaximumLength = NtTmp.Length + 2;
+ NtTmp.Buffer = (PRTUTF16)RTMemTmpAlloc(NtTmp.MaximumLength);
+ if (NtTmp.Buffer)
+ {
+ memcpy(NtTmp.Buffer, pNtName->Buffer, pNtName->Length);
+ NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16)] = '\\';
+ NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16) + 1] = '\0';
+
+ hFile = RTNT_INVALID_HANDLE_VALUE;
+ Ios.Status = -1;
+ Ios.Information = 0;
+ ObjAttr.ObjectName = &NtTmp;
+
+ rcNt = NtCreateFile(&hFile,
+ fDesiredAccess,
+ &ObjAttr,
+ &Ios,
+ NULL /* AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ fShareAccess,
+ FILE_OPEN,
+ fCreateOptions,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ RTMemTmpFree(NtTmp.Buffer);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (pfObjDir)
+ *pfObjDir = false;
+ *phHandle = hFile;
+ return VINF_SUCCESS;
+ }
+ ObjAttr.ObjectName = pNtName;
+ }
+ }
+
+ /*
+ * Try open it as a directory object if it makes sense.
+ */
+ if ( pfObjDir
+ && ( rcNt == STATUS_OBJECT_NAME_INVALID
+ || rcNt == STATUS_OBJECT_TYPE_MISMATCH ))
+ {
+ /* Strip trailing slash. */
+ struct _UNICODE_STRING NtName2 = *pNtName;
+ if ( NtName2.Length > 2
+ && RTPATH_IS_SLASH(NtName2.Buffer[(NtName2.Length / 2) - 1]))
+ NtName2.Length -= 2;
+ ObjAttr.ObjectName = &NtName2;
+
+ /* Rought conversion of the access flags. */
+ ULONG fObjDesiredAccess = 0;
+ if ( (fDesiredAccess & GENERIC_ALL)
+ || (fDesiredAccess & STANDARD_RIGHTS_ALL) == STANDARD_RIGHTS_ALL)
+ fObjDesiredAccess = DIRECTORY_ALL_ACCESS;
+ else
+ {
+ if (fDesiredAccess & (GENERIC_WRITE | STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA))
+ fObjDesiredAccess |= DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY;
+
+ if ( (fDesiredAccess & (GENERIC_READ | STANDARD_RIGHTS_READ | FILE_LIST_DIRECTORY))
+ || !fObjDesiredAccess)
+ fObjDesiredAccess |= DIRECTORY_QUERY;
+
+ if (fDesiredAccess & FILE_TRAVERSE)
+ fObjDesiredAccess |= DIRECTORY_TRAVERSE;
+ }
+
+ rcNt = NtOpenDirectoryObject(&hFile, fObjDesiredAccess, &ObjAttr);
+ if (NT_SUCCESS(rcNt))
+ {
+ *pfObjDir = true;
+ *phHandle = hFile;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+
+/**
+ * Closes an handled open by rtNtPathOpen.
+ *
+ * @returns IPRT status code
+ * @param hHandle The handle value.
+ */
+RTDECL(int) RTNtPathClose(HANDLE hHandle)
+{
+ NTSTATUS rcNt = NtClose(hHandle);
+ if (NT_SUCCESS(rcNt))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
diff --git a/src/VBox/Runtime/r3/nt/time-nt.cpp b/src/VBox/Runtime/r3/nt/time-nt.cpp
new file mode 100644
index 00000000..f60250e6
--- /dev/null
+++ b/src/VBox/Runtime/r3/nt/time-nt.cpp
@@ -0,0 +1,228 @@
+/* $Id: time-nt.cpp $ */
+/** @file
+ * IPRT - Time, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include "internal-r3-nt.h"
+
+#include <iprt/time.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/ldr.h>
+#include <iprt/uint128.h>
+#include "internal/time.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Whether we've tried to resolve g_pfnRtlGetSystemTimePrecise or not. */
+static bool g_fInitialized = false;
+/** Pointer to RtlGetSystemTimePrecise, added in 6.2 (windows 8). */
+static PFNRTLGETSYSTEMTIMEPRECISE g_pfnRtlGetSystemTimePrecise = NULL;
+
+
+/**
+ * Initializes globals.
+ */
+static void rtTimeNtInitialize(void)
+{
+ /*
+ * Make sure we don't recurse here when calling into RTLdr.
+ */
+ if (ASMAtomicCmpXchgBool(&g_fInitialized, true, false))
+ {
+ void *pvFunc = RTLdrGetSystemSymbol("ntdll.dll", "RtlGetSystemTimePrecise");
+ if (pvFunc)
+ ASMAtomicWritePtr((void * volatile *)&g_pfnRtlGetSystemTimePrecise, pvFunc);
+ ASMCompilerBarrier();
+ }
+}
+
+
+static uint64_t rtTimeGetSystemNanoTS(void)
+{
+ if (RT_UNLIKELY(!g_fInitialized))
+ rtTimeNtInitialize();
+
+ KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA;
+
+#if 1
+ /*
+ * If there is precise time, get the precise system time and calculate the
+ * interrupt time from it. (Microsoft doesn't expose interrupt time to user
+ * application, which is very unfortunate as there are a lot place where
+ * monotonic time is applicable but developer is "forced" to use wall clock.)
+ */
+ if (g_pfnRtlGetSystemTimePrecise)
+ {
+ for (;;)
+ {
+ uint64_t uUpdateLockBefore;
+ while ((uUpdateLockBefore = pUserSharedData->TimeUpdateLock) & 1)
+ ASMNopPause();
+
+ uint64_t uInterruptTime = *(uint64_t volatile *)&pUserSharedData->InterruptTime;
+ uint64_t uBaselineInterruptTimeQpc = pUserSharedData->BaselineInterruptTimeQpc;
+ uint64_t uQpcInterruptTimeIncrement = pUserSharedData->QpcInterruptTimeIncrement;
+ uint8_t uQpcInterruptTimeIncrementShift = pUserSharedData->QpcInterruptTimeIncrementShift;
+ LARGE_INTEGER QpcValue;
+ RtlQueryPerformanceCounter(&QpcValue);
+
+ if (pUserSharedData->TimeUpdateLock == uUpdateLockBefore)
+ {
+ uint64_t uQpcValue = QpcValue.QuadPart;
+ if (uQpcValue <= uBaselineInterruptTimeQpc)
+ return uInterruptTime * 100;
+
+ /* Calc QPC delta since base line. */
+ uQpcValue -= uBaselineInterruptTimeQpc;
+ uQpcValue--;
+
+ /* Multiply by 10 million. */
+ uQpcValue *= UINT32_C(10000000);
+
+ /* Multiply by QPC interrupt time increment value. */
+ RTUINT128U Tmp128;
+ RTUInt128MulU64ByU64(&Tmp128, uQpcValue, uQpcInterruptTimeIncrement);
+
+ /* Shift the upper 64 bits by the increment shift factor. */
+ uint64_t uResult = Tmp128.s.Hi >> uQpcInterruptTimeIncrementShift;
+
+ /* Add to base interrupt time value. */
+ uResult += uInterruptTime;
+
+ /* Convert from NT unit to nano seconds. */
+ return uResult * 100;
+ }
+
+ ASMNopPause();
+ }
+ }
+#endif
+
+ /*
+ * Just read interrupt time.
+ */
+#if ARCH_BITS >= 64
+ uint64_t uRet = *(uint64_t volatile *)&pUserSharedData->InterruptTime; /* This is what KeQueryInterruptTime does. */
+ uRet *= 100;
+ return uRet;
+#else
+
+ LARGE_INTEGER NtTime;
+ do
+ {
+ NtTime.HighPart = pUserSharedData->InterruptTime.High1Time;
+ NtTime.LowPart = pUserSharedData->InterruptTime.LowPart;
+ } while (pUserSharedData->InterruptTime.High2Time != NtTime.HighPart);
+
+ return (uint64_t)NtTime.QuadPart * 100;
+#endif
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ /*
+ * Get the precise time if possible.
+ */
+ if (RT_UNLIKELY(!g_fInitialized))
+ rtTimeNtInitialize();
+ if (g_pfnRtlGetSystemTimePrecise != NULL)
+ return RTTimeSpecSetNtTime(pTime, g_pfnRtlGetSystemTimePrecise());
+
+ /*
+ * Just read system time.
+ */
+ KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA;
+#ifdef RT_ARCH_AMD64
+ uint64_t uRet = *(uint64_t volatile *)&pUserSharedData->SystemTime; /* This is what KeQuerySystemTime does. */
+ return RTTimeSpecSetNtTime(pTime, uRet);
+#else
+
+ LARGE_INTEGER NtTime;
+ do
+ {
+ NtTime.HighPart = pUserSharedData->SystemTime.High1Time;
+ NtTime.LowPart = pUserSharedData->SystemTime.LowPart;
+ } while (pUserSharedData->SystemTime.High2Time != NtTime.HighPart);
+ return RTTimeSpecSetNtTime(pTime, NtTime.QuadPart);
+#endif
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeLocalNow(PRTTIMESPEC pTime)
+{
+ return RTTimeSpecAddNano(RTTimeNow(pTime), RTTimeLocalDeltaNano());
+}
+
+
+RTDECL(int64_t) RTTimeLocalDeltaNano(void)
+{
+ /*
+ * UTC = local + TimeZoneBias; The bias is given in NT units.
+ */
+ KUSER_SHARED_DATA volatile *pUserSharedData = (KUSER_SHARED_DATA volatile *)MM_SHARED_USER_DATA_VA;
+ LARGE_INTEGER Delta;
+#if ARCH_BITS == 64
+ Delta.QuadPart = *(int64_t volatile *)&pUserSharedData->TimeZoneBias;
+#else
+ do
+ {
+ Delta.HighPart = pUserSharedData->TimeZoneBias.High1Time;
+ Delta.LowPart = pUserSharedData->TimeZoneBias.LowPart;
+ } while (pUserSharedData->TimeZoneBias.High2Time != Delta.HighPart);
+#endif
+ return Delta.QuadPart * -100;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/Makefile.kup b/src/VBox/Runtime/r3/os2/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/Makefile.kup
diff --git a/src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp b/src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp
new file mode 100644
index 00000000..fded291b
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/RTTimeSet-os2.cpp
@@ -0,0 +1,121 @@
+/* $Id: RTTimeSet-os2.cpp $ */
+/** @file
+ * IPRT - RTTimeSet, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ * --------------------------------------------------------------------
+ *
+ * Copyright (c) 2018 knut st. osmundsen <bird-src-spam@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#define INCL_DOSDATETIME
+#define INCL_DOSERRORS
+#include <os2.h>
+
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTTimeSet(PCRTTIMESPEC pTime)
+{
+ /*
+ * Convert to local time and explode it, keeping the distance
+ * between UTC and local.
+ */
+ int64_t cNsLocalDelta = RTTimeLocalDeltaNanoFor(pTime);
+ RTTIMESPEC TimeLocal = *pTime;
+ RTTIME Exploded;
+ if (RTTimeExplode(&Exploded, RTTimeSpecAddNano(&TimeLocal, cNsLocalDelta)))
+ {
+ /*
+ * Fill in the OS/2 structure and make the call.
+ */
+ DATETIME DateTime;
+ DateTime.hours = Exploded.u8Hour;
+ DateTime.minutes = Exploded.u8Minute;
+ DateTime.seconds = Exploded.u8Second;
+ DateTime.hundredths = (uint8_t)(Exploded.u32Nanosecond / (RT_NS_1SEC_64 / 100));
+ DateTime.day = Exploded.u8MonthDay;
+ DateTime.month = Exploded.u8Month;
+ DateTime.year = (uint16_t)Exploded.i32Year;
+ DateTime.weekday = Exploded.u8WeekDay;
+
+ /* Minutes from UTC. http://www.edm2.com/os2api/Dos/DosSetDateTime.html says
+ that timezones west of UTC should have a positive value. The kernel fails
+ the call if we're more than +/-780 min (13h) distant, so clamp it in
+ case of bogus TZ values. */
+ DateTime.timezone = (int16_t)(-cNsLocalDelta / (int64_t)RT_NS_1MIN);
+ if (DateTime.timezone > 780)
+ DateTime.timezone = 780;
+ else if (DateTime.timezone < -780)
+ DateTime.timezone = -780;
+
+ APIRET rc = DosSetDateTime(&DateTime);
+ if (rc == NO_ERROR)
+ return VINF_SUCCESS;
+ AssertMsgFailed(("rc=%u\n", rc));
+ return RTErrConvertFromOS2(rc);
+ }
+ return VERR_INVALID_PARAMETER;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/filelock-os2.cpp b/src/VBox/Runtime/r3/os2/filelock-os2.cpp
new file mode 100644
index 00000000..9fb93078
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/filelock-os2.cpp
@@ -0,0 +1,186 @@
+/* $Id: filelock-os2.cpp $ */
+/** @file
+ * IPRT - File Locking, OS/2.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <iprt/file.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/file.h"
+#include "internal/fs.h"
+
+
+
+
+RTR3DECL(int) RTFileLock(RTFILE File, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /* Check arguments. */
+ if (fLock & ~RTFILE_LOCK_MASK)
+ {
+ AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Validate offset.
+ */
+ if ( sizeof(off_t) < sizeof(cbLock)
+ && ( (offLock >> 32) != 0
+ || (cbLock >> 32) != 0
+ || ((offLock + cbLock) >> 32) != 0))
+ {
+ AssertMsgFailed(("64-bit file i/o not supported! offLock=%lld cbLock=%lld\n", offLock, cbLock));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /* Prepare flock structure. */
+ struct flock fl;
+ Assert(RTFILE_LOCK_WRITE);
+ fl.l_type = (fLock & RTFILE_LOCK_WRITE) ? F_WRLCK : F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = (off_t)offLock;
+ fl.l_len = (off_t)cbLock;
+ fl.l_pid = 0;
+
+ Assert(RTFILE_LOCK_WAIT);
+ if (fcntl(RTFileToNative(File), (fLock & RTFILE_LOCK_WAIT) ? F_SETLKW : F_SETLK, &fl) >= 0)
+ return VINF_SUCCESS;
+
+ int iErr = errno;
+ if ( iErr == EAGAIN
+ || iErr == EACCES)
+ return VERR_FILE_LOCK_VIOLATION;
+
+ return RTErrConvertFromErrno(iErr);
+}
+
+
+RTR3DECL(int) RTFileChangeLock(RTFILE File, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ /** @todo copied from ../win/fileio-win.cpp for now but a proper solution
+ * would probably be to modify kLIBC so that __fcntl_locking() first
+ * assumes a change lock request is made (e.g. the same region was
+ * previously F_RDLCK'ed and now needs to be F_WRLCK'ed or vice versa) and
+ * tries to use atomic locking, and only if it fails, it does the regular
+ * lock procedure. The alternative is to use DosSetFileLocks directly here
+ * which basically means copy-pasting the __fcntl_locking() source
+ * code :) Note that the first attempt to call RTFileLock() below assumes
+ * that kLIBC is patched as described above one day and gives it a chance;
+ * on failure, we fall back to the Win-like unlock-then-lock approach. */
+
+ int rc = RTFileLock(File, fLock, offLock, cbLock);
+ if (RT_FAILURE(rc) && rc != VERR_FILE_LOCK_VIOLATION)
+ return rc;
+
+ /* Check arguments. */
+ if (fLock & ~RTFILE_LOCK_MASK)
+ {
+ AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Remove old lock. */
+ rc = RTFileUnlock(File, offLock, cbLock);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Set new lock. */
+ rc = RTFileLock(File, fLock, offLock, cbLock);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /* Try to restore old lock. */
+ unsigned fLockOld = (fLock & RTFILE_LOCK_WRITE) ? fLock & ~RTFILE_LOCK_WRITE : fLock | RTFILE_LOCK_WRITE;
+ rc = RTFileLock(File, fLockOld, offLock, cbLock);
+ if (RT_SUCCESS(rc))
+ return VERR_FILE_LOCK_VIOLATION;
+ else
+ return VERR_FILE_LOCK_LOST;
+}
+
+
+RTR3DECL(int) RTFileUnlock(RTFILE File, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /*
+ * Validate offset.
+ */
+ if ( sizeof(off_t) < sizeof(cbLock)
+ && ( (offLock >> 32) != 0
+ || (cbLock >> 32) != 0
+ || ((offLock + cbLock) >> 32) != 0))
+ {
+ AssertMsgFailed(("64-bit file i/o not supported! offLock=%lld cbLock=%lld\n", offLock, cbLock));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /* Prepare flock structure. */
+ struct flock fl;
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = (off_t)offLock;
+ fl.l_len = (off_t)cbLock;
+ fl.l_pid = 0;
+
+ if (fcntl(RTFileToNative(File), F_SETLK, &fl) >= 0)
+ return VINF_SUCCESS;
+
+ /** @todo check error codes for non existing lock. */
+ int iErr = errno;
+ if ( iErr == EAGAIN
+ || iErr == EACCES)
+ return VERR_FILE_LOCK_VIOLATION;
+
+ return RTErrConvertFromErrno(iErr);
+}
+
diff --git a/src/VBox/Runtime/r3/os2/mp-os2.cpp b/src/VBox/Runtime/r3/os2/mp-os2.cpp
new file mode 100644
index 00000000..64ae06a8
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/mp-os2.cpp
@@ -0,0 +1,125 @@
+/* $Id: mp-os2.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, OS/2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_BASE
+#define INCL_ERRORS
+#include <os2.h>
+#undef RT_MAX
+
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/assert.h>
+
+/** @todo RTMpCpuId() */
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS ? (int) idCpu : -1;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu < RTCPUSET_MAX_CPUS ? iCpu : NIL_RTCPUID;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return RTCPUSET_MAX_CPUS;
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ RTCPUSET Set;
+ return RTCpuSetIsMember(RTMpGetSet(&Set), idCpu);
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu = RTMpGetCount();
+ RTCpuSetEmpty(pSet);
+ while (idCpu-- > 0)
+ RTCpuSetAdd(pSet, idCpu);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ ULONG cCpus = 1;
+ int rc = DosQuerySysInfo(QSV_NUMPROCESSORS, QSV_NUMPROCESSORS, &cCpus, sizeof(cCpus));
+ if (rc || !cCpus)
+ cCpus = 1;
+ return cCpus;
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ RTCPUSET Set;
+ return RTCpuSetIsMember(RTMpGetOnlineSet(&Set), idCpu);
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ union
+ {
+ uint64_t u64;
+ MPAFFINITY mpaff;
+ } u;
+
+ int rc = DosQueryThreadAffinity(AFNTY_SYSTEM, &u.mpaff);
+ if (rc)
+ u.u64 = 1;
+ return RTCpuSetFromU64(pSet, u.u64);
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ RTCPUSET Set;
+ RTMpGetOnlineSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+
diff --git a/src/VBox/Runtime/r3/os2/pipe-os2.cpp b/src/VBox/Runtime/r3/os2/pipe-os2.cpp
new file mode 100644
index 00000000..6a4792c5
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/pipe-os2.cpp
@@ -0,0 +1,1083 @@
+/* $Id: pipe-os2.cpp $ */
+/** @file
+ * IPRT - Anonymous Pipes, OS/2 Implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_ERRORS
+#define INCL_DOSSEMAPHORES
+#include <os2.h>
+
+#include <iprt/pipe.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/pipe.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The pipe buffer size we prefer. */
+#define RTPIPE_OS2_SIZE _32K
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTPIPEINTERNAL
+{
+ /** Magic value (RTPIPE_MAGIC). */
+ uint32_t u32Magic;
+ /** The pipe handle. */
+ HPIPE hPipe;
+ /** Set if this is the read end, clear if it's the write end. */
+ bool fRead;
+ /** RTPipeFromNative: Leave open. */
+ bool fLeaveOpen;
+ /** Whether the pipe is in blocking or non-blocking mode. */
+ bool fBlocking;
+ /** Set if the pipe is broken. */
+ bool fBrokenPipe;
+ /** Usage counter. */
+ uint32_t cUsers;
+
+ /** The event semaphore associated with the pipe. */
+ HEV hev;
+ /** The handle of the poll set currently polling on this pipe.
+ * We can only have one poller at the time (lazy bird). */
+ RTPOLLSET hPollSet;
+ /** Critical section protecting the above members.
+ * (Taking the lazy/simple approach.) */
+ RTCRITSECT CritSect;
+
+} RTPIPEINTERNAL;
+
+
+/**
+ * Ensures that the pipe has a semaphore associated with it.
+ *
+ * @returns VBox status code.
+ * @param pThis The pipe.
+ */
+static int rtPipeOs2EnsureSem(RTPIPEINTERNAL *pThis)
+{
+ if (pThis->hev != NULLHANDLE)
+ return VINF_SUCCESS;
+
+ HEV hev;
+ APIRET orc = DosCreateEventSem(NULL, &hev, DC_SEM_SHARED, FALSE);
+ if (orc == NO_ERROR)
+ {
+ orc = DosSetNPipeSem(pThis->hPipe, (HSEM)hev, 1);
+ if (orc == NO_ERROR)
+ {
+ pThis->hev = hev;
+ return VINF_SUCCESS;
+ }
+
+ DosCloseEventSem(hev);
+ }
+ return RTErrConvertFromOS2(orc);
+}
+
+
+RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
+ AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Try create and connect a pipe pair.
+ */
+ APIRET orc;
+ HPIPE hPipeR;
+ HFILE hPipeW;
+ int rc;
+ for (;;)
+ {
+ static volatile uint32_t g_iNextPipe = 0;
+ char szName[128];
+ RTStrPrintf(szName, sizeof(szName), "\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe));
+
+ /*
+ * Create the read end of the pipe.
+ */
+ ULONG fPipeMode = 1 /*instance*/ | NP_TYPE_BYTE | NP_READMODE_BYTE | NP_NOWAIT;
+ ULONG fOpenMode = NP_ACCESS_DUPLEX | NP_WRITEBEHIND;
+ if (fFlags & RTPIPE_C_INHERIT_READ)
+ fOpenMode |= NP_INHERIT;
+ else
+ fOpenMode |= NP_NOINHERIT;
+ orc = DosCreateNPipe((PSZ)szName, &hPipeR, fOpenMode, fPipeMode, RTPIPE_OS2_SIZE, RTPIPE_OS2_SIZE, NP_DEFAULT_WAIT);
+ if (orc == NO_ERROR)
+ {
+ orc = DosConnectNPipe(hPipeR);
+ if (orc == ERROR_PIPE_NOT_CONNECTED || orc == NO_ERROR)
+ {
+ /*
+ * Connect to the pipe (the write end), attach sem below.
+ */
+ ULONG ulAction = 0;
+ ULONG fOpenW = OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
+ ULONG fModeW = OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_FAIL_ON_ERROR;
+ if (!(fFlags & RTPIPE_C_INHERIT_WRITE))
+ fModeW |= OPEN_FLAGS_NOINHERIT;
+ orc = DosOpen((PSZ)szName, &hPipeW, &ulAction, 0 /*cbFile*/, FILE_NORMAL,
+ fOpenW, fModeW, NULL /*peaop2*/);
+ if (orc == NO_ERROR)
+ break;
+ }
+ DosClose(hPipeR);
+ }
+ if ( orc != ERROR_PIPE_BUSY /* already exist - compatible */
+ && orc != ERROR_ACCESS_DENIED /* already exist - incompatible (?) */)
+ return RTErrConvertFromOS2(orc);
+ /* else: try again with a new name */
+ }
+
+ /*
+ * Create the two handles.
+ */
+ RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (pThisR)
+ {
+ RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (pThisW)
+ {
+ /* Crit sects. */
+ rc = RTCritSectInit(&pThisR->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&pThisW->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* Initialize the structures. */
+ pThisR->u32Magic = RTPIPE_MAGIC;
+ pThisW->u32Magic = RTPIPE_MAGIC;
+ pThisR->hPipe = hPipeR;
+ pThisW->hPipe = hPipeW;
+ pThisR->hev = NULLHANDLE;
+ pThisW->hev = NULLHANDLE;
+ pThisR->fRead = true;
+ pThisW->fRead = false;
+ pThisR->fLeaveOpen = false;
+ pThisW->fLeaveOpen = false;
+ pThisR->fBlocking = false;
+ pThisW->fBlocking = true;
+ //pThisR->fBrokenPipe = false;
+ //pThisW->fBrokenPipe = false;
+ //pThisR->cUsers = 0;
+ //pThisW->cUsers = 0;
+ pThisR->hPollSet = NIL_RTPOLLSET;
+ pThisW->hPollSet = NIL_RTPOLLSET;
+
+ *phPipeRead = pThisR;
+ *phPipeWrite = pThisW;
+ return VINF_SUCCESS;
+ }
+
+ RTCritSectDelete(&pThisR->CritSect);
+ }
+ RTMemFree(pThisW);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTMemFree(pThisR);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ /* Don't call DosDisConnectNPipe! */
+ DosClose(hPipeW);
+ DosClose(hPipeR);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ if (pThis == NIL_RTPIPE)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
+ RTCritSectEnter(&pThis->CritSect);
+ Assert(pThis->cUsers == 0);
+
+ /* Don't call DosDisConnectNPipe! */
+ if (!fLeaveOpen && !pThis->fLeaveOpen)
+ DosClose(pThis->hPipe);
+ pThis->hPipe = (HPIPE)-1;
+
+ if (pThis->hev != NULLHANDLE)
+ {
+ DosCloseEventSem(pThis->hev);
+ pThis->hev = NULLHANDLE;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeClose(RTPIPE hPipe)
+{
+ return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/);
+}
+
+
+RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER);
+ AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
+
+ /*
+ * Get and validate the pipe handle info.
+ */
+ HPIPE hNative = (HPIPE)hNativePipe;
+ ULONG ulType = 0;
+ ULONG ulAttr = 0;
+ APIRET orc = DosQueryHType(hNative, &ulType, &ulAttr);
+ AssertMsgReturn(orc == NO_ERROR, ("%d\n", orc), RTErrConvertFromOS2(orc));
+ AssertReturn((ulType & 0x7) == HANDTYPE_PIPE, VERR_INVALID_HANDLE);
+
+#if 0
+ union
+ {
+ PIPEINFO PipeInfo;
+ uint8_t abPadding[sizeof(PIPEINFO) + 127];
+ } Buf;
+ orc = DosQueryNPipeInfo(hNative, 1, &Buf, sizeof(Buf));
+ if (orc != NO_ERROR)
+ {
+ /* Sorry, anonymous pips are not supported. */
+ AssertMsgFailed(("%d\n", orc));
+ return VERR_INVALID_HANDLE;
+ }
+ AssertReturn(Buf.PipeInfo.cbMaxInst == 1, VERR_INVALID_HANDLE);
+#endif
+
+ ULONG fPipeState = 0;
+ orc = DosQueryNPHState(hNative, &fPipeState);
+ if (orc != NO_ERROR)
+ {
+ /* Sorry, anonymous pips are not supported. */
+ AssertMsgFailed(("%d\n", orc));
+ return VERR_INVALID_HANDLE;
+ }
+ AssertReturn(!(fPipeState & NP_TYPE_MESSAGE), VERR_INVALID_HANDLE);
+ AssertReturn(!(fPipeState & NP_READMODE_MESSAGE), VERR_INVALID_HANDLE);
+ AssertReturn((fPipeState & 0xff) == 1, VERR_INVALID_HANDLE);
+
+ ULONG fFileState = 0;
+ orc = DosQueryFHState(hNative, &fFileState);
+ AssertMsgReturn(orc == NO_ERROR, ("%d\n", orc), VERR_INVALID_HANDLE);
+ AssertMsgReturn( (fFileState & 0x3) == (fFlags & RTPIPE_N_READ ? OPEN_ACCESS_READONLY : OPEN_ACCESS_WRITEONLY)
+ || (fFileState & 0x3) == OPEN_ACCESS_READWRITE
+ , ("%#x\n", fFileState), VERR_INVALID_HANDLE);
+
+ /*
+ * Looks kind of OK. Fix the inherit flag.
+ */
+ orc = DosSetFHState(hNative, (fFileState & (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_CACHE))
+ | (fFlags & RTPIPE_N_INHERIT ? 0 : OPEN_FLAGS_NOINHERIT));
+ AssertMsgReturn(orc == NO_ERROR, ("%d\n", orc), RTErrConvertFromOS2(orc));
+
+
+ /*
+ * Create a handle so we can try rtPipeQueryInfo on it
+ * and see if we need to duplicate it to make that call work.
+ */
+ RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ int rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32Magic = RTPIPE_MAGIC;
+ pThis->hPipe = hNative;
+ pThis->hev = NULLHANDLE;
+ pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ);
+ pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN);
+ pThis->fBlocking = !(fPipeState & NP_NOWAIT);
+ //pThis->fBrokenPipe = false;
+ //pThis->cUsers = 0;
+ pThis->hPollSet = NIL_RTPOLLSET;
+
+ *phPipe = pThis;
+ return VINF_SUCCESS;
+
+ //RTCritSectDelete(&pThis->CritSect);
+ }
+ RTMemFree(pThis);
+ return rc;
+}
+
+RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
+
+ return (RTHCINTPTR)pThis->hPipe;
+}
+
+/**
+ * Prepare blocking mode.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_WRONG_ORDER if simultaneous non-blocking and blocking access is
+ * attempted.
+ *
+ * @param pThis The pipe handle.
+ *
+ * @remarks Caller owns the critical section.
+ */
+static int rtPipeTryBlocking(RTPIPEINTERNAL *pThis)
+{
+ if (!pThis->fBlocking)
+ {
+ if (pThis->cUsers != 0)
+ return VERR_WRONG_ORDER;
+
+ APIRET orc = DosSetNPHState(pThis->hPipe, NP_WAIT | NP_READMODE_BYTE);
+ if (orc != NO_ERROR)
+ {
+ if (orc != ERROR_BROKEN_PIPE && orc != ERROR_PIPE_NOT_CONNECTED)
+ return RTErrConvertFromOS2(orc);
+ pThis->fBrokenPipe = true;
+ }
+ pThis->fBlocking = true;
+ }
+
+ pThis->cUsers++;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Prepare non-blocking mode.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_WRONG_ORDER if simultaneous non-blocking and blocking access is
+ * attempted.
+ *
+ * @param pThis The pipe handle.
+ */
+static int rtPipeTryNonBlocking(RTPIPEINTERNAL *pThis)
+{
+ if (pThis->fBlocking)
+ {
+ if (pThis->cUsers != 0)
+ return VERR_WRONG_ORDER;
+
+ APIRET orc = DosSetNPHState(pThis->hPipe, NP_NOWAIT | NP_READMODE_BYTE);
+ if (orc != NO_ERROR)
+ {
+ if (orc != ERROR_BROKEN_PIPE && orc != ERROR_PIPE_NOT_CONNECTED)
+ return RTErrConvertFromOS2(orc);
+ pThis->fBrokenPipe = true;
+ }
+ pThis->fBlocking = false;
+ }
+
+ pThis->cUsers++;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks if the read pipe has been broken.
+ *
+ * @returns true if broken, false if no.
+ * @param pThis The pipe handle (read).
+ */
+static bool rtPipeOs2IsBroken(RTPIPEINTERNAL *pThis)
+{
+ Assert(pThis->fRead);
+
+#if 0
+ /*
+ * Query it via the semaphore. Not sure how fast this is...
+ */
+ PIPESEMSTATE aStates[3]; RT_ZERO(aStates);
+ APIRET orc = DosQueryNPipeSemState(pThis->hev, &aStates[0], sizeof(aStates));
+ if (orc == NO_ERROR)
+ {
+ if (aStates[0].fStatus == NPSS_CLOSE)
+ return true;
+ if (aStates[0].fStatus == NPSS_RDATA)
+ return false;
+ }
+ AssertMsgFailed(("%d / %d\n", orc, aStates[0].fStatus));
+
+ /*
+ * Fall back / alternative method.
+ */
+#endif
+ ULONG cbActual = 0;
+ ULONG ulState = 0;
+ AVAILDATA Avail = { 0, 0 };
+ APIRET orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState);
+ if (orc != NO_ERROR)
+ {
+ if (orc != ERROR_PIPE_BUSY)
+ AssertMsgFailed(("%d\n", orc));
+ return false;
+ }
+
+ return ulState != NP_STATE_CONNECTED;
+}
+
+
+RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbRead);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPipeTryNonBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ ULONG cbActual = 0;
+ APIRET orc = DosRead(pThis->hPipe, pvBuf, cbToRead, &cbActual);
+ if (orc == NO_ERROR)
+ {
+ if (cbActual || !cbToRead || !rtPipeOs2IsBroken(pThis))
+ *pcbRead = cbActual;
+ else
+ rc = VERR_BROKEN_PIPE;
+ }
+ else if (orc == ERROR_NO_DATA)
+ {
+ *pcbRead = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromOS2(orc);
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPipeTryBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ size_t cbTotalRead = 0;
+ while (cbToRead > 0)
+ {
+ ULONG cbActual = 0;
+ APIRET orc = DosRead(pThis->hPipe, pvBuf, cbToRead, &cbActual);
+ if (orc != NO_ERROR)
+ {
+ rc = RTErrConvertFromOS2(orc);
+ break;
+ }
+ if (!cbActual && rtPipeOs2IsBroken(pThis))
+ {
+ rc = VERR_BROKEN_PIPE;
+ break;
+ }
+
+ /* advance */
+ pvBuf = (char *)pvBuf + cbActual;
+ cbTotalRead += cbActual;
+ cbToRead -= cbActual;
+ }
+
+ if (pcbRead)
+ {
+ *pcbRead = cbTotalRead;
+ if ( RT_FAILURE(rc)
+ && cbTotalRead)
+ rc = VINF_SUCCESS;
+ }
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+/**
+ * Gets the available write buffer size of the pipe.
+ *
+ * @returns Number of bytes, 1 on failure.
+ * @param pThis The pipe handle.
+ */
+static ULONG rtPipeOs2GetSpace(RTPIPEINTERNAL *pThis)
+{
+ Assert(!pThis->fRead);
+
+#if 0 /* Not sure which is more efficient, neither are really optimal, I fear. */
+ /*
+ * Query via semaphore state.
+ * This will walk the list of active named pipes...
+ */
+ /** @todo Check how hev and hpipe are associated, if complicated, use the
+ * alternative method below. */
+ PIPESEMSTATE aStates[3]; RT_ZERO(aStates);
+ APIRET orc = DosQueryNPipeSemState((HSEM)pThis->hev, &aStates[0], sizeof(aStates));
+ if (orc == NO_ERROR)
+ {
+ if (aStates[0].fStatus == NPSS_WSPACE)
+ return aStates[0].usAvail;
+ if (aStates[1].fStatus == NPSS_WSPACE)
+ return aStates[1].usAvail;
+ return 0;
+ }
+ AssertMsgFailed(("%d / %d\n", orc, aStates[0].fStatus));
+
+#else
+ /*
+ * Query via the pipe info.
+ * This will have to lookup and store the pipe name.
+ */
+ union
+ {
+ PIPEINFO PipeInfo;
+ uint8_t abPadding[sizeof(PIPEINFO) + 127];
+ } Buf;
+ APIRET orc = DosQueryNPipeInfo(pThis->hPipe, 1, &Buf, sizeof(Buf));
+ if (orc == NO_ERROR)
+ return Buf.PipeInfo.cbOut;
+ AssertMsgFailed(("%d\n", orc));
+#endif
+
+ return 1;
+}
+
+
+RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbWritten);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPipeTryNonBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ if (cbToWrite > 0)
+ {
+ ULONG cbActual = 0;
+ APIRET orc = DosWrite(pThis->hPipe, pvBuf, cbToWrite, &cbActual);
+ if (orc == NO_ERROR && cbActual == 0)
+ {
+ /* Retry with the request adjusted to the available buffer space. */
+ ULONG cbAvail = rtPipeOs2GetSpace(pThis);
+ orc = DosWrite(pThis->hPipe, pvBuf, RT_MIN(cbAvail, cbToWrite), &cbActual);
+ }
+
+ if (orc == NO_ERROR)
+ {
+ *pcbWritten = cbActual;
+ if (cbActual == 0)
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ {
+ rc = RTErrConvertFromOS2(orc);
+ if (rc == VERR_PIPE_NOT_CONNECTED)
+ rc = VERR_BROKEN_PIPE;
+ }
+ }
+ else
+ *pcbWritten = 0;
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+ AssertPtrNull(pcbWritten);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPipeTryBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ ULONG cbActual = 0;
+ APIRET orc = DosWrite(pThis->hPipe, pvBuf, cbToWrite, &cbActual);
+ if (orc != NO_ERROR)
+ {
+ rc = RTErrConvertFromOS2(orc);
+ if (rc == VERR_PIPE_NOT_CONNECTED)
+ rc = VERR_BROKEN_PIPE;
+ break;
+ }
+ pvBuf = (char const *)pvBuf + cbActual;
+ cbToWrite -= cbActual;
+ cbTotalWritten += cbActual;
+ }
+
+ if (pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ if ( RT_FAILURE(rc)
+ && cbTotalWritten)
+ rc = VINF_SUCCESS;
+ }
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeFlush(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+
+ APIRET orc = DosResetBuffer(pThis->hPipe);
+ if (orc != NO_ERROR)
+ {
+ int rc = RTErrConvertFromOS2(orc);
+ if (rc == VERR_BROKEN_PIPE)
+ {
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fBrokenPipe = true;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ uint64_t const StartMsTS = RTTimeMilliTS();
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = rtPipeOs2EnsureSem(pThis);
+ if (RT_SUCCESS(rc) && cMillies > 0)
+ {
+ /* Stop polling attempts if we might block. */
+ if (pThis->hPollSet == NIL_RTPOLLSET)
+ pThis->hPollSet = (RTPOLLSET)(uintptr_t)0xbeef0042;
+ else
+ rc = VERR_WRONG_ORDER;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ for (unsigned iLoop = 0;; iLoop++)
+ {
+ /*
+ * Check the handle state.
+ */
+ APIRET orc;
+ if (cMillies > 0)
+ {
+ ULONG ulIgnore;
+ orc = DosResetEventSem(pThis->hev, &ulIgnore);
+ AssertMsg(orc == NO_ERROR || orc == ERROR_ALREADY_RESET, ("%d\n", orc));
+ }
+
+ PIPESEMSTATE aStates[4]; RT_ZERO(aStates);
+ orc = DosQueryNPipeSemState((HSEM)pThis->hev, &aStates[0], sizeof(aStates));
+ if (orc != NO_ERROR)
+ {
+ rc = RTErrConvertFromOS2(orc);
+ break;
+ }
+ int i = 0;
+ if (pThis->fRead)
+ while (aStates[i].fStatus == NPSS_WSPACE)
+ i++;
+ else
+ while (aStates[i].fStatus == NPSS_RDATA)
+ i++;
+ if (aStates[i].fStatus == NPSS_CLOSE)
+ break;
+ Assert(aStates[i].fStatus == NPSS_WSPACE || aStates[i].fStatus == NPSS_RDATA || aStates[i].fStatus == NPSS_EOI);
+ if ( aStates[i].fStatus != NPSS_EOI
+ && aStates[i].usAvail > 0)
+ break;
+
+ /*
+ * Check for timeout.
+ */
+ ULONG cMsMaxWait = SEM_INDEFINITE_WAIT;
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
+ if (cElapsed >= cMillies)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ cMsMaxWait = cMillies - (uint32_t)cElapsed;
+ }
+
+ /*
+ * Wait.
+ */
+ RTCritSectLeave(&pThis->CritSect);
+ orc = DosWaitEventSem(pThis->hev, cMsMaxWait);
+ RTCritSectEnter(&pThis->CritSect);
+ if (orc != NO_ERROR && orc != ERROR_TIMEOUT && orc != ERROR_SEM_TIMEOUT )
+ {
+ rc = RTErrConvertFromOS2(orc);
+ break;
+ }
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ if (cMillies > 0)
+ pThis->hPollSet = NIL_RTPOLLSET;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
+ AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ ULONG cbActual = 0;
+ ULONG ulState = 0;
+ AVAILDATA Avail = { 0, 0 };
+ APIRET orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState);
+ if (orc == NO_ERROR)
+ {
+ if (Avail.cbpipe > 0 || ulState == NP_STATE_CONNECTED)
+ *pcbReadable = Avail.cbpipe;
+ else
+ rc = VERR_PIPE_NOT_CONNECTED; /*??*/
+ }
+ else
+ rc = RTErrConvertFromOS2(orc);
+
+ RTCritSectLeave(&pThis->CritSect);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, 0);
+
+ rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
+
+ if (pThis->fRead)
+ {
+ ULONG cbActual = 0;
+ ULONG ulState = 0;
+ AVAILDATA Avail = { 0, 0 };
+ APIRET orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState);
+ if (orc == NO_ERROR && (Avail.cbpipe > 0 || ulState == NP_STATE_CONNECTED))
+ pObjInfo->cbObject = Avail.cbpipe;
+ }
+ else
+ pObjInfo->cbObject = rtPipeOs2GetSpace(pThis);
+ pObjInfo->cbAllocated = RTPIPE_OS2_SIZE; /** @todo this isn't necessarily true if we didn't create it... but, whatever */
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+
+int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtPipeOs2EnsureSem(pThis);
+ if (RT_SUCCESS(rc))
+ *phNative = (RTHCINTPTR)pThis->hev;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+/**
+ * Checks for pending events.
+ *
+ * @returns Event mask or 0.
+ * @param pThis The pipe handle.
+ * @param fEvents The desired events.
+ * @param fResetEvtSem Whether to reset the event semaphore.
+ */
+static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents, bool fResetEvtSem)
+{
+ /*
+ * Reset the event semaphore if we're gonna wait.
+ */
+ APIRET orc;
+ ULONG ulIgnore;
+ if (fResetEvtSem)
+ {
+ orc = DosResetEventSem(pThis->hev, &ulIgnore);
+ AssertMsg(orc == NO_ERROR || orc == ERROR_ALREADY_RESET, ("%d\n", orc));
+ }
+
+ /*
+ * Check for events.
+ */
+ uint32_t fRetEvents = 0;
+ if (pThis->fBrokenPipe)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ else if (pThis->fRead)
+ {
+ ULONG cbActual = 0;
+ ULONG ulState = 0;
+ AVAILDATA Avail = { 0, 0 };
+ orc = DosPeekNPipe(pThis->hPipe, NULL, 0, &cbActual, &Avail, &ulState);
+ if (orc != NO_ERROR)
+ {
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ if (orc == ERROR_BROKEN_PIPE || orc == ERROR_PIPE_NOT_CONNECTED)
+ pThis->fBrokenPipe = true;
+ }
+ else if (Avail.cbpipe > 0)
+ fRetEvents |= RTPOLL_EVT_READ;
+ else if (ulState != NP_STATE_CONNECTED)
+ {
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ pThis->fBrokenPipe = true;
+ }
+ }
+ else
+ {
+ PIPESEMSTATE aStates[4]; RT_ZERO(aStates);
+ orc = DosQueryNPipeSemState((HSEM)pThis->hev, &aStates[0], sizeof(aStates));
+ if (orc == NO_ERROR)
+ {
+ int i = 0;
+ while (aStates[i].fStatus == NPSS_RDATA)
+ i++;
+ if (aStates[i].fStatus == NPSS_CLOSE)
+ {
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ pThis->fBrokenPipe = true;
+ }
+ else if ( aStates[i].fStatus == NPSS_WSPACE
+ && aStates[i].usAvail > 0)
+ fRetEvents |= RTPOLL_EVT_WRITE;
+ }
+ else
+ {
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ if (orc == ERROR_BROKEN_PIPE || orc == ERROR_PIPE_NOT_CONNECTED)
+ pThis->fBrokenPipe = true;
+ }
+ }
+
+ return fRetEvents & (fEvents | RTPOLL_EVT_ERROR);
+}
+
+
+uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, UINT32_MAX);
+
+ /* Check that this is the only current use of this pipe. */
+ uint32_t fRetEvents;
+ if ( pThis->cUsers == 0
+ || pThis->hPollSet == NIL_RTPOLLSET)
+ {
+ fRetEvents = rtPipePollCheck(pThis, fEvents, fNoWait);
+ if (!fRetEvents && !fNoWait)
+ {
+ /* Mark the set busy while waiting. */
+ pThis->cUsers++;
+ pThis->hPollSet = hPollSet;
+ }
+ }
+ else
+ {
+ AssertFailed();
+ fRetEvents = UINT32_MAX;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ return fRetEvents;
+}
+
+
+uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, 0);
+
+ Assert(pThis->cUsers > 0);
+
+ /* harvest events. */
+ uint32_t fRetEvents = rtPipePollCheck(pThis, fEvents, false);
+
+ /* update counters. */
+ pThis->cUsers--;
+ pThis->hPollSet = NIL_RTPOLLSET;
+
+ RTCritSectLeave(&pThis->CritSect);
+ return fRetEvents;
+}
diff --git a/src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp b/src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp
new file mode 100644
index 00000000..bd5e249a
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/rtProcInitExePath-os2.cpp
@@ -0,0 +1,71 @@
+/* $Id: rtProcInitExePath-os2.cpp $ */
+/** @file
+ * IPRT - rtProcInitName, OS/2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include "internal/process.h"
+#include "internal/path.h"
+
+
+DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath)
+{
+ /*
+ * Query the image name from the dynamic linker, convert and return it.
+ */
+ _execname(pszPath, cchPath);
+
+ char const *pszTmp;
+ int rc = rtPathFromNative(&pszTmp, pszPath, NULL);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, cchPath, pszPath), rc);
+ if (pszTmp != pszPath)
+ {
+ rc = RTStrCopy(pszPath, cchPath, pszTmp);
+ rtPathFreeIprt(pszTmp, pszPath);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/sched-os2.cpp b/src/VBox/Runtime/r3/os2/sched-os2.cpp
new file mode 100644
index 00000000..25eb75c3
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/sched-os2.cpp
@@ -0,0 +1,244 @@
+/* $Id: sched-os2.cpp $ */
+/** @file
+ * IPRT - Scheduling, OS/2
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @def OS2_SCHED_ENABLED
+ * Enables the priority scheme. */
+#define OS2_SCHED_ENABLED
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#define INCL_BASE
+#define INCL_ERRORS
+#include <os2.h>
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/sched.h"
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Configuration of one priority.
+ */
+typedef struct
+{
+ /** The priority. */
+ RTPROCPRIORITY enmPriority;
+ /** The name of this priority. */
+ const char *pszName;
+ /** Array scheduler attributes corresponding to each of the thread types. */
+ struct
+ {
+ /** For sanity include the array index. */
+ RTTHREADTYPE enmType;
+ /** The OS/2 priority class. */
+ ULONG ulClass;
+ /** The OS/2 priority delta. */
+ ULONG ulDelta;
+ } aTypes[RTTHREADTYPE_END];
+} PROCPRIORITY;
+
+/** Matches any process priority class. */
+#define ANY_PROCESS_PRIORITY_CLASS (~0U)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Array of static priority configurations.
+ */
+static const PROCPRIORITY g_aPriorities[] =
+{
+ {
+ RTPROCPRIORITY_FLAT, "Flat",
+ {
+ { RTTHREADTYPE_INVALID, ~0, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_GUI, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_IO, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_TIMER, PRTYC_REGULAR, 0 }
+ }
+ },
+ {
+ RTPROCPRIORITY_LOW, "Low",
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 0 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_IDLETIME, 0 },
+ { RTTHREADTYPE_EMULATION, PRTYC_IDLETIME, 0 },
+ { RTTHREADTYPE_DEFAULT, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_GUI, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_MAIN_WORKER, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_IO, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_TIMER, PRTYC_REGULAR, 0 }
+ }
+ },
+ {
+ RTPROCPRIORITY_NORMAL, "Normal",
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_IDLETIME, 31 },
+ { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 5 },
+ { RTTHREADTYPE_GUI, PRTYC_REGULAR, 10 },
+ { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 12 },
+ { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 15 },
+ { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 20 },
+ { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 25 },
+ { RTTHREADTYPE_IO, PRTYC_FOREGROUNDSERVER, 5 },
+ { RTTHREADTYPE_TIMER, PRTYC_TIMECRITICAL, 0 }
+ }
+ },
+ {
+ RTPROCPRIORITY_HIGH, "High",
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 15 },
+ { RTTHREADTYPE_GUI, PRTYC_REGULAR, 20 },
+ { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 25 },
+ { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 30 },
+ { RTTHREADTYPE_DEBUGGER, PRTYC_TIMECRITICAL, 2 },
+ { RTTHREADTYPE_MSG_PUMP, PRTYC_TIMECRITICAL, 3 },
+ { RTTHREADTYPE_IO, PRTYC_TIMECRITICAL, 4 },
+ { RTTHREADTYPE_TIMER, PRTYC_TIMECRITICAL, 5 }
+ }
+ }
+};
+
+/**
+ * The dynamic default priority configuration.
+ *
+ * This can be recalulated at runtime depending on what the
+ * system allow us to do. Presently we don't do this as it's
+ * generally not a bit issue on OS/2 hosts.
+ */
+static PROCPRIORITY g_aDefaultPriority =
+{
+ RTPROCPRIORITY_LOW, "Default",
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, PRTYC_IDLETIME, 30 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, PRTYC_IDLETIME, 31 },
+ { RTTHREADTYPE_EMULATION, PRTYC_REGULAR, 0 },
+ { RTTHREADTYPE_DEFAULT, PRTYC_REGULAR, 5 },
+ { RTTHREADTYPE_GUI, PRTYC_REGULAR, 10 },
+ { RTTHREADTYPE_MAIN_WORKER, PRTYC_REGULAR, 12 },
+ { RTTHREADTYPE_VRDP_IO, PRTYC_REGULAR, 15 },
+ { RTTHREADTYPE_DEBUGGER, PRTYC_REGULAR, 20 },
+ { RTTHREADTYPE_MSG_PUMP, PRTYC_REGULAR, 25 },
+ { RTTHREADTYPE_IO, PRTYC_FOREGROUNDSERVER, 5 },
+ { RTTHREADTYPE_TIMER, PRTYC_TIMECRITICAL, 0 }
+ }
+};
+
+
+/** Pointer to the current priority configuration. */
+static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority;
+
+
+/**
+ * Calculate the scheduling properties for all the threads in the default
+ * process priority, assuming the current thread have the type enmType.
+ *
+ * @returns iprt status code.
+ * @param enmType The thread type to be assumed for the current thread.
+ */
+DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority)
+{
+ Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST);
+
+ if (enmPriority == RTPROCPRIORITY_DEFAULT)
+ {
+ g_pProcessPriority = &g_aDefaultPriority;
+ return VINF_SUCCESS;
+ }
+
+ for (size_t i = 0; i < RT_ELEMENTS(g_aPriorities); i++)
+ if (g_aPriorities[i].enmPriority == enmPriority)
+ {
+ g_pProcessPriority = &g_aPriorities[i];
+ return VINF_SUCCESS;
+ }
+
+ AssertFailedReturn(VERR_INTERNAL_ERROR);
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ AssertMsg(g_pProcessPriority && g_pProcessPriority->aTypes[enmType].enmType == enmType,
+ ("enmType=%d entry=%d\n", enmType, g_pProcessPriority->aTypes[enmType].enmType));
+
+#ifdef OS2_SCHED_ENABLED
+ APIRET rc = DosSetPriority(PRTYS_THREAD, g_pProcessPriority->aTypes[enmType].ulClass, g_pProcessPriority->aTypes[enmType].ulDelta, (ULONG)pThread->Core.Key & 0xffff /*tid*/);
+ AssertMsg(rc == NO_ERROR, ("%d\n", rc));
+ return RTErrConvertFromOS2(rc);
+#else
+ return VINF_SUCCESS;
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/os2/sems-os2.cpp b/src/VBox/Runtime/r3/os2/sems-os2.cpp
new file mode 100644
index 00000000..d4b8136e
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/sems-os2.cpp
@@ -0,0 +1,392 @@
+/* $Id: sems-os2.cpp $ */
+/** @file
+ * IPRT - Semaphores, OS/2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_DOSSEMAPHORES
+#define INCL_ERRORS
+#include <os2.h>
+#undef RT_MAX
+
+#include <iprt/semaphore.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+
+
+/** Converts semaphore to OS/2 handle. */
+#define SEM2HND(Sem) ((LHANDLE)(uintptr_t)Sem)
+
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+
+ /*
+ * Create the semaphore.
+ * (Auto reset, not signaled, private event object.)
+ */
+ HEV hev;
+ int rc = DosCreateEventSem(NULL, &hev, DCE_AUTORESET | DCE_POSTONE, 0);
+ if (!rc)
+ {
+ *phEventSem = (RTSEMEVENT)(void *)hev;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ if (hEventSem == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+
+ /*
+ * Close semaphore handle.
+ */
+ int rc = DosCloseEventSem(SEM2HND(hEventSem));
+ if (!rc)
+ return VINF_SUCCESS;
+ AssertMsgFailed(("Destroy hEventSem %p failed, rc=%d\n", hEventSem, rc));
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
+{
+ /*
+ * Wait for condition.
+ */
+ int rc = DosWaitEventSem(SEM2HND(hEventSem), cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : cMillies);
+ switch (rc)
+ {
+ case NO_ERROR: return VINF_SUCCESS;
+ case ERROR_SEM_TIMEOUT:
+ case ERROR_TIMEOUT: return VERR_TIMEOUT;
+ case ERROR_INTERRUPT: return VERR_INTERRUPTED;
+ default:
+ {
+ AssertMsgFailed(("Wait on hEventSem %p failed, rc=%d\n", hEventSem, rc));
+ return RTErrConvertFromOS2(rc);
+ }
+ }
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ /*
+ * Signal the object.
+ */
+ int rc = DosPostEventSem(SEM2HND(hEventSem));
+ switch (rc)
+ {
+ case NO_ERROR:
+ case ERROR_ALREADY_POSTED:
+ case ERROR_TOO_MANY_POSTS:
+ return VINF_SUCCESS;
+ default:
+ return RTErrConvertFromOS2(rc);
+ }
+}
+
+
+RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+/** @todo implement RTSemEventSetSignaller and friends for OS/2 */
+}
+
+
+RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+
+}
+
+
+RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+
+}
+
+
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the semaphore.
+ * (Manual reset, not signaled, private event object.)
+ */
+ HEV hev;
+ int rc = DosCreateEventSem(NULL, &hev, 0, FALSE);
+ if (!rc)
+ {
+ *phEventMultiSem = (RTSEMEVENTMULTI)(void *)hev;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ if (hEventMultiSem == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+
+ /*
+ * Close semaphore handle.
+ */
+ int rc = DosCloseEventSem(SEM2HND(hEventMultiSem));
+ if (!rc)
+ return VINF_SUCCESS;
+ AssertMsgFailed(("Destroy hEventMultiSem %p failed, rc=%d\n", hEventMultiSem, rc));
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Signal the object.
+ */
+ int rc = DosPostEventSem(SEM2HND(hEventMultiSem));
+ switch (rc)
+ {
+ case NO_ERROR:
+ case ERROR_ALREADY_POSTED:
+ case ERROR_TOO_MANY_POSTS:
+ return VINF_SUCCESS;
+ default:
+ return RTErrConvertFromOS2(rc);
+ }
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Reset the object.
+ */
+ ULONG ulIgnore;
+ int rc = DosResetEventSem(SEM2HND(hEventMultiSem), &ulIgnore);
+ switch (rc)
+ {
+ case NO_ERROR:
+ case ERROR_ALREADY_RESET:
+ return VINF_SUCCESS;
+ default:
+ return RTErrConvertFromOS2(rc);
+ }
+}
+
+
+RTDECL(int) RTSemEventMultiWaitNoResume(RTSEMEVENTMULTI hEventMultiSem, RTMSINTERVAL cMillies)
+{
+ /*
+ * Wait for condition.
+ */
+ int rc = DosWaitEventSem(SEM2HND(hEventMultiSem), cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : cMillies);
+ switch (rc)
+ {
+ case NO_ERROR: return VINF_SUCCESS;
+ case ERROR_SEM_TIMEOUT:
+ case ERROR_TIMEOUT: return VERR_TIMEOUT;
+ case ERROR_INTERRUPT: return VERR_INTERRUPTED;
+ default:
+ {
+ AssertMsgFailed(("Wait on hEventMultiSem %p failed, rc=%d\n", hEventMultiSem, rc));
+ return RTErrConvertFromOS2(rc);
+ }
+ }
+}
+
+
+RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+ /** @todo implement RTSemEventMultiSetSignaller on OS/2 */
+}
+
+
+RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+}
+
+
+RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+}
+
+
+
+#undef RTSemMutexCreate
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+}
+
+
+RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the semaphore.
+ */
+ HMTX hmtx;
+ int rc = DosCreateMutexSem(NULL, &hmtx, 0, FALSE);
+ if (!rc)
+ {
+ /** @todo implement lock validation of OS/2 mutex semaphores. */
+ *phMutexSem = (RTSEMMUTEX)(void *)hmtx;
+ return VINF_SUCCESS;
+ }
+
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ if (hMutexSem == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+
+ /*
+ * Close semaphore handle.
+ */
+ int rc = DosCloseMutexSem(SEM2HND(hMutexSem));
+ if (!rc)
+ return VINF_SUCCESS;
+ AssertMsgFailed(("Destroy hMutexSem %p failed, rc=%d\n", hMutexSem, rc));
+ return RTErrConvertFromOS2(rc);
+}
+
+
+
+RTDECL(uint32_t) RTSemMutexSetSubClass(RTSEMMUTEX hMutexSem, uint32_t uSubClass)
+{
+#if 0 /** @todo def RTSEMMUTEX_STRICT */
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+
+ return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorRec, uSubClass);
+#else
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+#endif
+}
+
+
+#undef RTSemMutexRequestNoResume
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ /*
+ * Lock mutex semaphore.
+ */
+ int rc = DosRequestMutexSem(SEM2HND(hMutexSem), cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : cMillies);
+ switch (rc)
+ {
+ case NO_ERROR: return VINF_SUCCESS;
+ case ERROR_SEM_TIMEOUT:
+ case ERROR_TIMEOUT: return VERR_TIMEOUT;
+ case ERROR_INTERRUPT: return VERR_INTERRUPTED;
+ case ERROR_SEM_OWNER_DIED: return VERR_SEM_OWNER_DIED;
+ default:
+ {
+ AssertMsgFailed(("Wait on hMutexSem %p failed, rc=%d\n", hMutexSem, rc));
+ return RTErrConvertFromOS2(rc);
+ }
+ }
+}
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+// RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+// return rtSemMutexRequestNoResume(hMutexSem, cMillies, &SrcPos);
+ return RTSemMutexRequestNoResume(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Unlock mutex semaphore.
+ */
+ int rc = DosReleaseMutexSem(SEM2HND(hMutexSem));
+ if (!rc)
+ return VINF_SUCCESS;
+ AssertMsgFailed(("Release hMutexSem %p failed, rc=%d\n", hMutexSem, rc));
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Unlock mutex semaphore.
+ */
+ PID pid;
+ TID tid;
+ ULONG cRecursions;
+ int rc = DosQueryMutexSem(SEM2HND(hMutexSem), &pid, &tid, &cRecursions);
+ if (!rc)
+ return cRecursions != 0;
+ AssertMsgFailed(("DosQueryMutexSem %p failed, rc=%d\n", hMutexSem, rc));
+ return rc == ERROR_SEM_OWNER_DIED;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/serialport-os2.cpp b/src/VBox/Runtime/r3/os2/serialport-os2.cpp
new file mode 100644
index 00000000..95158dea
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/serialport-os2.cpp
@@ -0,0 +1,755 @@
+/* $Id: serialport-os2.cpp $ */
+/** @file
+ * IPRT - Serial Port API, OS/2 Implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_BASE
+#define INCL_DOSFILEMGR
+#define INCL_ERRORS
+#define INCL_DOS
+#define INCL_DOSDEVIOCTL
+#define INCL_DOSDEVICES
+#include <os2.h>
+#undef RT_MAX
+
+#include <iprt/serialport.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/cdefs.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Returned data structure for ASYNC_EXTGETBAUDRATE.
+ */
+typedef struct OS2EXTGETBAUDRATEDATA
+{
+ /** Current bit rate. */
+ ULONG uBitRateCur;
+ /** Fraction of the current bit rate. */
+ BYTE bBitRateCurFrac;
+ /** Minimum supported bit rate. */
+ ULONG uBitRateMin;
+ /** Fraction of the minimum bit rate. */
+ BYTE bBitRateCurMin;
+ /** Maximum supported bit rate. */
+ ULONG uBitRateMax;
+ /** Fraction of the maximum bit rate. */
+ BYTE bBitRateCurMax;
+} OS2EXTGETBAUDRATEDATA;
+/** Pointer to the get extended baud rate data packet. */
+typedef OS2EXTGETBAUDRATEDATA *POS2EXTGETBAUDRATEDATA;
+
+
+/**
+ * Data packet for the ASYNC_EXTSETBAUDRATE ioctl.
+ */
+typedef struct OS2EXTSETBAUDRATEDATA
+{
+ /** Current bit rate. */
+ ULONG uBitRate;
+ /** Fraction of the current bit rate. */
+ BYTE bBitRateFrac;
+} OS2EXTSETBAUDRATEDATA;
+/** Pointer to the set extended baud rate data packet. */
+typedef OS2EXTSETBAUDRATEDATA *POS2EXTSETBAUDRATEDATA;
+
+
+/**
+ * Data packet for the ASYNC_GETLINECTRL ioctl.
+ */
+typedef struct OS2GETLINECTRLDATA
+{
+ /** Returns the current amount of data bits in a symbol used for the communication. */
+ BYTE bDataBits;
+ /** Current parity setting. */
+ BYTE bParity;
+ /** Current number of stop bits. */
+ BYTE bStopBits;
+ /** Flag whether a break condition is currently transmitted on the line. */
+ BYTE bTxBrk;
+} OS2GETLINECTRLDATA;
+/** Pointer to the get line control data packet. */
+typedef OS2GETLINECTRLDATA *POS2GETLINECTRLDATA;
+
+
+/**
+ * Data packet for the ASYNC_SETLINECTRL ioctl.
+ */
+typedef struct OS2SETLINECTRLDATA
+{
+ /** Returns the current amount of data bits in a symbol used for the communication. */
+ BYTE bDataBits;
+ /** Current parity setting. */
+ BYTE bParity;
+ /** Current number of stop bits. */
+ BYTE bStopBits;
+} OS2SETLINECTRLDATA;
+/** Pointer to the get line control data packet. */
+typedef OS2SETLINECTRLDATA *POS2SETLINECTRLDATA;
+
+
+/**
+ * Internal serial port state.
+ */
+typedef struct RTSERIALPORTINTERNAL
+{
+ /** Magic value (RTSERIALPORT_MAGIC). */
+ uint32_t u32Magic;
+ /** Flags given while opening the serial port. */
+ uint32_t fOpenFlags;
+ /** The file descriptor of the serial port. */
+ HFILE hDev;
+ /** Flag whether blocking mode is currently enabled. */
+ bool fBlocking;
+ /** Flag whether RTSerialPortEvtPoll() was interrupted by RTSerialPortEvtPollInterrupt(). */
+ volatile bool fInterrupt;
+} RTSERIALPORTINTERNAL;
+/** Pointer to the internal serial port state. */
+typedef RTSERIALPORTINTERNAL *PRTSERIALPORTINTERNAL;
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** Indicator whether the CTS input is set/clear. */
+#define OS2_GET_MODEM_INPUT_CTS RT_BIT(4)
+/** Indicator whether the DSR input is set/clear. */
+#define OS2_GET_MODEM_INPUT_DSR RT_BIT(5)
+/** Indicator whether the RI input is set/clear. */
+#define OS2_GET_MODEM_INPUT_RI RT_BIT(6)
+/** Indicator whether the DCD input is set/clear. */
+#define OS2_GET_MODEM_INPUT_DCD RT_BIT(7)
+
+/** There is something to read on the serial port. */
+#define OS2_GET_COMM_EVT_RX RT_BIT(0)
+/** A receive timeout interrupt was generated on the serial port during a read request. */
+#define OS2_GET_COMM_EVT_RTI RT_BIT(1)
+/** The transmit queue for the serial port is empty. */
+#define OS2_GET_COMM_EVT_TX_EMPTY RT_BIT(2)
+/** The CTS signal changes state. */
+#define OS2_GET_COMM_EVT_CTS_CHG RT_BIT(3)
+/** The DSR signal changes state. */
+#define OS2_GET_COMM_EVT_DSR_CHG RT_BIT(4)
+/** The DCD signal changes state. */
+#define OS2_GET_COMM_EVT_DCD_CHG RT_BIT(5)
+/** A break condition was detected on the serial port. */
+#define OS2_GET_COMM_EVT_BRK RT_BIT(6)
+/** A parity, framing or receive hardware overrun error occurred. */
+#define OS2_GET_COMM_EVT_COMM_ERR RT_BIT(7)
+/** Trailing edge ring indicator was detected. */
+#define OS2_GET_COMM_EVT_RI_TRAIL_EDGE RT_BIT(8)
+
+
+/*********************************************************************************************************************************
+* Global variables *
+*********************************************************************************************************************************/
+/** OS/2 parity value to IPRT parity enum. */
+static RTSERIALPORTPARITY s_aParityConvTbl[] =
+{
+ RTSERIALPORTPARITY_NONE,
+ RTSERIALPORTPARITY_ODD,
+ RTSERIALPORTPARITY_EVEN,
+ RTSERIALPORTPARITY_MARK,
+ RTSERIALPORTPARITY_SPACE
+};
+
+/** OS/2 data bits value to IPRT data bits enum. */
+static RTSERIALPORTDATABITS s_aDataBitsConvTbl[] =
+{
+ RTSERIALPORTDATABITS_INVALID,
+ RTSERIALPORTDATABITS_INVALID,
+ RTSERIALPORTDATABITS_INVALID,
+ RTSERIALPORTDATABITS_INVALID,
+ RTSERIALPORTDATABITS_INVALID,
+ RTSERIALPORTDATABITS_5BITS,
+ RTSERIALPORTDATABITS_6BITS,
+ RTSERIALPORTDATABITS_7BITS,
+ RTSERIALPORTDATABITS_8BITS
+};
+
+/** OS/2 stop bits value to IPRT stop bits enum. */
+static RTSERIALPORTSTOPBITS s_aStopBitsConvTbl[] =
+{
+ RTSERIALPORTSTOPBITS_ONE,
+ RTSERIALPORTSTOPBITS_ONEPOINTFIVE,
+ RTSERIALPORTSTOPBITS_TWO
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * The slow path of rtSerialPortSwitchBlockingMode that does the actual switching.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param fBlocking The desired mode of operation.
+ * @remarks Do not call directly.
+ *
+ * @note Affects only read behavior.
+ */
+static int rtSerialPortSwitchBlockingModeSlow(PRTSERIALPORTINTERNAL pThis, bool fBlocking)
+{
+ DCBINFO DcbInfo;
+ ULONG cbDcbInfo = sizeof(DcbInfo);
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETDCBINFO, NULL, 0, NULL, &DcbInfo, cbDcbInfo, &cbDcbInfo);
+ if (!rcOs2)
+ {
+ DcbInfo.fbTimeout &= ~0x06;
+ DcbInfo.fbTimeout |= fBlocking ? 0x04 : 0x06;
+ rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_SETDCBINFO, &DcbInfo, cbDcbInfo, &cbDcbInfo, NULL, 0, NULL);
+ if (rcOs2)
+ return RTErrConvertFromOS2(rcOs2);
+ }
+ else
+ return RTErrConvertFromOS2(rcOs2);
+
+ pThis->fBlocking = fBlocking;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Switches the serial port to the desired blocking mode if necessary.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param fBlocking The desired mode of operation.
+ *
+ * @note Affects only read behavior.
+ */
+DECLINLINE(int) rtSerialPortSwitchBlockingMode(PRTSERIALPORTINTERNAL pThis, bool fBlocking)
+{
+ if (pThis->fBlocking != fBlocking)
+ return rtSerialPortSwitchBlockingModeSlow(pThis, fBlocking);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags)
+{
+ AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszPortAddress, VERR_INVALID_POINTER);
+ AssertReturn(*pszPortAddress != '\0', VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE),
+ VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ ULONG fOpenMode = OPEN_SHARE_DENYREADWRITE
+ | OPEN_FLAGS_SEQUENTIAL
+ | OPEN_FLAGS_NOINHERIT
+ | OPEN_FLAGS_FAIL_ON_ERROR;
+
+ if ((fFlags & RTSERIALPORT_OPEN_F_READ) && !(fFlags & RTSERIALPORT_OPEN_F_WRITE))
+ fOpenMode |= OPEN_ACCESS_READONLY;
+ else if (!(fFlags & RTSERIALPORT_OPEN_F_READ) && (fFlags & RTSERIALPORT_OPEN_F_WRITE))
+ fOpenMode |= OPEN_ACCESS_WRITEONLY;
+ else
+ fOpenMode |= OPEN_ACCESS_READWRITE;
+
+ pThis->u32Magic = RTSERIALPORT_MAGIC;
+ pThis->fOpenFlags = fFlags;
+ pThis->fInterrupt = false;
+ pThis->fBlocking = true;
+
+ ULONG uAction = 0;
+ ULONG rcOs2 = DosOpen((const UCHAR *)pszPortAddress, &pThis->hDev, &uAction, 0, FILE_NORMAL, FILE_OPEN, fOpenMode, NULL);
+ if (!rcOs2)
+ {
+ /* Switch to a known read blocking mode. */
+ rc = rtSerialPortSwitchBlockingMode(pThis, false);
+ if (RT_SUCCESS(rc))
+ {
+ *phSerialPort = pThis;
+ return VINF_SUCCESS;
+ }
+
+ DosClose(pThis->hDev);
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ if (pThis == NIL_RTSERIALPORT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
+
+ DosClose(pThis->hDev);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1);
+
+ return pThis->hDev;
+}
+
+
+RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
+
+ int rc = rtSerialPortSwitchBlockingMode(pThis, true);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Attempt read.
+ */
+ ULONG cbRead = 0;
+ ULONG rcOs2 = DosRead(pThis->hDev, pvBuf, cbToRead, &cbRead);
+ if (!rcOs2)
+ {
+ if (pcbRead)
+ /* caller can handle partial read. */
+ *pcbRead = cbRead;
+ else
+ {
+ /* Caller expects all to be read. */
+ while (cbToRead > cbRead)
+ {
+ ULONG cbReadPart = 0;
+ rcOs2 = DosRead(pThis->hDev, (uint8_t *)pvBuf + cbRead, cbToRead - cbRead, &cbReadPart);
+ if (rcOs2)
+ return RTErrConvertFromOS2(rcOs2);
+
+ cbRead += cbReadPart;
+ }
+ }
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
+
+ *pcbRead = 0;
+
+ int rc = rtSerialPortSwitchBlockingMode(pThis, false);
+ if (RT_SUCCESS(rc))
+ {
+ ULONG cbThisRead = 0;
+ ULONG rcOs2 = DosRead(pThis->hDev, pvBuf, cbToRead, &cbThisRead);
+ if (!rcOs2)
+ {
+ *pcbRead = cbThisRead;
+
+ if (cbThisRead == 0)
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
+
+ /*
+ * Attempt write.
+ */
+ int rc = VINF_SUCCESS;
+ ULONG cbThisWritten = 0;
+ ULONG rcOs2 = DosWrite(pThis->hDev, pvBuf, cbToWrite, &cbThisWritten);
+ if (!rcOs2)
+ {
+ if (pcbWritten)
+ /* caller can handle partial write. */
+ *pcbWritten = cbThisWritten;
+ else
+ {
+ /** @todo Wait for TX empty and loop. */
+ rc = VERR_NOT_SUPPORTED;
+ }
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
+
+ *pcbWritten = 0;
+
+ int rc = VINF_SUCCESS;
+ ULONG cbThisWritten = 0;
+ ULONG rcOs2 = DosWrite(pThis->hDev, pvBuf, cbToWrite, &cbThisWritten);
+ if (!rcOs2)
+ {
+ *pcbWritten = cbThisWritten;
+ if (!cbThisWritten)
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ int rc = VINF_SUCCESS;
+ OS2EXTGETBAUDRATEDATA ExtBaudRate;
+ ULONG cbExtBaudRate = sizeof(ExtBaudRate);
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_EXTGETBAUDRATE, NULL, 0, NULL, &ExtBaudRate, cbExtBaudRate, &cbExtBaudRate);
+ if (!rcOs2)
+ {
+ OS2GETLINECTRLDATA LineCtrl;
+ ULONG cbLineCtrl = sizeof(LineCtrl);
+ rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETLINECTRL, NULL, 0, NULL, &LineCtrl, cbLineCtrl, &cbLineCtrl);
+ if (!rcOs2)
+ {
+ pCfg->uBaudRate = ExtBaudRate.uBitRateCur;
+ if (LineCtrl.bParity < RT_ELEMENTS(s_aParityConvTbl))
+ pCfg->enmParity = s_aParityConvTbl[LineCtrl.bParity];
+ else
+ rc = VERR_IPE_UNEXPECTED_STATUS;
+
+ if ( RT_SUCCESS(rc)
+ && LineCtrl.bDataBits < RT_ELEMENTS(s_aDataBitsConvTbl)
+ && s_aDataBitsConvTbl[LineCtrl.bDataBits] != RTSERIALPORTDATABITS_INVALID)
+ pCfg->enmDataBitCount = s_aDataBitsConvTbl[LineCtrl.bDataBits];
+ else
+ rc = VERR_IPE_UNEXPECTED_STATUS;
+
+ if ( RT_SUCCESS(rc)
+ && LineCtrl.bStopBits < RT_ELEMENTS(s_aStopBitsConvTbl))
+ pCfg->enmStopBitCount = s_aStopBitsConvTbl[LineCtrl.bStopBits];
+ else
+ rc = VERR_IPE_UNEXPECTED_STATUS;
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ int rc = VINF_SUCCESS;
+ OS2EXTSETBAUDRATEDATA ExtBaudRate;
+ OS2SETLINECTRLDATA LineCtrl;
+ ULONG cbExtBaudRate = sizeof(ExtBaudRate);
+ ULONG cbLineCtrl = sizeof(LineCtrl);
+
+ ExtBaudRate.uBitRate = pCfg->uBaudRate;
+ ExtBaudRate.bBitRateFrac = 0;
+
+ BYTE idx = 0;
+ while (idx < RT_ELEMENTS(s_aParityConvTbl))
+ {
+ if (s_aParityConvTbl[idx] == pCfg->enmParity)
+ {
+ LineCtrl.bParity = idx;
+ break;
+ }
+ idx++;
+ }
+ AssertReturn(idx < RT_ELEMENTS(s_aParityConvTbl), VERR_INTERNAL_ERROR);
+
+ idx = 0;
+ while (idx < RT_ELEMENTS(s_aDataBitsConvTbl))
+ {
+ if (s_aDataBitsConvTbl[idx] == pCfg->enmDataBitCount)
+ {
+ LineCtrl.bDataBits = idx;
+ break;
+ }
+ idx++;
+ }
+ AssertReturn(idx < RT_ELEMENTS(s_aDataBitsConvTbl), VERR_INTERNAL_ERROR);
+
+ idx = 0;
+ while (idx < RT_ELEMENTS(s_aStopBitsConvTbl))
+ {
+ if (s_aStopBitsConvTbl[idx] == pCfg->enmStopBitCount)
+ {
+ LineCtrl.bStopBits = idx;
+ break;
+ }
+ idx++;
+ }
+ AssertReturn(idx < RT_ELEMENTS(s_aStopBitsConvTbl), VERR_INTERNAL_ERROR);
+
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE, &ExtBaudRate, cbExtBaudRate, &cbExtBaudRate, NULL, 0, NULL);
+ if (!rcOs2)
+ {
+ rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_SETLINECTRL, &LineCtrl, cbLineCtrl, &cbLineCtrl, NULL, 0, NULL);
+ if (rcOs2)
+ rc = RTErrConvertFromOS2(rcOs2);
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv,
+ RTMSINTERVAL msTimeout)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
+
+ *pfEvtsRecv = 0;
+
+ /*
+ * We need to kind of busy wait here as there is, to my knowledge, no API
+ * to wait for a COM event.
+ *
+ * @todo Adaptive waiting
+ * @todo Handle rollover after 48days eventually
+ */
+ int rc = VINF_SUCCESS;
+ uint64_t tsStart = RTTimeSystemMilliTS();
+ do
+ {
+ if (ASMAtomicXchgBool(&pThis->fInterrupt, false))
+ {
+ rc = VERR_INTERRUPTED;
+ break;
+ }
+
+ USHORT fCommEvt = 0;
+ ULONG cbCommEvt = sizeof(fCommEvt);
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETCOMMEVENT, NULL, 0, NULL,
+ &fCommEvt, cbCommEvt, &cbCommEvt);
+ if (!rcOs2)
+ {
+ AssertReturn(cbCommEvt = sizeof(fCommEvt), VERR_IPE_UNEXPECTED_STATUS);
+
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX)
+ && (fCommEvt & OS2_GET_COMM_EVT_RX))
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
+
+ /** @todo Is there something better to indicate that there is room in the queue instead of queue is empty? */
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX)
+ && (fCommEvt & OS2_GET_COMM_EVT_TX_EMPTY))
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
+
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED)
+ && (fCommEvt & (OS2_GET_COMM_EVT_CTS_CHG | OS2_GET_COMM_EVT_DSR_CHG | OS2_GET_COMM_EVT_DCD_CHG)))
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
+
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_BREAK_DETECTED)
+ && (fCommEvt & OS2_GET_COMM_EVT_BRK))
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_BREAK_DETECTED;
+
+ if (*pfEvtsRecv != 0)
+ break;
+ }
+ else
+ {
+ rc = RTErrConvertFromOS2(rcOs2);
+ break;
+ }
+
+ uint64_t tsNow = RTTimeSystemMilliTS();
+ if ( msTimeout == RT_INDEFINITE_WAIT
+ || tsNow - tsStart < msTimeout)
+ DosSleep(1);
+ else
+ rc = VERR_TIMEOUT;
+ } while (RT_SUCCESS(rc));
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ ASMAtomicXchgBool(&pThis->fInterrupt, true);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, fSet ? ASYNC_SETBREAKON : ASYNC_SETBREAKOFF,
+ NULL, 0, NULL, NULL, 0, NULL);
+
+ return RTErrConvertFromOS2(rcOs2);
+}
+
+
+RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ MODEMSTATUS MdmSts;
+ ULONG cbMdmSts = sizeof(MdmSts);
+
+ MdmSts.fbModemOn = (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS ? 0x02 : 0x00)
+ | (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR ? 0x01 : 0x00);
+ MdmSts.fbModemOff = 0xff;
+ MdmSts.fbModemOff &= ~( (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS ? 0x02 : 0x00)
+ | (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR ? 0x01 : 0x00));
+
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_SETMODEMCTRL, &MdmSts, cbMdmSts, &cbMdmSts, NULL, 0, NULL);
+
+ return RTErrConvertFromOS2(rcOs2);
+}
+
+
+RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
+
+ *pfStsLines = 0;
+
+ int rc = VINF_SUCCESS;
+ BYTE fStsLines = 0;
+ ULONG cbStsLines = sizeof(fStsLines);
+ ULONG rcOs2 = DosDevIOCtl(pThis->hDev, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, NULL, 0, NULL, &fStsLines, cbStsLines, &cbStsLines);
+ if (!rcOs2)
+ {
+ AssertReturn(cbStsLines == sizeof(BYTE), VERR_IPE_UNEXPECTED_STATUS);
+
+ *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_DCD) ? RTSERIALPORT_STS_LINE_DCD : 0;
+ *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_RI) ? RTSERIALPORT_STS_LINE_RI : 0;
+ *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_DSR) ? RTSERIALPORT_STS_LINE_DSR : 0;
+ *pfStsLines |= (fStsLines & OS2_GET_MODEM_INPUT_CTS) ? RTSERIALPORT_STS_LINE_CTS : 0;
+ }
+ else
+ rc = RTErrConvertFromOS2(rcOs2);
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/systemmem-os2.cpp b/src/VBox/Runtime/r3/os2/systemmem-os2.cpp
new file mode 100644
index 00000000..71eb1adf
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/systemmem-os2.cpp
@@ -0,0 +1,80 @@
+/* $Id: systemmem-os2.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryTotalRam, OS/2 ring-3.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define INCL_DOSMISC
+#define INCL_ERRORS
+#include <os2.h>
+#undef RT_MAX
+
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ ULONG cbMem;
+ APIRET rc = DosQuerySysInfo(QSV_TOTPHYSMEM, QSV_TOTPHYSMEM, &cbMem, sizeof(cbMem));
+ if (rc != NO_ERROR)
+ return RTErrConvertFromOS2(rc);
+
+ *pcb = cbMem;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ ULONG cbAvailMem;
+ APIRET rc = DosQuerySysInfo(QSV_TOTAVAILMEM, QSV_TOTAVAILMEM, &cbAvailMem, sizeof(cbAvailMem));
+ if (rc != NO_ERROR)
+ return RTErrConvertFromOS2(rc);
+
+ *pcb = cbAvailMem;
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/os2/thread-os2.cpp b/src/VBox/Runtime/r3/os2/thread-os2.cpp
new file mode 100644
index 00000000..366f4803
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/thread-os2.cpp
@@ -0,0 +1,330 @@
+/* $Id: thread-os2.cpp $ */
+/** @file
+ * IPRT - Threads, OS/2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#define INCL_BASE
+#include <os2.h>
+#undef RT_MAX
+
+#include <errno.h>
+#include <process.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <InnoTekLIBC/FastInfoBlocks.h>
+#include <InnoTekLIBC/thread.h>
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/cpuset.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to thread local memory which points to the current thread. */
+static PRTTHREADINT *g_ppCurThread;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtThreadNativeMain(void *pvArgs);
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ /*
+ * Allocate thread local memory.
+ */
+ PULONG pul;
+ int rc = DosAllocThreadLocalMemory(1, &pul);
+ if (rc)
+ return VERR_NO_TLS_FOR_SELF;
+ g_ppCurThread = (PRTTHREADINT *)(void *)pul;
+ return VINF_SUCCESS;
+}
+
+
+static void rtThreadOs2BlockSigAlarm(void)
+{
+ /*
+ * Block SIGALRM - required for timer-posix.cpp.
+ * This is done to limit harm done by OSes which doesn't do special SIGALRM scheduling.
+ * It will not help much if someone creates threads directly using pthread_create. :/
+ */
+ sigset_t SigSet;
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, SIGALRM);
+ sigprocmask(SIG_BLOCK, &SigSet, NULL);
+}
+
+
+DECLHIDDEN(void) rtThreadNativeReInitObtrusive(void)
+{
+ rtThreadOs2BlockSigAlarm();
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+
+ *g_ppCurThread = pThread;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ if (pThread == *g_ppCurThread)
+ *g_ppCurThread = NULL;
+}
+
+
+/**
+ * Wrapper which unpacks the params and calls thread function.
+ */
+static void rtThreadNativeMain(void *pvArgs)
+{
+ rtThreadOs2BlockSigAlarm();
+
+ /*
+ * Call common main.
+ */
+ PRTTHREADINT pThread = (PRTTHREADINT)pvArgs;
+ *g_ppCurThread = pThread;
+
+#ifdef fibGetTidPid
+ rtThreadMain(pThread, fibGetTidPid(), &pThread->szName[0]);
+#else
+ rtThreadMain(pThread, _gettid(), &pThread->szName[0]);
+#endif
+
+ *g_ppCurThread = NULL;
+ _endthread();
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread)
+{
+ /*
+ * Default stack size.
+ */
+ if (!pThread->cbStack)
+ pThread->cbStack = 512*1024;
+
+ /*
+ * Create the thread.
+ */
+ int iThreadId = _beginthread(rtThreadNativeMain, NULL, pThread->cbStack, pThread);
+ if (iThreadId > 0)
+ {
+#ifdef fibGetTidPid
+ *pNativeThread = iThreadId | (fibGetPid() << 16);
+#else
+ *pNativeThread = iThreadId;
+#endif
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ PRTTHREADINT pThread = *g_ppCurThread;
+ if (pThread)
+ return (RTTHREAD)pThread;
+ /** @todo import alien threads? */
+ return NULL;
+}
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+#ifdef fibGetTidPid
+ return fibGetTidPid();
+#else
+ return _gettid();
+#endif
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ LogFlow(("RTThreadSleep: cMillies=%d\n", cMillies));
+ DosSleep(cMillies);
+ LogFlow(("RTThreadSleep: returning (cMillies=%d)\n", cMillies));
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies)
+{
+ DosSleep(cMillies);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+ uint64_t u64TS = ASMReadTSC();
+ DosSleep(0);
+ u64TS = ASMReadTSC() - u64TS;
+ bool fRc = u64TS > 1750;
+ LogFlow(("RTThreadYield: returning %d (%llu ticks)\n", fRc, u64TS));
+ return fRc;
+}
+
+
+RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet)
+{
+ union
+ {
+ uint64_t u64;
+ MPAFFINITY mpaff;
+ } u;
+
+ APIRET rc = DosQueryThreadAffinity(AFNTY_THREAD, &u.mpaff);
+ if (!rc)
+ {
+ RTCpuSetFromU64(pCpuSet, u.u64);
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet)
+{
+ union
+ {
+ uint64_t u64;
+ MPAFFINITY mpaff;
+ } u;
+ u.u64 = pCpuSet ? RTCpuSetToU64(pCpuSet) : UINT64_MAX;
+ int rc = DosSetThreadAffinity(&u.mpaff);
+ if (!rc)
+ return VINF_SUCCESS;
+ return RTErrConvertFromOS2(rc);
+}
+
+
+RTR3DECL(RTTLS) RTTlsAlloc(void)
+{
+ AssertCompile(NIL_RTTLS == -1);
+ return __libc_TLSAlloc();
+}
+
+
+RTR3DECL(int) RTTlsAllocEx(PRTTLS piTls, PFNRTTLSDTOR pfnDestructor)
+{
+ int rc;
+ int iTls = __libc_TLSAlloc();
+ if (iTls != -1)
+ {
+ if ( !pfnDestructor
+ || __libc_TLSDestructor(iTls, (void (*)(void *, int, unsigned))pfnDestructor, 0) != -1)
+ {
+ *piTls = iTls;
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromErrno(errno);
+ __libc_TLSFree(iTls);
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ *piTls = NIL_RTTLS;
+ return rc;
+}
+
+
+RTR3DECL(int) RTTlsFree(RTTLS iTls)
+{
+ if (iTls == NIL_RTTLS)
+ return VINF_SUCCESS;
+ if (__libc_TLSFree(iTls) != -1)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(void *) RTTlsGet(RTTLS iTls)
+{
+ return __libc_TLSGet(iTls);
+}
+
+
+RTR3DECL(int) RTTlsGetEx(RTTLS iTls, void **ppvValue)
+{
+ int rc = VINF_SUCCESS;
+ void *pv = __libc_TLSGet(iTls);
+ if (RT_UNLIKELY(!pv))
+ {
+ errno = 0;
+ pv = __libc_TLSGet(iTls);
+ if (!pv && errno)
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ *ppvValue = pv;
+ return rc;
+}
+
+
+RTR3DECL(int) RTTlsSet(RTTLS iTls, void *pvValue)
+{
+ if (__libc_TLSSet(iTls, pvValue) != -1)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTThreadGetExecutionTimeMilli(uint64_t *pKernelTime, uint64_t *pUserTime)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
diff --git a/src/VBox/Runtime/r3/os2/time-os2.cpp b/src/VBox/Runtime/r3/os2/time-os2.cpp
new file mode 100644
index 00000000..e06b9fcd
--- /dev/null
+++ b/src/VBox/Runtime/r3/os2/time-os2.cpp
@@ -0,0 +1,87 @@
+/* $Id: time-os2.cpp $ */
+/** @file
+ * IPRT - Time, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ * --------------------------------------------------------------------
+ *
+ * This code is based on:
+ *
+ * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include <InnoTekLIBC/FastInfoBlocks.h>
+
+#include <iprt/time.h>
+#include "internal/time.h"
+
+/** @todo mscount will roll over after ~48 days. */
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return fibGetMsCount() * UINT64_C(10000000);
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return fibGetMsCount();
+}
+
diff --git a/src/VBox/Runtime/r3/path.cpp b/src/VBox/Runtime/r3/path.cpp
new file mode 100644
index 00000000..bcd8deb1
--- /dev/null
+++ b/src/VBox/Runtime/r3/path.cpp
@@ -0,0 +1,163 @@
+/* $Id: path.cpp $ */
+/** @file
+ * IPRT - Path Manipulation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/path.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include "internal/path.h"
+#include "internal/process.h"
+
+
+#ifdef RT_OS_SOLARIS
+/**
+ * Hack to strip of the architecture subdirectory from the exec dir.
+ *
+ * @returns See RTPathExecDir.
+ * @param pszPath See RTPathExecDir.
+ * @param cchPath See RTPathExecDir.
+ */
+DECLINLINE(int) rtPathSolarisArchHack(char *pszPath, size_t cchPath)
+{
+ int rc = RTPathExecDir(pszPath, cchPath);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszLast = RTPathFilename(pszPath);
+ if ( !strcmp(pszLast, "amd64")
+ || !strcmp(pszLast, "i386"))
+ RTPathStripFilename(pszPath);
+ }
+ return rc;
+}
+#endif
+
+
+RTDECL(int) RTPathExecDir(char *pszPath, size_t cchPath)
+{
+ AssertReturn(g_szrtProcExePath[0], VERR_WRONG_ORDER);
+
+ /*
+ * Calc the length and check if there is space before copying.
+ */
+ size_t cch = g_cchrtProcExeDir;
+ if (cch < cchPath)
+ {
+ memcpy(pszPath, g_szrtProcExePath, cch);
+ pszPath[cch] = '\0';
+ return VINF_SUCCESS;
+ }
+
+ AssertMsgFailed(("Buffer too small (%zu <= %zu)\n", cchPath, cch));
+ return VERR_BUFFER_OVERFLOW;
+}
+
+
+RTDECL(int) RTPathAppPrivateNoArch(char *pszPath, size_t cchPath)
+{
+#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE)
+ return RTStrCopy(pszPath, cchPath, RTPATH_APP_PRIVATE);
+#elif defined(RT_OS_SOLARIS)
+ return rtPathSolarisArchHack(pszPath, cchPath);
+#else
+ return RTPathExecDir(pszPath, cchPath);
+#endif
+}
+
+
+RTDECL(int) RTPathAppPrivateArch(char *pszPath, size_t cchPath)
+{
+#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
+ return RTStrCopy(pszPath, cchPath, RTPATH_APP_PRIVATE_ARCH);
+#else
+ return RTPathExecDir(pszPath, cchPath);
+#endif
+}
+
+
+RTDECL(int) RTPathAppPrivateArchTop(char *pszPath, size_t cchPath)
+{
+#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH_TOP)
+ return RTStrCopy(pszPath, cchPath, RTPATH_APP_PRIVATE_ARCH_TOP);
+#elif !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_PRIVATE_ARCH)
+ return RTStrCopy(pszPath, cchPath, RTPATH_APP_PRIVATE_ARCH);
+#elif defined(RT_OS_SOLARIS)
+ return rtPathSolarisArchHack(pszPath, cchPath);
+#else
+ int rc = RTPathExecDir(pszPath, cchPath);
+ return rc;
+#endif
+}
+
+
+RTDECL(int) RTPathSharedLibs(char *pszPath, size_t cchPath)
+{
+#if !defined(RT_OS_WINDOWS) && defined(RTPATH_SHARED_LIBS)
+ return RTStrCopy(pszPath, cchPath, RTPATH_SHARED_LIBS);
+#else
+ return RTPathExecDir(pszPath, cchPath);
+#endif
+}
+
+
+RTDECL(int) RTPathAppDocs(char *pszPath, size_t cchPath)
+{
+#if !defined(RT_OS_WINDOWS) && defined(RTPATH_APP_DOCS)
+ return RTStrCopy(pszPath, cchPath, RTPATH_APP_DOCS);
+#elif defined(RT_OS_SOLARIS)
+ return rtPathSolarisArchHack(pszPath, cchPath);
+#else
+ return RTPathExecDir(pszPath, cchPath);
+#endif
+}
+
+
+RTR3DECL(int) RTPathGetMode(const char *pszPath, PRTFMODE pfMode)
+{
+ AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
+
+ RTFSOBJINFO ObjInfo;
+ int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ if (RT_SUCCESS(rc))
+ *pfMode = ObjInfo.Attr.fMode;
+
+ return rc;
+}
diff --git a/src/VBox/Runtime/r3/poll.cpp b/src/VBox/Runtime/r3/poll.cpp
new file mode 100644
index 00000000..51286e0c
--- /dev/null
+++ b/src/VBox/Runtime/r3/poll.cpp
@@ -0,0 +1,1148 @@
+/* $Id: poll.cpp $ */
+/** @file
+ * IPRT - Polling I/O Handles, Windows+Posix Implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+
+#elif defined(RT_OS_OS2)
+# define INCL_BASE
+# include <os2.h>
+# include <limits.h>
+# include <sys/socket.h>
+
+#else
+# include <limits.h>
+# include <errno.h>
+# include <sys/poll.h>
+# if defined(RT_OS_SOLARIS)
+# include <sys/socket.h>
+# endif
+#endif
+
+#include <iprt/poll.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/pipe.h>
+#include <iprt/socket.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "internal/pipe.h"
+#define IPRT_INTERNAL_SOCKET_POLLING_ONLY
+#include "internal/socket.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The maximum poll set size.
+ * @remarks To help portability, we set this to the Windows limit. We can lift
+ * this restriction later if it becomes necessary. */
+#define RTPOLL_SET_MAX 64
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Handle entry in a poll set.
+ */
+typedef struct RTPOLLSETHNDENT
+{
+ /** The handle type. */
+ RTHANDLETYPE enmType;
+ /** The handle ID. */
+ uint32_t id;
+ /** The events we're waiting for here. */
+ uint32_t fEvents;
+ /** Set if this is the final entry for this handle.
+ * If the handle is entered more than once, this will be clear for all but
+ * the last entry. */
+ bool fFinalEntry;
+ /** The handle union. */
+ RTHANDLEUNION u;
+} RTPOLLSETHNDENT;
+/** Pointer to a handle entry. */
+typedef RTPOLLSETHNDENT *PRTPOLLSETHNDENT;
+
+
+/**
+ * Poll set data.
+ */
+typedef struct RTPOLLSETINTERNAL
+{
+ /** The magic value (RTPOLLSET_MAGIC). */
+ uint32_t u32Magic;
+ /** Set when someone is polling or making changes. */
+ bool volatile fBusy;
+
+ /** The number of allocated handles. */
+ uint16_t cHandlesAllocated;
+ /** The number of valid handles in the set. */
+ uint16_t cHandles;
+
+#ifdef RT_OS_WINDOWS
+ /** Pointer to an array of native handles. */
+ HANDLE *pahNative;
+#elif defined(RT_OS_OS2)
+ /** The semaphore records. */
+ PSEMRECORD paSemRecs;
+ /** The multiple wait semaphore used for non-socket waits. */
+ HMUX hmux;
+ /** os2_select template. */
+ int *pafdSelect;
+ /** The number of sockets to monitor for read. */
+ uint16_t cReadSockets;
+ /** The number of sockets to monitor for write. */
+ uint16_t cWriteSockets;
+ /** The number of sockets to monitor for exceptions. */
+ uint16_t cXcptSockets;
+ /** The number of pipes. */
+ uint16_t cPipes;
+ /** Pointer to an array of native handles. */
+ PRTHCINTPTR pahNative;
+#else
+ /** Pointer to an array of pollfd structures. */
+ struct pollfd *paPollFds;
+#endif
+ /** Pointer to an array of handles and IDs. */
+ PRTPOLLSETHNDENT paHandles;
+} RTPOLLSETINTERNAL;
+
+
+
+/**
+ * Common worker for RTPoll and RTPollNoResume
+ */
+static int rtPollNoResumeWorker(RTPOLLSETINTERNAL *pThis, uint64_t MsStart, RTMSINTERVAL cMillies,
+ uint32_t *pfEvents, uint32_t *pid)
+{
+ int rc;
+
+ /*
+ * Check for special case, RTThreadSleep...
+ */
+ uint32_t const cHandles = pThis->cHandles;
+ if (cHandles == 0)
+ {
+ if (RT_LIKELY(cMillies != RT_INDEFINITE_WAIT))
+ {
+ rc = RTThreadSleep(cMillies);
+ if (RT_SUCCESS(rc))
+ rc = VERR_TIMEOUT;
+ }
+ else
+ rc = VERR_DEADLOCK;
+ return rc;
+ }
+
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ /*
+ * Check + prepare the handles before waiting.
+ */
+ uint32_t fEvents = 0;
+ bool const fNoWait = cMillies == 0;
+ uint32_t i;
+ for (i = 0; i < cHandles; i++)
+ {
+ switch (pThis->paHandles[i].enmType)
+ {
+ case RTHANDLETYPE_PIPE:
+ fEvents = rtPipePollStart(pThis->paHandles[i].u.hPipe, pThis, pThis->paHandles[i].fEvents,
+ pThis->paHandles[i].fFinalEntry, fNoWait);
+ break;
+
+ case RTHANDLETYPE_SOCKET:
+ fEvents = rtSocketPollStart(pThis->paHandles[i].u.hSocket, pThis, pThis->paHandles[i].fEvents,
+ pThis->paHandles[i].fFinalEntry, fNoWait);
+ break;
+
+ default:
+ AssertFailed();
+ fEvents = UINT32_MAX;
+ break;
+ }
+ if (fEvents)
+ break;
+ }
+ if ( fEvents
+ || fNoWait)
+ {
+
+ if (pid)
+ *pid = pThis->paHandles[i].id;
+ if (pfEvents)
+ *pfEvents = fEvents;
+ rc = !fEvents
+ ? VERR_TIMEOUT
+ : fEvents != UINT32_MAX
+ ? VINF_SUCCESS
+ : VERR_INTERNAL_ERROR_4;
+
+ /* clean up */
+ if (!fNoWait)
+ while (i-- > 0)
+ {
+ switch (pThis->paHandles[i].enmType)
+ {
+ case RTHANDLETYPE_PIPE:
+ rtPipePollDone(pThis->paHandles[i].u.hPipe, pThis->paHandles[i].fEvents,
+ pThis->paHandles[i].fFinalEntry, false);
+ break;
+
+ case RTHANDLETYPE_SOCKET:
+ rtSocketPollDone(pThis->paHandles[i].u.hSocket, pThis->paHandles[i].fEvents,
+ pThis->paHandles[i].fFinalEntry, false);
+ break;
+
+ default:
+ AssertFailed();
+ break;
+ }
+ }
+
+ return rc;
+ }
+
+
+ /*
+ * Wait.
+ */
+# ifdef RT_OS_WINDOWS
+ RT_NOREF_PV(MsStart);
+
+ DWORD dwRc = WaitForMultipleObjectsEx(cHandles, pThis->pahNative,
+ FALSE /*fWaitAll */,
+ cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies,
+ TRUE /*fAlertable*/);
+ AssertCompile(WAIT_OBJECT_0 == 0);
+ if (dwRc < WAIT_OBJECT_0 + cHandles)
+ rc = VERR_INTERRUPTED;
+ else if (dwRc == WAIT_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else if (dwRc == WAIT_IO_COMPLETION)
+ rc = VERR_INTERRUPTED;
+ else if (dwRc == WAIT_FAILED)
+ rc = RTErrConvertFromWin32(GetLastError());
+ else
+ {
+ AssertMsgFailed(("%u (%#x)\n", dwRc, dwRc));
+ rc = VERR_INTERNAL_ERROR_5;
+ }
+
+# else /* RT_OS_OS2 */
+ APIRET orc;
+ ULONG ulUser = 0;
+ uint16_t cSockets = pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets;
+ if (cSockets == 0)
+ {
+ /* Only pipes. */
+ AssertReturn(pThis->cPipes > 0, VERR_INTERNAL_ERROR_2);
+ orc = DosWaitMuxWaitSem(pThis->hmux,
+ cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : RT_MIN(cMillies, SEM_INDEFINITE_WAIT - 1),
+ &ulUser);
+ rc = RTErrConvertFromOS2(orc);
+ }
+ else
+ {
+ int *pafdSelect = (int *)alloca(cSockets + 1);
+ if (pThis->cPipes == 0)
+ {
+ /* Only sockets. */
+ memcpy(pafdSelect, pThis->pafdSelect, sizeof(pThis->pafdSelect[0]) * (cSockets + 1));
+ rc = os2_select(pafdSelect, pThis->cReadSockets, pThis->cWriteSockets, pThis->cXcptSockets,
+ cMillies == RT_INDEFINITE_WAIT ? -1 : (long)RT_MIN(cMillies, LONG_MAX));
+ if (rc > 0)
+ rc = VINF_SUCCESS;
+ else if (rc == 0)
+ rc = VERR_TIMEOUT;
+ else
+ rc = RTErrConvertFromErrno(sock_errno());
+ }
+ else
+ {
+ /* Mix of both - taking the easy way out, not optimal, but whatever... */
+ do
+ {
+ orc = DosWaitMuxWaitSem(pThis->hmux, 8, &ulUser);
+ if (orc != ERROR_TIMEOUT && orc != ERROR_SEM_TIMEOUT)
+ {
+ rc = RTErrConvertFromOS2(orc);
+ break;
+ }
+
+ memcpy(pafdSelect, pThis->pafdSelect, sizeof(pThis->pafdSelect[0]) * (cSockets + 1));
+ rc = os2_select(pafdSelect, pThis->cReadSockets, pThis->cWriteSockets, pThis->cXcptSockets, 8);
+ if (rc != 0)
+ {
+ if (rc > 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(sock_errno());
+ break;
+ }
+ } while (cMillies == RT_INDEFINITE_WAIT || RTTimeMilliTS() - MsStart < cMillies);
+ }
+ }
+# endif /* RT_OS_OS2 */
+
+ /*
+ * Get event (if pending) and do wait cleanup.
+ */
+ bool fHarvestEvents = true;
+ for (i = 0; i < cHandles; i++)
+ {
+ fEvents = 0;
+ switch (pThis->paHandles[i].enmType)
+ {
+ case RTHANDLETYPE_PIPE:
+ fEvents = rtPipePollDone(pThis->paHandles[i].u.hPipe, pThis->paHandles[i].fEvents,
+ pThis->paHandles[i].fFinalEntry, fHarvestEvents);
+ break;
+
+ case RTHANDLETYPE_SOCKET:
+ fEvents = rtSocketPollDone(pThis->paHandles[i].u.hSocket, pThis->paHandles[i].fEvents,
+ pThis->paHandles[i].fFinalEntry, fHarvestEvents);
+ break;
+
+ default:
+ AssertFailed();
+ break;
+ }
+ if ( fEvents
+ && fHarvestEvents)
+ {
+ Assert(fEvents != UINT32_MAX);
+ fHarvestEvents = false;
+ if (pfEvents)
+ *pfEvents = fEvents;
+ if (pid)
+ *pid = pThis->paHandles[i].id;
+ rc = VINF_SUCCESS;
+ }
+ }
+
+#else /* POSIX */
+
+ RT_NOREF_PV(MsStart);
+
+ /* clear the revents. */
+ uint32_t i = pThis->cHandles;
+ while (i-- > 0)
+ pThis->paPollFds[i].revents = 0;
+
+ rc = poll(&pThis->paPollFds[0], pThis->cHandles,
+ cMillies == RT_INDEFINITE_WAIT || cMillies >= INT_MAX
+ ? -1
+ : (int)cMillies);
+ if (rc == 0)
+ return VERR_TIMEOUT;
+ if (rc < 0)
+ return RTErrConvertFromErrno(errno);
+ for (i = 0; i < pThis->cHandles; i++)
+ if (pThis->paPollFds[i].revents)
+ {
+ if (pfEvents)
+ {
+ *pfEvents = 0;
+ if (pThis->paPollFds[i].revents & (POLLIN
+# ifdef POLLRDNORM
+ | POLLRDNORM /* just in case */
+# endif
+# ifdef POLLRDBAND
+ | POLLRDBAND /* ditto */
+# endif
+# ifdef POLLPRI
+ | POLLPRI /* ditto */
+# endif
+# ifdef POLLMSG
+ | POLLMSG /* ditto */
+# endif
+# ifdef POLLWRITE
+ | POLLWRITE /* ditto */
+# endif
+# ifdef POLLEXTEND
+ | POLLEXTEND /* ditto */
+# endif
+ )
+ )
+ *pfEvents |= RTPOLL_EVT_READ;
+
+ if (pThis->paPollFds[i].revents & (POLLOUT
+# ifdef POLLWRNORM
+ | POLLWRNORM /* just in case */
+# endif
+# ifdef POLLWRBAND
+ | POLLWRBAND /* ditto */
+# endif
+ )
+ )
+ *pfEvents |= RTPOLL_EVT_WRITE;
+
+ if (pThis->paPollFds[i].revents & (POLLERR | POLLHUP | POLLNVAL
+# ifdef POLLRDHUP
+ | POLLRDHUP
+# endif
+ )
+ )
+ *pfEvents |= RTPOLL_EVT_ERROR;
+
+# if defined(RT_OS_SOLARIS)
+ /* Solaris does not return POLLHUP for sockets, just POLLIN. Check if a
+ POLLIN should also have RTPOLL_EVT_ERROR set or not, so we present a
+ behaviour more in line with linux and BSDs. Note that this will not
+ help is only RTPOLL_EVT_ERROR was requested, that will require
+ extending this hack quite a bit further (restart poll): */
+ if ( *pfEvents == RTPOLL_EVT_READ
+ && pThis->paHandles[i].enmType == RTHANDLETYPE_SOCKET)
+ {
+ uint8_t abBuf[64];
+ ssize_t rcRecv = recv(pThis->paPollFds[i].fd, abBuf, sizeof(abBuf), MSG_PEEK | MSG_DONTWAIT);
+ if (rcRecv == 0)
+ *pfEvents |= RTPOLL_EVT_ERROR;
+ }
+# endif
+ }
+ if (pid)
+ *pid = pThis->paHandles[i].id;
+ return VINF_SUCCESS;
+ }
+
+ AssertFailed();
+ RTThreadYield();
+ rc = VERR_INTERRUPTED;
+
+#endif /* POSIX */
+
+ return rc;
+}
+
+
+RTDECL(int) RTPoll(RTPOLLSET hPollSet, RTMSINTERVAL cMillies, uint32_t *pfEvents, uint32_t *pid)
+{
+ RTPOLLSETINTERNAL *pThis = hPollSet;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrNull(pfEvents);
+ AssertPtrNull(pid);
+
+ /*
+ * Set the busy flag and do the job.
+ */
+ AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
+
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT || cMillies == 0)
+ {
+ do rc = rtPollNoResumeWorker(pThis, 0, cMillies, pfEvents, pid);
+ while (rc == VERR_INTERRUPTED);
+ }
+ else
+ {
+ uint64_t MsStart = RTTimeMilliTS();
+ rc = rtPollNoResumeWorker(pThis, MsStart, cMillies, pfEvents, pid);
+ while (RT_UNLIKELY(rc == VERR_INTERRUPTED))
+ {
+ if (RTTimeMilliTS() - MsStart >= cMillies)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ rc = rtPollNoResumeWorker(pThis, MsStart, cMillies, pfEvents, pid);
+ }
+ }
+
+ ASMAtomicWriteBool(&pThis->fBusy, false);
+
+ return rc;
+}
+
+
+RTDECL(int) RTPollNoResume(RTPOLLSET hPollSet, RTMSINTERVAL cMillies, uint32_t *pfEvents, uint32_t *pid)
+{
+ RTPOLLSETINTERNAL *pThis = hPollSet;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrNull(pfEvents);
+ AssertPtrNull(pid);
+
+ /*
+ * Set the busy flag and do the job.
+ */
+ AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
+
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT || cMillies == 0)
+ rc = rtPollNoResumeWorker(pThis, 0, cMillies, pfEvents, pid);
+ else
+ rc = rtPollNoResumeWorker(pThis, RTTimeMilliTS(), cMillies, pfEvents, pid);
+
+ ASMAtomicWriteBool(&pThis->fBusy, false);
+
+ return rc;
+}
+
+
+RTDECL(int) RTPollSetCreate(PRTPOLLSET phPollSet)
+{
+ AssertPtrReturn(phPollSet, VERR_INVALID_POINTER);
+ RTPOLLSETINTERNAL *pThis = (RTPOLLSETINTERNAL *)RTMemAlloc(sizeof(RTPOLLSETINTERNAL));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->fBusy = false;
+ pThis->cHandles = 0;
+ pThis->cHandlesAllocated = 0;
+#ifdef RT_OS_WINDOWS
+ pThis->pahNative = NULL;
+#elif defined(RT_OS_OS2)
+ pThis->hmux = NULLHANDLE;
+ APIRET orc = DosCreateMuxWaitSem(NULL, &pThis->hmux, 0, NULL, DCMW_WAIT_ANY);
+ if (orc != NO_ERROR)
+ {
+ RTMemFree(pThis);
+ return RTErrConvertFromOS2(orc);
+ }
+ pThis->pafdSelect = NULL;
+ pThis->cReadSockets = 0;
+ pThis->cWriteSockets = 0;
+ pThis->cXcptSockets = 0;
+ pThis->cPipes = 0;
+ pThis->pahNative = NULL;
+#else
+ pThis->paPollFds = NULL;
+#endif
+ pThis->paHandles = NULL;
+ pThis->u32Magic = RTPOLLSET_MAGIC;
+
+ *phPollSet = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPollSetDestroy(RTPOLLSET hPollSet)
+{
+ RTPOLLSETINTERNAL *pThis = hPollSet;
+ if (pThis == NIL_RTPOLLSET)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
+
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTPOLLSET_MAGIC);
+#ifdef RT_OS_WINDOWS
+ RTMemFree(pThis->pahNative);
+ pThis->pahNative = NULL;
+#elif defined(RT_OS_OS2)
+ DosCloseMuxWaitSem(pThis->hmux);
+ pThis->hmux = NULLHANDLE;
+ RTMemFree(pThis->pafdSelect);
+ pThis->pafdSelect = NULL;
+ RTMemFree(pThis->pahNative);
+ pThis->pahNative = NULL;
+#else
+ RTMemFree(pThis->paPollFds);
+ pThis->paPollFds = NULL;
+#endif
+ RTMemFree(pThis->paHandles);
+ pThis->paHandles = NULL;
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+#ifdef RT_OS_OS2
+
+/**
+ * Checks if @a fd is in the specific socket subset.
+ *
+ * @returns true / false.
+ * @param pThis The poll set instance.
+ * @param iStart The index to start at.
+ * @param cFds The number of sockets to check.
+ * @param fd The socket to look for.
+ */
+static bool rtPollSetOs2IsSocketInSet(RTPOLLSETINTERNAL *pThis, uint16_t iStart, uint16_t cFds, int fd)
+{
+ int const *pfd = pThis->pafdSelect + iStart;
+ while (cFds-- > 0)
+ {
+ if (*pfd == fd)
+ return true;
+ pfd++;
+ }
+ return false;
+}
+
+
+/**
+ * Removes a socket from a select template subset.
+ *
+ * @param pThis The poll set instance.
+ * @param iStart The index to start at.
+ * @param pcSubSet The subset counter to decrement.
+ * @param fd The socket to remove.
+ */
+static void rtPollSetOs2RemoveSocket(RTPOLLSETINTERNAL *pThis, uint16_t iStart, uint16_t *pcFds, int fd)
+{
+ uint16_t cFds = *pcFds;
+ while (cFds-- > 0)
+ {
+ if (pThis->pafdSelect[iStart] == fd)
+ break;
+ iStart++;
+ }
+ AssertReturnVoid(iStart != UINT16_MAX);
+
+ /* Note! We keep a -1 entry at the end of the set, thus the + 1. */
+ memmove(&pThis->pafdSelect[iStart],
+ &pThis->pafdSelect[iStart + 1],
+ pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets + 1 - 1 - iStart);
+ *pcFds -= 1;
+
+ Assert(pThis->pafdSelect[pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets] == -1);
+}
+
+
+/**
+ * Adds a socket to a select template subset.
+ *
+ * @param pThis The poll set instance.
+ * @param iInsert The insertion point.
+ * ASSUMED to be at the end of the subset.
+ * @param pcSubSet The subset counter to increment.
+ * @param fd The socket to add.
+ */
+static void rtPollSetOs2AddSocket(RTPOLLSETINTERNAL *pThis, uint16_t iInsert, uint16_t *pcFds, int fd)
+{
+ Assert(!rtPollSetOs2IsSocketInSet(pThis, iInsert - *pcFds, *pcFds, fd));
+
+ /* Note! We keep a -1 entry at the end of the set, thus the + 1. */
+ memmove(&pThis->pafdSelect[iInsert + 1],
+ &pThis->pafdSelect[iInsert],
+ pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets + 1 - iInsert);
+ pThis->pafdSelect[iInsert] = fd;
+ *pcFds += 1;
+
+ Assert(pThis->pafdSelect[pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets] == -1);
+}
+
+
+/**
+ * OS/2 specific RTPollSetAdd worker.
+ *
+ * @returns IPRT status code.
+ * @param pThis The poll set instance.
+ * @param i The index of the new handle (not committed).
+ * @param fEvents The events to poll for.
+ */
+static int rtPollSetOs2Add(RTPOLLSETINTERNAL *pThis, unsigned i, uint32_t fEvents)
+{
+ if (pThis->paHandles[i].enmType == RTHANDLETYPE_SOCKET)
+ {
+ int const fdSocket = pThis->pahNative[i];
+ if ( (fEvents & RTPOLL_EVT_READ)
+ && rtPollSetOs2IsSocketInSet(pThis, 0, pThis->cReadSockets, fdSocket))
+ rtPollSetOs2AddSocket(pThis, pThis->cReadSockets, &pThis->cReadSockets, fdSocket);
+
+ if ( (fEvents & RTPOLL_EVT_WRITE)
+ && rtPollSetOs2IsSocketInSet(pThis, pThis->cReadSockets, pThis->cWriteSockets, fdSocket))
+ rtPollSetOs2AddSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets, &pThis->cWriteSockets, fdSocket);
+
+ if ( (fEvents & RTPOLL_EVT_ERROR)
+ && rtPollSetOs2IsSocketInSet(pThis, pThis->cReadSockets + pThis->cWriteSockets, pThis->cXcptSockets, fdSocket))
+ rtPollSetOs2AddSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets,
+ &pThis->cXcptSockets, fdSocket);
+ }
+ else if (pThis->paHandles[i].enmType == RTHANDLETYPE_PIPE)
+ {
+ SEMRECORD Rec = { (HSEM)pThis->pahNative[i], pThis->paHandles[i].id };
+ APIRET orc = DosAddMuxWaitSem(pThis->hmux, &Rec);
+ if (orc != NO_ERROR && orc != ERROR_DUPLICATE_HANDLE)
+ return RTErrConvertFromOS2(orc);
+ pThis->cPipes++;
+ }
+ else
+ AssertFailedReturn(VERR_INTERNAL_ERROR_2);
+ return VINF_SUCCESS;
+}
+
+#endif /* RT_OS_OS2 */
+
+/**
+ * Grows the poll set.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_MEMORY.
+ * @param pThis The poll set instance.
+ * @param cHandlesNew The new poll set size.
+ */
+static int rtPollSetGrow(RTPOLLSETINTERNAL *pThis, uint32_t cHandlesNew)
+{
+ Assert(cHandlesNew > pThis->cHandlesAllocated);
+
+ /* The common array. */
+ void *pvNew = RTMemRealloc(pThis->paHandles, cHandlesNew * sizeof(pThis->paHandles[0]));
+ if (!pvNew)
+ return VERR_NO_MEMORY;
+ pThis->paHandles = (PRTPOLLSETHNDENT)pvNew;
+
+
+ /* OS specific handles */
+#if defined(RT_OS_WINDOWS)
+ pvNew = RTMemRealloc(pThis->pahNative, cHandlesNew * sizeof(pThis->pahNative[0]));
+ if (!pvNew)
+ return VERR_NO_MEMORY;
+ pThis->pahNative = (HANDLE *)pvNew;
+
+#elif defined(RT_OS_OS2)
+ pvNew = RTMemRealloc(pThis->pahNative, cHandlesNew * sizeof(pThis->pahNative[0]));
+ if (!pvNew)
+ return VERR_NO_MEMORY;
+ pThis->pahNative = (PRTHCINTPTR)pvNew;
+
+ pvNew = RTMemRealloc(pThis->pafdSelect, (cHandlesNew * 3 + 1) * sizeof(pThis->pafdSelect[0]));
+ if (!pvNew)
+ return VERR_NO_MEMORY;
+ pThis->pafdSelect = (int *)pvNew;
+ if (pThis->cHandlesAllocated == 0)
+ pThis->pafdSelect[0] = -1;
+
+#else
+ pvNew = RTMemRealloc(pThis->paPollFds, cHandlesNew * sizeof(pThis->paPollFds[0]));
+ if (!pvNew)
+ return VERR_NO_MEMORY;
+ pThis->paPollFds = (struct pollfd *)pvNew;
+
+#endif
+
+ pThis->cHandlesAllocated = (uint16_t)cHandlesNew;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPollSetAdd(RTPOLLSET hPollSet, PCRTHANDLE pHandle, uint32_t fEvents, uint32_t id)
+{
+ /*
+ * Validate the input (tedious).
+ */
+ RTPOLLSETINTERNAL *pThis = hPollSet;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fEvents & ~RTPOLL_EVT_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(fEvents, VERR_INVALID_PARAMETER);
+ AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
+
+ if (!pHandle)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pHandle, VERR_INVALID_POINTER);
+ AssertReturn(pHandle->enmType > RTHANDLETYPE_INVALID && pHandle->enmType < RTHANDLETYPE_END, VERR_INVALID_PARAMETER);
+
+ /*
+ * Set the busy flag and do the job.
+ */
+
+ int rc = VINF_SUCCESS;
+ RTHCINTPTR hNative = -1;
+ RTHANDLEUNION uh;
+ uh.uInt = 0;
+ switch (pHandle->enmType)
+ {
+ case RTHANDLETYPE_PIPE:
+ uh.hPipe = pHandle->u.hPipe;
+ if (uh.hPipe == NIL_RTPIPE)
+ return VINF_SUCCESS;
+ rc = rtPipePollGetHandle(uh.hPipe, fEvents, &hNative);
+ break;
+
+ case RTHANDLETYPE_SOCKET:
+ uh.hSocket = pHandle->u.hSocket;
+ if (uh.hSocket == NIL_RTSOCKET)
+ return VINF_SUCCESS;
+ rc = rtSocketPollGetHandle(uh.hSocket, fEvents, &hNative);
+ break;
+
+ case RTHANDLETYPE_FILE:
+ AssertMsgFailed(("Files are always ready for reading/writing and thus not pollable. Use native APIs for special devices.\n"));
+ rc = VERR_POLL_HANDLE_NOT_POLLABLE;
+ break;
+
+ case RTHANDLETYPE_THREAD:
+ AssertMsgFailed(("Thread handles are currently not pollable\n"));
+ rc = VERR_POLL_HANDLE_NOT_POLLABLE;
+ break;
+
+ default:
+ AssertMsgFailed(("\n"));
+ rc = VERR_POLL_HANDLE_NOT_POLLABLE;
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
+
+ uint32_t const i = pThis->cHandles;
+
+ /* Check that the handle ID doesn't exist already. */
+ uint32_t iPrev = UINT32_MAX;
+ uint32_t j = i;
+ while (j-- > 0)
+ {
+ if (pThis->paHandles[j].id == id)
+ {
+ rc = VERR_POLL_HANDLE_ID_EXISTS;
+ break;
+ }
+ if ( pThis->paHandles[j].enmType == pHandle->enmType
+ && pThis->paHandles[j].u.uInt == uh.uInt)
+ iPrev = j;
+ }
+
+ /* Check that we won't overflow the poll set now. */
+ if ( RT_SUCCESS(rc)
+ && i + 1 > RTPOLL_SET_MAX)
+ rc = VERR_POLL_SET_IS_FULL;
+
+ /* Grow the tables if necessary. */
+ if (RT_SUCCESS(rc) && i + 1 > pThis->cHandlesAllocated)
+ rc = rtPollSetGrow(pThis, pThis->cHandlesAllocated + 32);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add the handles to the two parallel arrays.
+ */
+#ifdef RT_OS_WINDOWS
+ pThis->pahNative[i] = (HANDLE)hNative;
+#elif defined(RT_OS_OS2)
+ pThis->pahNative[i] = hNative;
+#else
+ pThis->paPollFds[i].fd = (int)hNative;
+ pThis->paPollFds[i].revents = 0;
+ pThis->paPollFds[i].events = 0;
+ if (fEvents & RTPOLL_EVT_READ)
+ pThis->paPollFds[i].events |= POLLIN;
+ if (fEvents & RTPOLL_EVT_WRITE)
+ pThis->paPollFds[i].events |= POLLOUT;
+ if (fEvents & RTPOLL_EVT_ERROR)
+# ifdef RT_OS_DARWIN
+ pThis->paPollFds[i].events |= POLLERR | POLLHUP;
+# else
+ pThis->paPollFds[i].events |= POLLERR;
+# endif
+#endif
+ pThis->paHandles[i].enmType = pHandle->enmType;
+ pThis->paHandles[i].u = uh;
+ pThis->paHandles[i].id = id;
+ pThis->paHandles[i].fEvents = fEvents;
+ pThis->paHandles[i].fFinalEntry = true;
+
+ if (iPrev != UINT32_MAX)
+ {
+ Assert(pThis->paHandles[iPrev].fFinalEntry);
+ pThis->paHandles[iPrev].fFinalEntry = false;
+ }
+
+ /*
+ * Validations and OS specific updates.
+ */
+#ifdef RT_OS_WINDOWS
+ /* none */
+#elif defined(RT_OS_OS2)
+ rc = rtPollSetOs2Add(pThis, i, fEvents);
+#else /* POSIX */
+ if (poll(&pThis->paPollFds[i], 1, 0) < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ pThis->paPollFds[i].fd = -1;
+ }
+#endif /* POSIX */
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Commit it to the set.
+ */
+ pThis->cHandles++; Assert(pThis->cHandles == i + 1);
+ rc = VINF_SUCCESS;
+ }
+ }
+ }
+
+ ASMAtomicWriteBool(&pThis->fBusy, false);
+ return rc;
+}
+
+
+RTDECL(int) RTPollSetRemove(RTPOLLSET hPollSet, uint32_t id)
+{
+ /*
+ * Validate the input.
+ */
+ RTPOLLSETINTERNAL *pThis = hPollSet;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
+
+ /*
+ * Set the busy flag and do the job.
+ */
+ AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
+
+ int rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
+ uint32_t i = pThis->cHandles;
+ while (i-- > 0)
+ if (pThis->paHandles[i].id == id)
+ {
+ /* Save some details for the duplicate searching. */
+ bool const fFinalEntry = pThis->paHandles[i].fFinalEntry;
+ RTHANDLETYPE const enmType = pThis->paHandles[i].enmType;
+ RTHANDLEUNION const uh = pThis->paHandles[i].u;
+#ifdef RT_OS_OS2
+ uint32_t fRemovedEvents = pThis->paHandles[i].fEvents;
+ RTHCINTPTR const hNative = pThis->pahNative[i];
+#endif
+
+ /* Remove the entry. */
+ pThis->cHandles--;
+ size_t const cToMove = pThis->cHandles - i;
+ if (cToMove)
+ {
+ memmove(&pThis->paHandles[i], &pThis->paHandles[i + 1], cToMove * sizeof(pThis->paHandles[i]));
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ memmove(&pThis->pahNative[i], &pThis->pahNative[i + 1], cToMove * sizeof(pThis->pahNative[i]));
+#else
+ memmove(&pThis->paPollFds[i], &pThis->paPollFds[i + 1], cToMove * sizeof(pThis->paPollFds[i]));
+#endif
+ }
+
+ /* Check for duplicate and set the fFinalEntry flag. */
+ if (fFinalEntry)
+ while (i-- > 0)
+ if ( pThis->paHandles[i].u.uInt == uh.uInt
+ && pThis->paHandles[i].enmType == enmType)
+ {
+ Assert(!pThis->paHandles[i].fFinalEntry);
+ pThis->paHandles[i].fFinalEntry = true;
+ break;
+ }
+
+#ifdef RT_OS_OS2
+ /*
+ * Update OS/2 wait structures.
+ */
+ uint32_t fNewEvents = 0;
+ i = pThis->cHandles;
+ while (i-- > 0)
+ if ( pThis->paHandles[i].u.uInt == uh.uInt
+ && pThis->paHandles[i].enmType == enmType)
+ fNewEvents |= pThis->paHandles[i].fEvents;
+ if (enmType == RTHANDLETYPE_PIPE)
+ {
+ pThis->cPipes--;
+ if (fNewEvents == 0)
+ {
+ APIRET orc = DosDeleteMuxWaitSem(pThis->hmux, (HSEM)hNative);
+ AssertMsg(orc == NO_ERROR, ("%d\n", orc));
+ }
+ }
+ else if ( fNewEvents != (fNewEvents | fRemovedEvents)
+ && enmType == RTHANDLETYPE_SOCKET)
+ {
+ fRemovedEvents = fNewEvents ^ (fNewEvents | fRemovedEvents);
+ if (fRemovedEvents & RTPOLL_EVT_ERROR)
+ rtPollSetOs2RemoveSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets, &pThis->cXcptSockets, (int)hNative);
+ if (fRemovedEvents & RTPOLL_EVT_WRITE)
+ rtPollSetOs2RemoveSocket(pThis, pThis->cReadSockets, &pThis->cWriteSockets, (int)hNative);
+ if (fRemovedEvents & RTPOLL_EVT_READ)
+ rtPollSetOs2RemoveSocket(pThis, 0, &pThis->cReadSockets, (int)hNative);
+ }
+#endif /* RT_OS_OS2 */
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ ASMAtomicWriteBool(&pThis->fBusy, false);
+ return rc;
+}
+
+
+RTDECL(int) RTPollSetQueryHandle(RTPOLLSET hPollSet, uint32_t id, PRTHANDLE pHandle)
+{
+ /*
+ * Validate the input.
+ */
+ RTPOLLSETINTERNAL *pThis = hPollSet;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pHandle, VERR_INVALID_POINTER);
+
+ /*
+ * Set the busy flag and do the job.
+ */
+ AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
+
+ int rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
+ uint32_t i = pThis->cHandles;
+ while (i-- > 0)
+ if (pThis->paHandles[i].id == id)
+ {
+ if (pHandle)
+ {
+ pHandle->enmType = pThis->paHandles[i].enmType;
+ pHandle->u = pThis->paHandles[i].u;
+ }
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ ASMAtomicWriteBool(&pThis->fBusy, false);
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTPollSetGetCount(RTPOLLSET hPollSet)
+{
+ /*
+ * Validate the input.
+ */
+ RTPOLLSETINTERNAL *pThis = hPollSet;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, UINT32_MAX);
+
+ /*
+ * Set the busy flag and do the job.
+ */
+ AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), UINT32_MAX);
+ uint32_t cHandles = pThis->cHandles;
+ ASMAtomicWriteBool(&pThis->fBusy, false);
+
+ return cHandles;
+}
+
+RTDECL(int) RTPollSetEventsChange(RTPOLLSET hPollSet, uint32_t id, uint32_t fEvents)
+{
+ /*
+ * Validate the input.
+ */
+ RTPOLLSETINTERNAL *pThis = hPollSet;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPOLLSET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(id != UINT32_MAX, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fEvents & ~RTPOLL_EVT_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(fEvents, VERR_INVALID_PARAMETER);
+
+ /*
+ * Set the busy flag and do the job.
+ */
+ AssertReturn(ASMAtomicCmpXchgBool(&pThis->fBusy, true, false), VERR_CONCURRENT_ACCESS);
+
+ int rc = VERR_POLL_HANDLE_ID_NOT_FOUND;
+ uint32_t i = pThis->cHandles;
+ while (i-- > 0)
+ if (pThis->paHandles[i].id == id)
+ {
+ if (pThis->paHandles[i].fEvents != fEvents)
+ {
+#if defined(RT_OS_WINDOWS)
+ /*nothing*/
+#elif defined(RT_OS_OS2)
+ if (pThis->paHandles[i].enmType == RTHANDLETYPE_SOCKET)
+ {
+ uint32_t fOldEvents = 0;
+ uint32_t j = pThis->cHandles;
+ while (j-- > 0)
+ if ( pThis->paHandles[j].enmType == RTHANDLETYPE_SOCKET
+ && pThis->paHandles[j].u.uInt == pThis->paHandles[i].u.uInt
+ && j != i)
+ fOldEvents |= pThis->paHandles[j].fEvents;
+ uint32_t fNewEvents = fOldEvents | fEvents;
+ fOldEvents |= pThis->paHandles[i].fEvents;
+ if (fOldEvents != fEvents)
+ {
+ int const fdSocket = pThis->pahNative[i];
+ uint32_t const fChangedEvents = fOldEvents ^ fNewEvents;
+
+ if ((fChangedEvents & RTPOLL_EVT_READ) && (fNewEvents & RTPOLL_EVT_READ))
+ rtPollSetOs2AddSocket(pThis, pThis->cReadSockets, &pThis->cReadSockets, fdSocket);
+ else if (fChangedEvents & RTPOLL_EVT_READ)
+ rtPollSetOs2RemoveSocket(pThis, 0, &pThis->cReadSockets, fdSocket);
+
+ if ((fChangedEvents & RTPOLL_EVT_WRITE) && (fNewEvents & RTPOLL_EVT_WRITE))
+ rtPollSetOs2AddSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets,
+ &pThis->cWriteSockets, fdSocket);
+ else if (fChangedEvents & RTPOLL_EVT_WRITE)
+ rtPollSetOs2RemoveSocket(pThis, pThis->cReadSockets, &pThis->cWriteSockets, fdSocket);
+
+ if ((fChangedEvents & RTPOLL_EVT_ERROR) && (fNewEvents & RTPOLL_EVT_ERROR))
+ rtPollSetOs2AddSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets + pThis->cXcptSockets,
+ &pThis->cXcptSockets, fdSocket);
+ else if (fChangedEvents & RTPOLL_EVT_ERROR)
+ rtPollSetOs2RemoveSocket(pThis, pThis->cReadSockets + pThis->cWriteSockets, &pThis->cXcptSockets,
+ fdSocket);
+ }
+ }
+#else
+ pThis->paPollFds[i].events = 0;
+ if (fEvents & RTPOLL_EVT_READ)
+ pThis->paPollFds[i].events |= POLLIN;
+ if (fEvents & RTPOLL_EVT_WRITE)
+ pThis->paPollFds[i].events |= POLLOUT;
+ if (fEvents & RTPOLL_EVT_ERROR)
+ pThis->paPollFds[i].events |= POLLERR;
+#endif
+ pThis->paHandles[i].fEvents = fEvents;
+ }
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ ASMAtomicWriteBool(&pThis->fBusy, false);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/Makefile.kup b/src/VBox/Runtime/r3/posix/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/Makefile.kup
diff --git a/src/VBox/Runtime/r3/posix/RTFileQueryFsSizes-posix.cpp b/src/VBox/Runtime/r3/posix/RTFileQueryFsSizes-posix.cpp
new file mode 100644
index 00000000..886c3844
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTFileQueryFsSizes-posix.cpp
@@ -0,0 +1,78 @@
+/* $Id: RTFileQueryFsSizes-posix.cpp $ */
+/** @file
+ * IPRT - File I/O, RTFileFsQuerySizes, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+
+#include <errno.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/statvfs.h>
+
+#include <iprt/file.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+
+
+RTR3DECL(int) RTFileQueryFsSizes(RTFILE hFile, PRTFOFF pcbTotal, RTFOFF *pcbFree,
+ uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ struct statvfs StatVFS;
+ RT_ZERO(StatVFS);
+ if (fstatvfs(RTFileToNative(hFile), &StatVFS))
+ return RTErrConvertFromErrno(errno);
+
+ /*
+ * Calc the returned values.
+ */
+ if (pcbTotal)
+ *pcbTotal = (RTFOFF)StatVFS.f_blocks * StatVFS.f_frsize;
+ if (pcbFree)
+ *pcbFree = (RTFOFF)StatVFS.f_bavail * StatVFS.f_frsize;
+ if (pcbBlock)
+ *pcbBlock = StatVFS.f_frsize;
+ /* no idea how to get the sector... */
+ if (pcbSector)
+ *pcbSector = 512;
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/RTFileSetAllocationSize-posix.cpp b/src/VBox/Runtime/r3/posix/RTFileSetAllocationSize-posix.cpp
new file mode 100644
index 00000000..534754ac
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTFileSetAllocationSize-posix.cpp
@@ -0,0 +1,87 @@
+/* $Id: RTFileSetAllocationSize-posix.cpp $ */
+/** @file
+ * IPRT - RTFileSetAllocationSize, linux implementation.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include <iprt/file.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/**
+ * The posix_fallocate() method.
+ */
+typedef int (*PFNPOSIXFALLOCATE) (int iFd, off_t offStart, off_t cb);
+
+RTDECL(int) RTFileSetAllocationSize(RTFILE hFile, uint64_t cbSize, uint32_t fFlags)
+{
+ AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTFILE_ALLOC_SIZE_F_VALID), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(sizeof(off_t) >= sizeof(cbSize) || RT_HIDWORD(cbSize) == 0,
+ ("64-bit filesize not supported! cbSize=%lld\n", cbSize),
+ VERR_NOT_SUPPORTED);
+
+ if (fFlags & RTFILE_ALLOC_SIZE_F_KEEP_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ int rc = VINF_SUCCESS;
+ PFNPOSIXFALLOCATE pfnPosixFAllocate = (PFNPOSIXFALLOCATE)(uintptr_t)dlsym(RTLD_DEFAULT, "posix_fallocate");
+ if (RT_VALID_PTR(pfnPosixFAllocate))
+ {
+ int rcPosix = pfnPosixFAllocate(RTFileToNative(hFile), 0, cbSize);
+ if (rcPosix != 0)
+ {
+ if (errno == EOPNOTSUPP)
+ rc = VERR_NOT_SUPPORTED;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTFileSetAllocationSize);
diff --git a/src/VBox/Runtime/r3/posix/RTHandleGetStandard-posix.cpp b/src/VBox/Runtime/r3/posix/RTHandleGetStandard-posix.cpp
new file mode 100644
index 00000000..30e54331
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTHandleGetStandard-posix.cpp
@@ -0,0 +1,144 @@
+/* $Id: RTHandleGetStandard-posix.cpp $ */
+/** @file
+ * IPRT - RTHandleGetStandard, POSIX.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#ifdef _MSC_VER
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+#include "internal/iprt.h"
+#include <iprt/handle.h>
+
+#include <iprt/file.h>
+#include <iprt/pipe.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+
+#include "internal/socket.h"
+
+
+
+RTDECL(int) RTHandleGetStandard(RTHANDLESTD enmStdHandle, bool fLeaveOpen, PRTHANDLE ph)
+{
+ /*
+ * Validate and convert input.
+ */
+ AssertPtrReturn(ph, VERR_INVALID_POINTER);
+ int fd;
+ switch (enmStdHandle)
+ {
+ case RTHANDLESTD_INPUT: fd = 0; break;
+ case RTHANDLESTD_OUTPUT: fd = 1; break;
+ case RTHANDLESTD_ERROR: fd = 2; break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Is the requested descriptor valid and which IPRT handle type does it
+ * best map on to?
+ */
+ struct stat st;
+ int rc = fstat(fd, &st);
+ if (rc == -1)
+ return RTErrConvertFromErrno(errno);
+
+ rc = fcntl(fd, F_GETFD, 0);
+ if (rc == -1)
+ return RTErrConvertFromErrno(errno);
+ bool const fInherit = !(rc & FD_CLOEXEC);
+
+ RTHANDLE h;
+ if (S_ISREG(st.st_mode))
+ h.enmType = RTHANDLETYPE_FILE;
+ else if ( S_ISFIFO(st.st_mode)
+ || (st.st_mode == 0 && st.st_nlink == 0 /*see bugs on bsd manpage*/))
+ h.enmType = RTHANDLETYPE_PIPE;
+ else if (S_ISSOCK(st.st_mode))
+ {
+ /** @todo check if it's really a socket... IIRC some OSes reports
+ * anonymouse pips as sockets. */
+ h.enmType = RTHANDLETYPE_SOCKET;
+ }
+#if 0 /** @todo re-enable this when the VFS pipe has been coded up. */
+ else if (isatty(fd))
+ h.enmType = RTHANDLETYPE_PIPE;
+#endif
+ else
+ h.enmType = RTHANDLETYPE_FILE;
+
+ /*
+ * Create the IPRT handle.
+ */
+ switch (h.enmType)
+ {
+ case RTHANDLETYPE_FILE:
+ /** @todo fLeaveOpen */
+ rc = RTFileFromNative(&h.u.hFile, fd);
+ break;
+
+ case RTHANDLETYPE_PIPE:
+ rc = RTPipeFromNative(&h.u.hPipe, fd,
+ (enmStdHandle == RTHANDLESTD_INPUT ? RTPIPE_N_READ : RTPIPE_N_WRITE)
+ | (fInherit ? RTPIPE_N_INHERIT : 0)
+ | (fLeaveOpen ? RTPIPE_N_LEAVE_OPEN : 0));
+ break;
+
+ case RTHANDLETYPE_SOCKET:
+ rc = rtSocketCreateForNative(&h.u.hSocket, fd, fLeaveOpen);
+ break;
+
+ default: /* shut up gcc */
+ return VERR_INTERNAL_ERROR;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ph = h;
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/RTMemProtect-posix.cpp b/src/VBox/Runtime/r3/posix/RTMemProtect-posix.cpp
new file mode 100644
index 00000000..41400a87
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTMemProtect-posix.cpp
@@ -0,0 +1,105 @@
+/* $Id: RTMemProtect-posix.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/param.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include <errno.h>
+#include <sys/mman.h>
+
+
+RTDECL(int) RTMemProtect(void *pv, size_t cb, unsigned fProtect) RT_NO_THROW_DEF
+{
+ /*
+ * Validate input.
+ */
+ if (cb == 0)
+ {
+ AssertMsgFailed(("!cb\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (fProtect & ~(RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC))
+ {
+ AssertMsgFailed(("fProtect=%#x\n", fProtect));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Convert the flags.
+ */
+ int fProt;
+#if RTMEM_PROT_NONE == PROT_NONE \
+ && RTMEM_PROT_READ == PROT_READ \
+ && RTMEM_PROT_WRITE == PROT_WRITE \
+ && RTMEM_PROT_EXEC == PROT_EXEC
+ fProt = fProtect;
+#else
+ Assert(!RTMEM_PROT_NONE);
+ if (!fProtect)
+ fProt = PROT_NONE;
+ else
+ {
+ fProt = 0;
+ if (fProtect & RTMEM_PROT_READ)
+ fProt |= PROT_READ;
+ if (fProtect & RTMEM_PROT_WRITE)
+ fProt |= PROT_WRITE;
+ if (fProtect & RTMEM_PROT_EXEC)
+ fProt |= PROT_EXEC;
+ }
+#endif
+
+ /*
+ * Align the request.
+ */
+ cb += (uintptr_t)pv & PAGE_OFFSET_MASK;
+ pv = (void *)((uintptr_t)pv & ~PAGE_OFFSET_MASK);
+
+ /*
+ * Change the page attributes.
+ */
+ int rc = mprotect(pv, cb, fProt);
+ if (!rc)
+ return rc;
+ return RTErrConvertFromErrno(errno);
+}
diff --git a/src/VBox/Runtime/r3/posix/RTMpGetCount-posix.cpp b/src/VBox/Runtime/r3/posix/RTMpGetCount-posix.cpp
new file mode 100644
index 00000000..f48d6f98
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTMpGetCount-posix.cpp
@@ -0,0 +1,89 @@
+/* $Id: RTMpGetCount-posix.cpp $ */
+/** @file
+ * IPRT - RTMpGetCount, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/mp.h>
+#include <iprt/assert.h>
+
+#include <unistd.h>
+#if !defined(RT_OS_SOLARIS)
+# include <sys/sysctl.h>
+#endif
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ /*
+ * The sysconf way (linux and others).
+ */
+#if defined(_SC_NPROCESSORS_MAX) || defined(_SC_NPROCESSORS_CONF) || defined(_SC_NPROCESSORS_ONLN)
+ int cCpusSC = -1;
+# ifdef _SC_NPROCESSORS_MAX
+ int cMax = sysconf(_SC_NPROCESSORS_MAX);
+ cCpusSC = RT_MAX(cCpusSC, cMax);
+# endif
+# ifdef _SC_NPROCESSORS_CONF
+ int cConf = sysconf(_SC_NPROCESSORS_CONF);
+ cCpusSC = RT_MAX(cCpusSC, cConf);
+# endif
+# ifdef _SC_NPROCESSORS_ONLN
+ int cOnln = sysconf(_SC_NPROCESSORS_ONLN);
+ cCpusSC = RT_MAX(cCpusSC, cOnln);
+# endif
+ Assert(cCpusSC > 0);
+ if (cCpusSC > 0)
+ return cCpusSC;
+#endif
+
+ /*
+ * The BSD 4.4 way.
+ */
+#if defined(CTL_HW) && defined(HW_NCPU)
+ int aiMib[2];
+ aiMib[0] = CTL_HW;
+ aiMib[1] = HW_NCPU;
+ int cCpus = -1;
+ size_t cb = sizeof(cCpus);
+ int rc = sysctl(aiMib, RT_ELEMENTS(aiMib), &cCpus, &cb, NULL, 0);
+ if (rc != -1 && cCpus >= 1)
+ return cCpus;
+#endif
+ return 1;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/RTPathUserDocuments-posix.cpp b/src/VBox/Runtime/r3/posix/RTPathUserDocuments-posix.cpp
new file mode 100644
index 00000000..ee8c8036
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTPathUserDocuments-posix.cpp
@@ -0,0 +1,63 @@
+/* $Id: RTPathUserDocuments-posix.cpp $ */
+/** @file
+ * IPRT - RTPathUserDocuments, posix ring-3.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/path.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+RTDECL(int) RTPathUserDocuments(char *pszPath, size_t cchPath)
+{
+ /*
+ * Validate input
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(cchPath, VERR_INVALID_PARAMETER);
+
+ int rc = RTPathUserHome(pszPath, cchPath);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = RTPathAppend(pszPath, cchPath, "Documents");
+ if (RT_FAILURE(rc))
+ *pszPath = '\0';
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/RTPathUserHome-posix.cpp b/src/VBox/Runtime/r3/posix/RTPathUserHome-posix.cpp
new file mode 100644
index 00000000..daa30ea1
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTPathUserHome-posix.cpp
@@ -0,0 +1,173 @@
+/* $Id: RTPathUserHome-posix.cpp $ */
+/** @file
+ * IPRT - Path Manipulation, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PATH
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include <iprt/path.h>
+#include <iprt/env.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/path.h"
+#include "internal/fs.h"
+
+
+#ifndef RT_OS_L4
+/**
+ * Worker for RTPathUserHome that looks up the home directory
+ * using the getpwuid_r api.
+ *
+ * @returns IPRT status code.
+ * @param pszPath The path buffer.
+ * @param cchPath The size of the buffer.
+ * @param uid The User ID to query the home directory of.
+ */
+static int rtPathUserHomeByPasswd(char *pszPath, size_t cchPath, uid_t uid)
+{
+ /*
+ * The getpwuid_r function uses the passed in buffer to "allocate" any
+ * extra memory it needs. On some systems we should probably use the
+ * sysconf function to find the appropriate buffer size, but since it won't
+ * work everywhere we'll settle with a 5KB buffer and ASSUME that it'll
+ * suffice for even the lengthiest user descriptions...
+ */
+ char achBuffer[5120];
+ struct passwd Passwd;
+ struct passwd *pPasswd;
+ memset(&Passwd, 0, sizeof(Passwd));
+ int rc = getpwuid_r(uid, &Passwd, &achBuffer[0], sizeof(achBuffer), &pPasswd);
+ if (rc != 0)
+ return RTErrConvertFromErrno(rc);
+ if (!pPasswd) /* uid not found in /etc/passwd */
+ return VERR_PATH_NOT_FOUND;
+
+ /*
+ * Check that it isn't empty and that it exists.
+ */
+ struct stat st;
+ if ( !pPasswd->pw_dir
+ || !*pPasswd->pw_dir
+ || stat(pPasswd->pw_dir, &st)
+ || !S_ISDIR(st.st_mode))
+ return VERR_PATH_NOT_FOUND;
+
+ /*
+ * Convert it to UTF-8 and copy it to the return buffer.
+ */
+ return rtPathFromNativeCopy(pszPath, cchPath, pPasswd->pw_dir, NULL);
+}
+#endif
+
+
+/**
+ * Worker for RTPathUserHome that looks up the home directory
+ * using the HOME environment variable.
+ *
+ * @returns IPRT status code.
+ * @param pszPath The path buffer.
+ * @param cchPath The size of the buffer.
+ */
+static int rtPathUserHomeByEnv(char *pszPath, size_t cchPath)
+{
+ /*
+ * Get HOME env. var it and validate it's existance.
+ */
+ int rc = VERR_PATH_NOT_FOUND;
+ const char *pszHome = RTEnvGet("HOME"); /** @todo Codeset confusion in RTEnv. */
+ if (pszHome)
+
+ {
+ struct stat st;
+ if ( !stat(pszHome, &st)
+ && S_ISDIR(st.st_mode))
+ rc = rtPathFromNativeCopy(pszPath, cchPath, pszHome, NULL);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPathUserHome(char *pszPath, size_t cchPath)
+{
+ int rc;
+#ifndef RT_OS_L4
+ /*
+ * We make an exception for the root user and use the system call
+ * getpwuid_r to determine their initial home path instead of
+ * reading it from the $HOME variable. This is because the $HOME
+ * variable does not get changed by sudo (and possibly su and others)
+ * which can cause root-owned files to appear in user's home folders.
+ */
+ uid_t uid = geteuid();
+ if (!uid)
+ rc = rtPathUserHomeByPasswd(pszPath, cchPath, uid);
+ else
+ rc = rtPathUserHomeByEnv(pszPath, cchPath);
+
+ /*
+ * On failure, retry using the alternative method.
+ * (Should perhaps restrict the retry cases a bit more here...)
+ */
+ if ( RT_FAILURE(rc)
+ && rc != VERR_BUFFER_OVERFLOW)
+ {
+ if (!uid)
+ rc = rtPathUserHomeByEnv(pszPath, cchPath);
+ else
+ rc = rtPathUserHomeByPasswd(pszPath, cchPath, uid);
+ }
+#else /* RT_OS_L4 */
+ rc = rtPathUserHomeByEnv(pszPath, cchPath);
+#endif /* RT_OS_L4 */
+
+ LogFlow(("RTPathUserHome(%p:{%s}, %u): returns %Rrc\n", pszPath,
+ RT_SUCCESS(rc) ? pszPath : "<failed>", cchPath, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/RTSystemQueryOSInfo-posix.cpp b/src/VBox/Runtime/r3/posix/RTSystemQueryOSInfo-posix.cpp
new file mode 100644
index 00000000..6dc2f975
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTSystemQueryOSInfo-posix.cpp
@@ -0,0 +1,100 @@
+/* $Id: RTSystemQueryOSInfo-posix.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryOSInfo, POSIX implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/errcore.h>
+
+#include <errno.h>
+#include <sys/utsname.h>
+
+
+RTDECL(int) RTSystemQueryOSInfo(RTSYSOSINFO enmInfo, char *pszInfo, size_t cchInfo)
+{
+ /*
+ * Quick validation.
+ */
+ AssertReturn(enmInfo > RTSYSOSINFO_INVALID && enmInfo < RTSYSOSINFO_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszInfo, VERR_INVALID_POINTER);
+ if (!cchInfo)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Handle the request.
+ */
+ switch (enmInfo)
+ {
+ case RTSYSOSINFO_PRODUCT:
+ case RTSYSOSINFO_RELEASE:
+ case RTSYSOSINFO_VERSION:
+ {
+ struct utsname UtsInfo;
+ if (uname(&UtsInfo) < 0)
+ return RTErrConvertFromErrno(errno);
+ const char *pszSrc;
+ switch (enmInfo)
+ {
+ case RTSYSOSINFO_PRODUCT: pszSrc = UtsInfo.sysname; break;
+ case RTSYSOSINFO_RELEASE: pszSrc = UtsInfo.release; break;
+ case RTSYSOSINFO_VERSION: pszSrc = UtsInfo.version; break;
+ default: AssertFatalFailed(); /* screw gcc */
+ }
+ size_t cch = strlen(pszSrc);
+ if (cch < cchInfo)
+ {
+ memcpy(pszInfo, pszSrc, cch + 1);
+ return VINF_SUCCESS;
+ }
+ memcpy(pszInfo, pszSrc, cchInfo - 1);
+ pszInfo[cchInfo - 1] = '\0';
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+
+ case RTSYSOSINFO_SERVICE_PACK:
+ default:
+ *pszInfo = '\0';
+ return VERR_NOT_SUPPORTED;
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/RTSystemQueryTotalRam-posix.cpp b/src/VBox/Runtime/r3/posix/RTSystemQueryTotalRam-posix.cpp
new file mode 100644
index 00000000..151a12f5
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTSystemQueryTotalRam-posix.cpp
@@ -0,0 +1,61 @@
+/* $Id: RTSystemQueryTotalRam-posix.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryTotalRam, windows ring-3.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+
+
+
+RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+ return VERR_NOT_IMPLEMENTED;
+}
diff --git a/src/VBox/Runtime/r3/posix/RTTimeNow-posix.cpp b/src/VBox/Runtime/r3/posix/RTTimeNow-posix.cpp
new file mode 100644
index 00000000..b00394c8
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTTimeNow-posix.cpp
@@ -0,0 +1,61 @@
+/* $Id: RTTimeNow-posix.cpp $ */
+/** @file
+ * IPRT - RTTimeNow, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#define RTTIME_INCL_TIMEVAL
+#include <sys/time.h>
+#include <time.h>
+
+#include <iprt/time.h>
+
+
+/**
+ * Gets the current system time.
+ *
+ * @returns pTime.
+ * @param pTime Where to store the time.
+ */
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return RTTimeSpecSetTimeval(pTime, &tv);
+}
+
diff --git a/src/VBox/Runtime/r3/posix/RTTimeSet-posix.cpp b/src/VBox/Runtime/r3/posix/RTTimeSet-posix.cpp
new file mode 100644
index 00000000..8d848b50
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTTimeSet-posix.cpp
@@ -0,0 +1,60 @@
+/* $Id: RTTimeSet-posix.cpp $ */
+/** @file
+ * IPRT - RTTimeSet, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#define RTTIME_INCL_TIMEVAL
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTTimeSet(PCRTTIMESPEC pTime)
+{
+ struct timeval tv;
+ if (settimeofday(RTTimeSpecGetTimeval(pTime, &tv), NULL) == 0)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
diff --git a/src/VBox/Runtime/r3/posix/RTTimeZoneGetCurrent-posix.cpp b/src/VBox/Runtime/r3/posix/RTTimeZoneGetCurrent-posix.cpp
new file mode 100644
index 00000000..6d0d84d0
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/RTTimeZoneGetCurrent-posix.cpp
@@ -0,0 +1,262 @@
+/* $Id: RTTimeZoneGetCurrent-posix.cpp $ */
+/** @file
+ * IPRT - RTTimeZoneGetCurrent, POSIX.
+ */
+
+/*
+ * Copyright (C) 2020-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/errcore.h>
+#include <iprt/types.h>
+#include <iprt/symlink.h>
+#include <iprt/stream.h>
+
+#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
+# include <tzfile.h>
+#else
+# define TZDIR "/usr/share/zoneinfo"
+# define TZ_MAGIC "TZif"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define PATH_LOCALTIME "/etc/localtime"
+#if defined(RT_OS_FREEBSD)
+# define PATH_TIMEZONE "/var/db/zoneinfo"
+#else
+# define PATH_TIMEZONE "/etc/timezone"
+#endif
+#define PATH_SYSCONFIG_CLOCK "/etc/sysconfig/clock"
+
+
+/**
+ * Checks if a time zone database file is valid by verifying it begins with
+ * TZ_MAGIC.
+ *
+ * @returns IPRT status code.
+ * @param pszTimezone The time zone database file relative to
+ * <tzfile.h>:TZDIR (normally /usr/share/zoneinfo),
+ * e.g. Europe/London, or Etc/UTC, or UTC, or etc.
+ *
+ * @note File format is documented in RFC-8536.
+ */
+static int rtIsValidTimeZoneFile(const char *pszTimeZone)
+{
+ if (pszTimeZone == NULL || *pszTimeZone == '\0' || *pszTimeZone == '/')
+ return VERR_INVALID_PARAMETER;
+
+ int rc = RTStrValidateEncoding(pszTimeZone);
+ if (RT_SUCCESS(rc))
+ {
+ /* construct full pathname of the time zone file */
+ char szTZPath[RTPATH_MAX];
+ rc = RTPathJoin(szTZPath, sizeof(szTZPath), TZDIR, pszTimeZone);
+ if (RT_SUCCESS(rc))
+ {
+ /* open the time zone file and check that it begins with the correct magic number */
+ RTFILE hFile = NIL_RTFILE;
+ rc = RTFileOpen(&hFile, szTZPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ char achTZBuf[sizeof(TZ_MAGIC)];
+ rc = RTFileRead(hFile, achTZBuf, sizeof(achTZBuf), NULL);
+ RTFileClose(hFile);
+ if (RT_SUCCESS(rc))
+ {
+ if (memcmp(achTZBuf, RT_STR_TUPLE(TZ_MAGIC)) == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_INVALID_MAGIC;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Return the system time zone.
+ *
+ * @returns IPRT status code.
+ * @param pszName The buffer to return the time zone in.
+ * @param cbName The size of the pszName buffer.
+ */
+RTDECL(int) RTTimeZoneGetCurrent(char *pszName, size_t cbName)
+{
+ int rc = RTEnvGetEx(RTENV_DEFAULT, "TZ", pszName, cbName, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * $TZ can have two different formats and one of them doesn't specify
+ * a time zone database file under <tzfile.h>:TZDIR but since all
+ * current callers of this routine expect a time zone filename we do
+ * the validation check here so that if it is invalid then we fall back
+ * to the other mechanisms to return the system's current time zone.
+ */
+ if (*pszName == ':') /* POSIX allows $TZ to begin with a colon (:) so we allow for that here */
+ memmove(pszName, pszName + 1, strlen(pszName));
+ /** @todo this isn't perfect for absolute paths... Should probably try treat
+ * it like /etc/localtime. */
+ rc = rtIsValidTimeZoneFile(pszName);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ else if (rc != VERR_ENV_VAR_NOT_FOUND)
+ return rc;
+
+ /*
+ * /etc/localtime is a symbolic link to the system time zone on many OSes
+ * including Solaris, macOS, Ubuntu, RH/OEL 6 and later, Arch Linux, NetBSD,
+ * and etc. We extract the time zone pathname relative to TZDIR defined in
+ * <tzfile.h> which is normally /usr/share/zoneinfo.
+ *
+ * N.B. Some OSes have /etc/localtime as a regular file instead of a
+ * symlink and while we could trawl through all the files under TZDIR
+ * looking for a match we instead fallback to other popular mechanisms of
+ * specifying the system-wide time zone for the sake of simplicity.
+ */
+ char szBuf[RTPATH_MAX];
+ const char *pszPath = PATH_LOCALTIME;
+ if (RTSymlinkExists(pszPath))
+ {
+ /* the contents of the symink may contain '..' or other links */
+ char szLinkPathReal[RTPATH_MAX];
+ rc = RTPathReal(pszPath, szLinkPathReal, sizeof(szLinkPathReal));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPathReal(TZDIR, szBuf, sizeof(szBuf));
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(RTPathStartsWith(szLinkPathReal, szBuf));
+ if (RTPathStartsWith(szLinkPathReal, szBuf))
+ {
+ /* <tzfile.h>:TZDIR doesn't include a trailing slash */
+ const char *pszTimeZone = &szLinkPathReal[strlen(szBuf) + 1];
+ rc = rtIsValidTimeZoneFile(pszTimeZone);
+ if (RT_SUCCESS(rc))
+ return RTStrCopy(pszName, cbName, pszTimeZone);
+ }
+ }
+ }
+ }
+
+ /*
+ * /etc/timezone is a regular file consisting of a single line containing
+ * the time zone (e.g. Europe/London or Etc/UTC or etc.) and is used by a
+ * variety of Linux distros such as Ubuntu, Gentoo, Debian, and etc.
+ * The equivalent on FreeBSD is /var/db/zoneinfo.
+ */
+ pszPath = PATH_TIMEZONE;
+ if (RTFileExists(pszPath))
+ {
+ RTFILE hFile = NIL_RTFILE;
+ rc = RTFileOpen(&hFile, PATH_TIMEZONE, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbRead = 0;
+ rc = RTFileRead(hFile, szBuf, sizeof(szBuf), &cbRead);
+ RTFileClose(hFile);
+ if (RT_SUCCESS(rc) && cbRead > 0)
+ {
+ /* Get the first line and strip it. */
+ szBuf[RT_MIN(sizeof(szBuf) - 1, cbRead)] = '\0';
+ size_t const offNewLine = RTStrOffCharOrTerm(szBuf, '\n');
+ szBuf[offNewLine] = '\0';
+ const char *pszTimeZone = RTStrStrip(szBuf);
+
+ rc = rtIsValidTimeZoneFile(pszTimeZone);
+ if (RT_SUCCESS(rc))
+ return RTStrCopy(pszName, cbName, pszTimeZone);
+ }
+ }
+ }
+
+ /*
+ * Older versions of RedHat / OEL don't have /etc/localtime as a symlink or
+ * /etc/timezone but instead have /etc/sysconfig/clock which contains a line
+ * of the syntax ZONE=Europe/London or ZONE="Europe/London" amongst other entries.
+ */
+ pszPath = PATH_SYSCONFIG_CLOCK;
+ if (RTFileExists(pszPath))
+ {
+ PRTSTREAM pStrm;
+ rc = RTStrmOpen(pszPath, "r", &pStrm);
+ if (RT_SUCCESS(rc))
+ {
+ while (RT_SUCCESS(rc = RTStrmGetLine(pStrm, szBuf, sizeof(szBuf))))
+ {
+ static char const s_szVarEq[] = "ZONE=";
+ char *pszStart = RTStrStrip(szBuf);
+ if (memcmp(pszStart, RT_STR_TUPLE(s_szVarEq)) == 0)
+ {
+ char *pszTimeZone = &pszStart[sizeof(s_szVarEq) - 1];
+
+ /* Drop any quoting before using the value, assuming it is plain stuff: */
+ if (*pszTimeZone == '\"' || *pszTimeZone == '\'')
+ {
+ pszTimeZone++;
+ size_t const cchTimeZone = strlen(pszTimeZone);
+ if (cchTimeZone && (pszTimeZone[cchTimeZone - 1] == '"' || pszTimeZone[cchTimeZone - 1] == '\''))
+ pszTimeZone[cchTimeZone - 1] = '\0';
+ }
+
+ rc = rtIsValidTimeZoneFile(pszTimeZone);
+ if (RT_SUCCESS(rc))
+ {
+ RTStrmClose(pStrm);
+ return RTStrCopy(pszName, cbName, pszTimeZone);
+ }
+ }
+ }
+ RTStrmClose(pStrm);
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/allocex-r3-posix.cpp b/src/VBox/Runtime/r3/posix/allocex-r3-posix.cpp
new file mode 100644
index 00000000..b8f1a1ad
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/allocex-r3-posix.cpp
@@ -0,0 +1,120 @@
+/* $Id: allocex-r3-posix.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Extended Alloc Workers, posix.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTMEM_NO_WRAP_TO_EF_APIS
+#include <iprt/mem.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include "../allocex.h"
+
+#include <sys/mman.h>
+
+
+DECLHIDDEN(int) rtMemAllocEx16BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv)
+{
+ AssertReturn(cbAlloc < _64K, VERR_NO_MEMORY);
+
+ /*
+ * Try with every possible address hint since the possible range is very limited.
+ */
+ int fProt = PROT_READ | PROT_WRITE | (fFlags & RTMEMALLOCEX_FLAGS_EXEC ? PROT_EXEC : 0);
+ uintptr_t uAddr = 0x1000;
+ uintptr_t uAddrLast = _64K - uAddr - cbAlloc;
+ while (uAddr <= uAddrLast)
+ {
+ void *pv = mmap((void *)uAddr, cbAlloc, fProt, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (pv && (uintptr_t)pv <= uAddrLast)
+ {
+ *ppv = pv;
+ return VINF_SUCCESS;
+ }
+
+ if (pv)
+ {
+ munmap(pv, cbAlloc);
+ pv = NULL;
+ }
+ uAddr += _4K;
+ }
+
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtMemAllocEx32BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv)
+{
+ int fProt = PROT_READ | PROT_WRITE | (fFlags & RTMEMALLOCEX_FLAGS_EXEC ? PROT_EXEC : 0);
+#if ARCH_BITS == 32
+ void *pv = mmap(NULL, cbAlloc, fProt, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (pv)
+ {
+ *ppv = pv;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+
+#elif defined(RT_OS_LINUX)
+# ifdef MAP_32BIT
+ void *pv = mmap(NULL, cbAlloc, fProt, MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
+ if (pv)
+ {
+ *ppv = pv;
+ return VINF_SUCCESS;
+ }
+# endif
+
+ /** @todo On linux, we need an accurate hint. Since I don't need this branch of
+ * the code right now, I won't bother starting to parse
+ * /proc/curproc/mmap right now... */
+#else
+#endif
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(void) rtMemFreeExYyBitReach(void *pv, size_t cb, uint32_t fFlags)
+{
+ RT_NOREF_PV(fFlags);
+ munmap(pv, cb);
+}
+
diff --git a/src/VBox/Runtime/r3/posix/dir-posix.cpp b/src/VBox/Runtime/r3/posix/dir-posix.cpp
new file mode 100644
index 00000000..558085ab
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/dir-posix.cpp
@@ -0,0 +1,733 @@
+/* $Id: dir-posix.cpp $ */
+/** @file
+ * IPRT - Directory manipulation, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <stdio.h>
+
+#include <iprt/dir.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include "internal/dir.h"
+#include "internal/fs.h"
+#include "internal/path.h"
+
+#if !defined(RT_OS_SOLARIS) && !defined(RT_OS_HAIKU)
+# define HAVE_DIRENT_D_TYPE 1
+#endif
+
+
+RTDECL(bool) RTDirExists(const char *pszPath)
+{
+ bool fRc = false;
+ char const *pszNativePath;
+ int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat s;
+ fRc = !stat(pszNativePath, &s)
+ && S_ISDIR(s.st_mode);
+
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+
+ LogFlow(("RTDirExists(%p={%s}): returns %RTbool\n", pszPath, pszPath, fRc));
+ return fRc;
+}
+
+
+RTDECL(int) RTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate)
+{
+ RT_NOREF_PV(fCreate);
+
+ int rc;
+ fMode = rtFsModeNormalize(fMode, pszPath, 0, RTFS_TYPE_DIRECTORY);
+ if (rtFsModeIsValidPermissions(fMode))
+ {
+ char const *pszNativePath;
+ rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat st;
+ if (mkdir(pszNativePath, fMode & RTFS_UNIX_MASK) == 0)
+ {
+ /* If requested, we try make use the permission bits are set
+ correctly when asked. For now, we'll just ignore errors here. */
+ if (fCreate & RTDIRCREATE_FLAGS_IGNORE_UMASK)
+ {
+ if ( stat(pszNativePath, &st)
+ || (st.st_mode & 07777) != (fMode & 07777) )
+ chmod(pszNativePath, fMode & RTFS_UNIX_MASK);
+ }
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = errno;
+ /*
+ * Solaris mkdir returns ENOSYS on autofs directories, and also
+ * did this apparently for NFS mount points in some Nevada
+ * development builds. It also returned EACCES when it should
+ * have returned EEXIST, which actually is within the POSIX
+ * spec (not that I like this interpretation, but it seems
+ * valid). Check ourselves.
+ */
+ if ( rc == ENOSYS
+ || rc == EACCES)
+ {
+ rc = RTErrConvertFromErrno(rc);
+ if (!stat(pszNativePath, &st))
+ rc = VERR_ALREADY_EXISTS;
+ }
+ else
+ rc = RTErrConvertFromErrno(rc);
+ }
+ }
+
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+ else
+ {
+ AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
+ rc = VERR_INVALID_FMODE;
+ }
+ LogFlow(("RTDirCreate(%p={%s}, %RTfmode): returns %Rrc\n", pszPath, pszPath, fMode, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTDirRemove(const char *pszPath)
+{
+ char const *pszNativePath;
+ int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (rmdir(pszNativePath))
+ {
+ rc = errno;
+ if (rc == EEXIST) /* Solaris returns this, the rest have ENOTEMPTY. */
+ rc = VERR_DIR_NOT_EMPTY;
+ else if (rc != ENOTDIR)
+ rc = RTErrConvertFromErrno(rc);
+ else
+ {
+ /*
+ * This may be a valid path-not-found or it may be a non-directory in
+ * the final component. FsPerf want us to distinguish between the two,
+ * and trailing slash shouldn't matter because it doesn't on windows...
+ */
+ char *pszFree = NULL;
+ const char *pszStat = pszNativePath;
+ size_t cch = strlen(pszNativePath);
+ if (cch > 2 && RTPATH_IS_SLASH(pszNativePath[cch - 1]))
+ {
+ pszFree = (char *)RTMemTmpAlloc(cch);
+ if (pszFree)
+ {
+ memcpy(pszFree, pszNativePath, cch);
+ do
+ pszFree[--cch] = '\0';
+ while (cch > 2 && RTPATH_IS_SLASH(pszFree[cch - 1]));
+ pszStat = pszFree;
+ }
+ }
+
+ struct stat st;
+ if (!stat(pszStat, &st) && !S_ISDIR(st.st_mode))
+ rc = VERR_NOT_A_DIRECTORY;
+ else
+ rc = VERR_PATH_NOT_FOUND;
+
+ if (pszFree)
+ RTMemTmpFree(pszFree);
+ }
+ }
+
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+
+ LogFlow(("RTDirRemove(%p={%s}): returns %Rrc\n", pszPath, pszPath, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTDirFlush(const char *pszPath)
+{
+ /*
+ * Linux: The fsync() man page hints at this being required for ensuring
+ * consistency between directory and file in case of a crash.
+ *
+ * Solaris: No mentioned is made of directories on the fsync man page.
+ * While rename+fsync will do what we want on ZFS, the code needs more
+ * careful studying wrt whether the directory entry of a new file is
+ * implicitly synced when the file is synced (it's very likely for ZFS).
+ *
+ * FreeBSD: The FFS fsync code seems to flush the directory entry as well
+ * in some cases. Don't know exactly what's up with rename, but from the
+ * look of things fsync(dir) should work.
+ */
+ int rc;
+#ifdef O_DIRECTORY
+ int fd = open(pszPath, O_RDONLY | O_DIRECTORY, 0);
+#else
+ int fd = open(pszPath, O_RDONLY, 0);
+#endif
+ if (fd >= 0)
+ {
+ if (fsync(fd) == 0)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /* Linux fsync(2) man page documents both errors as an indication
+ * that the file descriptor can't be flushed (seen EINVAL for usual
+ * directories on CIFS). BSD (OS X) fsync(2) documents only the
+ * latter, and Solaris fsync(3C) pretends there is no problem. */
+ if (errno == EROFS || errno == EINVAL)
+ rc = VERR_NOT_SUPPORTED;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ close(fd);
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ return rc;
+}
+
+
+size_t rtDirNativeGetStructSize(const char *pszPath)
+{
+ long cbNameMax = pathconf(pszPath, _PC_NAME_MAX);
+# ifdef NAME_MAX
+ if (cbNameMax < NAME_MAX) /* This is plain paranoia, but it doesn't hurt. */
+ cbNameMax = NAME_MAX;
+# endif
+# ifdef _XOPEN_NAME_MAX
+ if (cbNameMax < _XOPEN_NAME_MAX) /* Ditto. */
+ cbNameMax = _XOPEN_NAME_MAX;
+# endif
+ size_t cbDir = RT_UOFFSETOF_DYN(RTDIRINTERNAL, Data.d_name[cbNameMax + 1]);
+ if (cbDir < sizeof(RTDIRINTERNAL)) /* Ditto. */
+ cbDir = sizeof(RTDIRINTERNAL);
+ cbDir = RT_ALIGN_Z(cbDir, 8);
+
+ return cbDir;
+}
+
+
+int rtDirNativeOpen(PRTDIRINTERNAL pDir, uintptr_t hRelativeDir, void *pvNativeRelative)
+{
+ NOREF(hRelativeDir);
+ NOREF(pvNativeRelative);
+
+ /*
+ * Convert to a native path and try opendir.
+ */
+ char *pszSlash = NULL;
+ char const *pszNativePath;
+ int rc;
+ if ( !(pDir->fFlags & RTDIR_F_NO_FOLLOW)
+ || pDir->fDirSlash
+ || pDir->cchPath <= 1)
+ rc = rtPathToNative(&pszNativePath, pDir->pszPath, NULL);
+ else
+ {
+ pszSlash = (char *)&pDir->pszPath[pDir->cchPath - 1];
+ *pszSlash = '\0';
+ rc = rtPathToNative(&pszNativePath, pDir->pszPath, NULL);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ if ( !(pDir->fFlags & RTDIR_F_NO_FOLLOW)
+ || pDir->fDirSlash)
+ pDir->pDir = opendir(pszNativePath);
+ else
+ {
+ /*
+ * If we can get fdopendir() and have both O_NOFOLLOW and O_DIRECTORY,
+ * we will use open() to safely open the directory without following
+ * symlinks in the final component, and then use fdopendir to get a DIR
+ * from the file descriptor.
+ *
+ * If we cannot get that, we will use lstat() + opendir() as a fallback.
+ *
+ * We ASSUME that support for the O_NOFOLLOW and O_DIRECTORY flags is
+ * older than fdopendir().
+ */
+#if defined(O_NOFOLLOW) && defined(O_DIRECTORY)
+ /* Need to resolve fdopendir dynamically. */
+ typedef DIR * (*PFNFDOPENDIR)(int);
+ static PFNFDOPENDIR s_pfnFdOpenDir = NULL;
+ static bool volatile s_fInitalized = false;
+
+ PFNFDOPENDIR pfnFdOpenDir = s_pfnFdOpenDir;
+ ASMCompilerBarrier();
+ if (s_fInitalized)
+ { /* likely */ }
+ else
+ {
+ pfnFdOpenDir = (PFNFDOPENDIR)(uintptr_t)dlsym(RTLD_DEFAULT, "fdopendir");
+ s_pfnFdOpenDir = pfnFdOpenDir;
+ ASMAtomicWriteBool(&s_fInitalized, true);
+ }
+
+ if (pfnFdOpenDir)
+ {
+ int fd = open(pszNativePath, O_RDONLY | O_DIRECTORY | O_NOFOLLOW, 0);
+ if (fd >= 0)
+ {
+ pDir->pDir = pfnFdOpenDir(fd);
+ if (RT_UNLIKELY(!pDir->pDir))
+ {
+ rc = RTErrConvertFromErrno(errno);
+ close(fd);
+ }
+ }
+ else
+ {
+ /* WSL returns ELOOP here, but we take no chances that O_NOFOLLOW
+ takes precedence over O_DIRECTORY everywhere. */
+ int iErr = errno;
+ if (iErr == ELOOP || iErr == ENOTDIR)
+ {
+ struct stat St;
+ if ( lstat(pszNativePath, &St) == 0
+ && S_ISLNK(St.st_mode))
+ rc = VERR_IS_A_SYMLINK;
+ else
+ rc = RTErrConvertFromErrno(iErr);
+ }
+ }
+ }
+ else
+#endif
+ {
+ /* Fallback. This contains a race condition. */
+ struct stat St;
+ if ( lstat(pszNativePath, &St) != 0
+ || !S_ISLNK(St.st_mode))
+ pDir->pDir = opendir(pszNativePath);
+ else
+ rc = VERR_IS_A_SYMLINK;
+ }
+ }
+ if (pDir->pDir)
+ {
+ /*
+ * Init data (allocated as all zeros).
+ */
+ pDir->fDataUnread = false; /* spelling it out */
+ }
+ else if (RT_SUCCESS_NP(rc))
+ rc = RTErrConvertFromErrno(errno);
+
+ rtPathFreeNative(pszNativePath, pDir->pszPath);
+ }
+ if (pszSlash)
+ *pszSlash = RTPATH_SLASH;
+ return rc;
+}
+
+
+RTDECL(int) RTDirClose(RTDIR hDir)
+{
+ PRTDIRINTERNAL pDir = hDir;
+
+ /*
+ * Validate input.
+ */
+ if (!pDir)
+ return VERR_INVALID_PARAMETER;
+ if (pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Close the handle.
+ */
+ int rc = VINF_SUCCESS;
+ pDir->u32Magic = RTDIR_MAGIC_DEAD;
+ if (closedir(pDir->pDir))
+ {
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("closedir(%p) -> errno=%d (%Rrc)\n", pDir->pDir, errno, rc));
+ }
+
+ RTMemFree(pDir);
+ return rc;
+}
+
+
+/**
+ * Ensure that there is unread data in the buffer
+ * and that there is a converted filename hanging around.
+ *
+ * @returns IPRT status code.
+ * @param pDir the open directory. Fully validated.
+ */
+static int rtDirReadMore(PRTDIRINTERNAL pDir)
+{
+ /** @todo try avoid the rematching on buffer overflow errors. */
+ for (;;)
+ {
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ struct dirent *pResult = NULL;
+#if RT_GNUC_PREREQ(4, 6)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ int rc = readdir_r(pDir->pDir, &pDir->Data, &pResult);
+#if RT_GNUC_PREREQ(4, 6)
+# pragma GCC diagnostic pop
+#endif
+ if (rc)
+ {
+ rc = RTErrConvertFromErrno(rc);
+ /** @todo Consider translating ENOENT (The current
+ * position of the directory stream is invalid)
+ * differently. */
+ AssertMsg(rc == VERR_FILE_NOT_FOUND, ("%Rrc\n", rc));
+ return rc;
+ }
+ if (!pResult)
+ return VERR_NO_MORE_FILES;
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ if (!pDir->pszName)
+ {
+ int rc = rtPathFromNative(&pDir->pszName, pDir->Data.d_name, pDir->pszPath);
+ if (RT_FAILURE(rc))
+ {
+ pDir->pszName = NULL;
+ return rc;
+ }
+ pDir->cchName = strlen(pDir->pszName);
+ }
+ if ( !pDir->pfnFilter
+ || pDir->pfnFilter(pDir, pDir->pszName))
+ break;
+ rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
+ pDir->pszName = NULL;
+ pDir->fDataUnread = false;
+ }
+
+ pDir->fDataUnread = true;
+ return VINF_SUCCESS;
+}
+
+
+#ifdef HAVE_DIRENT_D_TYPE
+/**
+ * Converts the d_type field to IPRT directory entry type.
+ *
+ * @returns IPRT directory entry type.
+ * @param Unix
+ */
+static RTDIRENTRYTYPE rtDirType(int iType)
+{
+ switch (iType)
+ {
+ case DT_UNKNOWN: return RTDIRENTRYTYPE_UNKNOWN;
+ case DT_FIFO: return RTDIRENTRYTYPE_FIFO;
+ case DT_CHR: return RTDIRENTRYTYPE_DEV_CHAR;
+ case DT_DIR: return RTDIRENTRYTYPE_DIRECTORY;
+ case DT_BLK: return RTDIRENTRYTYPE_DEV_BLOCK;
+ case DT_REG: return RTDIRENTRYTYPE_FILE;
+ case DT_LNK: return RTDIRENTRYTYPE_SYMLINK;
+ case DT_SOCK: return RTDIRENTRYTYPE_SOCKET;
+ case DT_WHT: return RTDIRENTRYTYPE_WHITEOUT;
+ default:
+ AssertMsgFailed(("iType=%d\n", iType));
+ return RTDIRENTRYTYPE_UNKNOWN;
+ }
+}
+#endif /*HAVE_DIRENT_D_TYPE */
+
+
+RTDECL(int) RTDirRead(RTDIR hDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
+{
+ PRTDIRINTERNAL pDir = hDir;
+
+ /*
+ * Validate and digest input.
+ */
+ if (!rtDirValidHandle(pDir))
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
+
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ AssertPtrReturn(pcbDirEntry, VERR_INVALID_POINTER);
+ cbDirEntry = *pcbDirEntry;
+ AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRY, szName[2]),
+ ("Invalid *pcbDirEntry=%d (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])),
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Fetch more data if necessary and/or convert the name.
+ */
+ int rc = rtDirReadMore(pDir);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRY, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired <= cbDirEntry)
+ {
+ /*
+ * Setup the returned data.
+ */
+ pDirEntry->INodeId = pDir->Data.d_ino; /* may need #ifdefing later */
+#ifdef HAVE_DIRENT_D_TYPE
+ pDirEntry->enmType = rtDirType(pDir->Data.d_type);
+#else
+ pDirEntry->enmType = RTDIRENTRYTYPE_UNKNOWN;
+#endif
+ pDirEntry->cbName = (uint16_t)cchName;
+ Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+
+ /* free cached data */
+ pDir->fDataUnread = false;
+ rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
+ pDir->pszName = NULL;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+
+ LogFlow(("RTDirRead(%p:{%s}, %p:{%s}, %p:{%u}): returns %Rrc\n",
+ pDir, pDir->pszPath, pDirEntry, RT_SUCCESS(rc) ? pDirEntry->szName : "<failed>",
+ pcbDirEntry, pcbDirEntry ? *pcbDirEntry : 0, rc));
+ return rc;
+}
+
+
+/**
+ * Fills dummy info into the info structure.
+ * This function is called if we cannot stat the file.
+ *
+ * @param pInfo The struct in question.
+ * @param
+ */
+static void rtDirSetDummyInfo(PRTFSOBJINFO pInfo, RTDIRENTRYTYPE enmType)
+{
+ pInfo->cbObject = 0;
+ pInfo->cbAllocated = 0;
+ RTTimeSpecSetNano(&pInfo->AccessTime, 0);
+ RTTimeSpecSetNano(&pInfo->ModificationTime, 0);
+ RTTimeSpecSetNano(&pInfo->ChangeTime, 0);
+ RTTimeSpecSetNano(&pInfo->BirthTime, 0);
+ memset(&pInfo->Attr, 0, sizeof(pInfo->Attr));
+ pInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ switch (enmType)
+ {
+ default:
+ case RTDIRENTRYTYPE_UNKNOWN: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL; break;
+ case RTDIRENTRYTYPE_FIFO: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FIFO; break;
+ case RTDIRENTRYTYPE_DEV_CHAR: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_DEV_CHAR; break;
+ case RTDIRENTRYTYPE_DIRECTORY: pInfo->Attr.fMode = RTFS_DOS_DIRECTORY | RTFS_TYPE_DIRECTORY; break;
+ case RTDIRENTRYTYPE_DEV_BLOCK: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_DEV_BLOCK; break;
+ case RTDIRENTRYTYPE_FILE: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE; break;
+ case RTDIRENTRYTYPE_SYMLINK: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_SYMLINK; break;
+ case RTDIRENTRYTYPE_SOCKET: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_SOCKET; break;
+ case RTDIRENTRYTYPE_WHITEOUT: pInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_WHITEOUT; break;
+ }
+}
+
+
+RTDECL(int) RTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
+ RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ PRTDIRINTERNAL pDir = hDir;
+
+ /*
+ * Validate and digest input.
+ */
+ if (!rtDirValidHandle(pDir))
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
+ AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
+ && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+ ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ AssertPtrReturn(pcbDirEntry, VERR_INVALID_POINTER);
+ cbDirEntry = *pcbDirEntry;
+ AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRYEX, szName[2]),
+ ("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])),
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Fetch more data if necessary and/or convert the name.
+ */
+ int rc = rtDirReadMore(pDir);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired <= cbDirEntry)
+ {
+ /*
+ * Setup the returned data.
+ */
+ pDirEntry->cwcShortName = 0;
+ pDirEntry->wszShortName[0] = 0;
+ pDirEntry->cbName = (uint16_t)cchName;
+ Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+
+ /* get the info data */
+ size_t cch = cchName + pDir->cchPath + 1;
+ char *pszNamePath = (char *)alloca(cch);
+ if (pszNamePath)
+ {
+ memcpy(pszNamePath, pDir->pszPath, pDir->cchPath);
+ memcpy(pszNamePath + pDir->cchPath, pszName, cchName + 1);
+ rc = RTPathQueryInfoEx(pszNamePath, &pDirEntry->Info, enmAdditionalAttribs, fFlags);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ if (RT_FAILURE(rc))
+ {
+#ifdef HAVE_DIRENT_D_TYPE
+ rtDirSetDummyInfo(&pDirEntry->Info, rtDirType(pDir->Data.d_type));
+#else
+ rtDirSetDummyInfo(&pDirEntry->Info, RTDIRENTRYTYPE_UNKNOWN);
+#endif
+ rc = VWRN_NO_DIRENT_INFO;
+ }
+
+ /* free cached data */
+ pDir->fDataUnread = false;
+ rtPathFreeIprt(pDir->pszName, pDir->Data.d_name);
+ pDir->pszName = NULL;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTDirRewind(RTDIR hDir)
+{
+ PRTDIRINTERNAL pDir = hDir;
+
+ /*
+ * Validate and digest input.
+ */
+ if (!rtDirValidHandle(pDir))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Do the rewinding.
+ */
+ /** @todo OS/2 does not rescan the directory as it should. */
+ rewinddir(pDir->pDir);
+ pDir->fDataUnread = false;
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTDirRename(const char *pszSrc, const char *pszDst, unsigned fRename)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Take common cause with RTPathRename.
+ */
+ int rc = rtPathPosixRename(pszSrc, pszDst, fRename, RTFS_TYPE_DIRECTORY);
+
+ LogFlow(("RTDirRename(%p:{%s}, %p:{%s}): returns %Rrc\n",
+ pszSrc, pszSrc, pszDst, pszDst, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/env-posix.cpp b/src/VBox/Runtime/r3/posix/env-posix.cpp
new file mode 100644
index 00000000..60a9112e
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/env-posix.cpp
@@ -0,0 +1,179 @@
+/* $Id: env-posix.cpp $ */
+/** @file
+ * IPRT - Environment, Posix.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_DARWIN
+/* pick the correct prototype for unsetenv. */
+# define _POSIX_C_SOURCE 1
+#endif
+#include <iprt/env.h>
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#if defined(DEBUG) && defined(RT_OS_LINUX)
+# include <iprt/asm.h>
+#endif
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "internal/alignmentchecks.h"
+
+
+RTDECL(bool) RTEnvExistsBad(const char *pszVar)
+{
+ AssertReturn(strchr(pszVar, '=') == NULL, false);
+ return RTEnvGetBad(pszVar) != NULL;
+}
+
+
+RTDECL(bool) RTEnvExist(const char *pszVar)
+{
+ return RTEnvExistsBad(pszVar);
+}
+
+
+RTDECL(const char *) RTEnvGetBad(const char *pszVar)
+{
+ AssertReturn(strchr(pszVar, '=') == NULL, NULL);
+
+ IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc causes trouble */
+ const char *pszValue = getenv(pszVar);
+ IPRT_ALIGNMENT_CHECKS_ENABLE();
+ return pszValue;
+}
+
+
+RTDECL(const char *) RTEnvGet(const char *pszVar)
+{
+ return RTEnvGetBad(pszVar);
+}
+
+
+RTDECL(int) RTEnvPutBad(const char *pszVarEqualValue)
+{
+ /** @todo putenv is a source memory leaks. deal with this on a per system basis. */
+ if (!putenv((char *)pszVarEqualValue))
+ return 0;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(int) RTEnvPut(const char *pszVarEqualValue)
+{
+ return RTEnvPutBad(pszVarEqualValue);
+}
+
+
+RTDECL(int) RTEnvSetBad(const char *pszVar, const char *pszValue)
+{
+ AssertMsgReturn(strchr(pszVar, '=') == NULL, ("'%s'\n", pszVar), VERR_ENV_INVALID_VAR_NAME);
+
+#if defined(_MSC_VER)
+ /* make a local copy and feed it to putenv. */
+ const size_t cchVar = strlen(pszVar);
+ const size_t cchValue = strlen(pszValue);
+ char *pszTmp = (char *)alloca(cchVar + cchValue + 2 + !*pszValue);
+ memcpy(pszTmp, pszVar, cchVar);
+ pszTmp[cchVar] = '=';
+ if (*pszValue)
+ memcpy(pszTmp + cchVar + 1, pszValue, cchValue + 1);
+ else
+ {
+ pszTmp[cchVar + 1] = ' '; /* wrong, but putenv will remove it otherwise. */
+ pszTmp[cchVar + 2] = '\0';
+ }
+
+ if (!putenv(pszTmp))
+ return 0;
+ return RTErrConvertFromErrno(errno);
+
+#else
+ if (!setenv(pszVar, pszValue, 1))
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+#endif
+}
+
+
+RTDECL(int) RTEnvSet(const char *pszVar, const char *pszValue)
+{
+ return RTEnvSetBad(pszVar, pszValue);
+}
+
+RTDECL(int) RTEnvUnsetBad(const char *pszVar)
+{
+ AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+
+ /*
+ * Check that it exists first.
+ */
+ if (!RTEnvExist(pszVar))
+ return VINF_ENV_VAR_NOT_FOUND;
+
+ /*
+ * Ok, try remove it.
+ */
+#ifdef RT_OS_WINDOWS
+ /* Use putenv(var=) since Windows does not have unsetenv(). */
+ size_t cchVar = strlen(pszVar);
+ char *pszBuf = (char *)alloca(cchVar + 2);
+ memcpy(pszBuf, pszVar, cchVar);
+ pszBuf[cchVar] = '=';
+ pszBuf[cchVar + 1] = '\0';
+
+ if (!putenv(pszBuf))
+ return VINF_SUCCESS;
+
+#else
+ /* This is the preferred function as putenv() like used above does neither work on Solaris nor on Darwin. */
+ if (!unsetenv((char*)pszVar))
+ return VINF_SUCCESS;
+#endif
+
+ return RTErrConvertFromErrno(errno);
+}
+
+RTDECL(int) RTEnvUnset(const char *pszVar)
+{
+ return RTEnvUnsetBad(pszVar);
+}
+
diff --git a/src/VBox/Runtime/r3/posix/errvars-posix.cpp b/src/VBox/Runtime/r3/posix/errvars-posix.cpp
new file mode 100644
index 00000000..b6a98709
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/errvars-posix.cpp
@@ -0,0 +1,88 @@
+/* $Id: errvars-posix.cpp $ */
+/** @file
+ * IPRT - Save and Restore Error Variables, POSIX Ring-3.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <netdb.h>
+#include <errno.h>
+
+#include <iprt/errcore.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include "internal/magics.h"
+
+
+
+RTDECL(PRTERRVARS) RTErrVarsSave(PRTERRVARS pVars)
+{
+ pVars->ai32Vars[0] = RTERRVARS_MAGIC;
+ pVars->ai32Vars[1] = errno;
+ pVars->ai32Vars[2] = h_errno;
+ return pVars;
+}
+
+
+RTDECL(void) RTErrVarsRestore(PCRTERRVARS pVars)
+{
+ AssertReturnVoid(pVars->ai32Vars[0] == RTERRVARS_MAGIC);
+ h_errno = pVars->ai32Vars[2];
+ errno = pVars->ai32Vars[1];
+}
+
+
+RTDECL(bool) RTErrVarsAreEqual(PCRTERRVARS pVars1, PCRTERRVARS pVars2)
+{
+ Assert(pVars1->ai32Vars[0] == RTERRVARS_MAGIC);
+ Assert(pVars2->ai32Vars[0] == RTERRVARS_MAGIC);
+
+ return pVars1->ai32Vars[0] == pVars2->ai32Vars[0]
+ && pVars1->ai32Vars[1] == pVars2->ai32Vars[1]
+ && pVars1->ai32Vars[2] == pVars2->ai32Vars[2];
+}
+
+
+RTDECL(bool) RTErrVarsHaveChanged(PCRTERRVARS pVars)
+{
+ Assert(pVars->ai32Vars[0] == RTERRVARS_MAGIC);
+
+ return pVars->ai32Vars[0] != RTERRVARS_MAGIC
+ || pVars->ai32Vars[1] != errno
+ || pVars->ai32Vars[2] != h_errno;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/fileaio-posix.cpp b/src/VBox/Runtime/r3/posix/fileaio-posix.cpp
new file mode 100644
index 00000000..befa49fb
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/fileaio-posix.cpp
@@ -0,0 +1,1071 @@
+/* $Id: fileaio-posix.cpp $ */
+/** @file
+ * IPRT - File async I/O, native implementation for POSIX compliant host platforms.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/asm.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/thread.h>
+#include <iprt/semaphore.h>
+#include "internal/fileaio.h"
+
+#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+# include <sys/types.h>
+# include <sys/sysctl.h> /* for sysctlbyname */
+#endif
+#if defined(RT_OS_FREEBSD)
+# include <fcntl.h> /* O_SYNC */
+#endif
+#include <aio.h>
+#include <errno.h>
+#include <time.h>
+
+/*
+ * Linux does not define this value.
+ * Just define it with really big
+ * value.
+ */
+#ifndef AIO_LISTIO_MAX
+# define AIO_LISTIO_MAX UINT32_MAX
+#endif
+
+#if 0 /* Only used for debugging */
+# undef AIO_LISTIO_MAX
+# define AIO_LISTIO_MAX 16
+#endif
+
+/** Invalid entry in the waiting array. */
+#define RTFILEAIOCTX_WAIT_ENTRY_INVALID (~0U)
+
+/** No-op replacement for rtFileAioCtxDump for non debug builds */
+#ifndef LOG_ENABLED
+# define rtFileAioCtxDump(pCtxInt) do {} while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Async I/O request state.
+ */
+typedef struct RTFILEAIOREQINTERNAL
+{
+ /** The aio control block. FIRST ELEMENT! */
+ struct aiocb AioCB;
+ /** Next element in the chain. */
+ struct RTFILEAIOREQINTERNAL *pNext;
+ /** Previous element in the chain. */
+ struct RTFILEAIOREQINTERNAL *pPrev;
+ /** Current state the request is in. */
+ RTFILEAIOREQSTATE enmState;
+ /** Flag whether this is a flush request. */
+ bool fFlush;
+ /** Flag indicating if the request was canceled. */
+ volatile bool fCanceled;
+ /** Opaque user data. */
+ void *pvUser;
+ /** Number of bytes actually transferred. */
+ size_t cbTransfered;
+ /** Status code. */
+ int Rc;
+ /** Completion context we are assigned to. */
+ struct RTFILEAIOCTXINTERNAL *pCtxInt;
+ /** Entry in the waiting list the request is in. */
+ unsigned iWaitingList;
+ /** Magic value (RTFILEAIOREQ_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOREQINTERNAL, *PRTFILEAIOREQINTERNAL;
+
+/**
+ * Async I/O completion context state.
+ */
+typedef struct RTFILEAIOCTXINTERNAL
+{
+ /** Current number of requests active on this context. */
+ volatile int32_t cRequests;
+ /** Maximum number of requests this context can handle. */
+ uint32_t cMaxRequests;
+ /** The ID of the thread which is currently waiting for requests. */
+ volatile RTTHREAD hThreadWait;
+ /** Flag whether the thread was woken up. */
+ volatile bool fWokenUp;
+ /** Flag whether the thread is currently waiting in the syscall. */
+ volatile bool fWaiting;
+ /** Flags given during creation. */
+ uint32_t fFlags;
+ /** Magic value (RTFILEAIOCTX_MAGIC). */
+ uint32_t u32Magic;
+ /** Flag whether the thread was woken up due to a internal event. */
+ volatile bool fWokenUpInternal;
+ /** List of new requests which needs to be inserted into apReqs by the
+ * waiting thread. */
+ volatile PRTFILEAIOREQINTERNAL apReqsNewHead[5];
+ /** Special entry for requests which are canceled. Because only one
+ * request can be canceled at a time and the thread canceling the request
+ * has to wait we need only one entry. */
+ volatile PRTFILEAIOREQINTERNAL pReqToCancel;
+ /** Event semaphore the canceling thread is waiting for completion of
+ * the operation. */
+ RTSEMEVENT SemEventCancel;
+ /** Head of submitted elements waiting to get into the array. */
+ PRTFILEAIOREQINTERNAL pReqsWaitHead;
+ /** Tail of submitted elements waiting to get into the array. */
+ PRTFILEAIOREQINTERNAL pReqsWaitTail;
+ /** Maximum number of elements in the waiting array. */
+ unsigned cReqsWaitMax;
+ /** First free slot in the waiting list. */
+ unsigned iFirstFree;
+ /** List of requests we are currently waiting on.
+ * Size depends on cMaxRequests and AIO_LISTIO_MAX. */
+ volatile PRTFILEAIOREQINTERNAL apReqs[1];
+} RTFILEAIOCTXINTERNAL, *PRTFILEAIOCTXINTERNAL;
+
+/**
+ * Internal worker for waking up the waiting thread.
+ */
+static void rtFileAioCtxWakeup(PRTFILEAIOCTXINTERNAL pCtxInt)
+{
+ /*
+ * Read the thread handle before the status flag.
+ * If we read the handle after the flag we might
+ * end up with an invalid handle because the thread
+ * waiting in RTFileAioCtxWakeup() might get scheduled
+ * before we read the flag and returns.
+ * We can ensure that the handle is valid if fWaiting is true
+ * when reading the handle before the status flag.
+ */
+ RTTHREAD hThread;
+ ASMAtomicReadHandle(&pCtxInt->hThreadWait, &hThread);
+ bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
+ if (fWaiting)
+ {
+ /*
+ * If a thread waits the handle must be valid.
+ * It is possible that the thread returns from
+ * aio_suspend() before the signal is send.
+ * This is no problem because we already set fWokenUp
+ * to true which will let the thread return VERR_INTERRUPTED
+ * and the next call to RTFileAioCtxWait() will not
+ * return VERR_INTERRUPTED because signals are not saved
+ * and will simply vanish if the destination thread can't
+ * receive it.
+ */
+ Assert(hThread != NIL_RTTHREAD);
+ RTThreadPoke(hThread);
+ }
+}
+
+/**
+ * Internal worker processing events and inserting new requests into the waiting list.
+ */
+static int rtFileAioCtxProcessEvents(PRTFILEAIOCTXINTERNAL pCtxInt)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Process new requests first. */
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUpInternal, false);
+ if (fWokenUp)
+ {
+ for (unsigned iSlot = 0; iSlot < RT_ELEMENTS(pCtxInt->apReqsNewHead); iSlot++)
+ {
+ PRTFILEAIOREQINTERNAL pReqHead = ASMAtomicXchgPtrT(&pCtxInt->apReqsNewHead[iSlot], NULL, PRTFILEAIOREQINTERNAL);
+
+ while ( (pCtxInt->iFirstFree < pCtxInt->cReqsWaitMax)
+ && pReqHead)
+ {
+ RTFIELAIOREQ_ASSERT_STATE(pReqHead, SUBMITTED);
+ pCtxInt->apReqs[pCtxInt->iFirstFree] = pReqHead;
+ pReqHead->iWaitingList = pCtxInt->iFirstFree;
+ pReqHead = pReqHead->pNext;
+
+ /* Clear pointer to next and previous element just for safety. */
+ pCtxInt->apReqs[pCtxInt->iFirstFree]->pNext = NULL;
+ pCtxInt->apReqs[pCtxInt->iFirstFree]->pPrev = NULL;
+ pCtxInt->iFirstFree++;
+
+ Assert( (pCtxInt->iFirstFree <= pCtxInt->cMaxRequests)
+ && (pCtxInt->iFirstFree <= pCtxInt->cReqsWaitMax));
+ }
+
+ /* Append the rest to the wait list. */
+ if (pReqHead)
+ {
+ RTFIELAIOREQ_ASSERT_STATE(pReqHead, SUBMITTED);
+ if (!pCtxInt->pReqsWaitHead)
+ {
+ Assert(!pCtxInt->pReqsWaitTail);
+ pCtxInt->pReqsWaitHead = pReqHead;
+ pReqHead->pPrev = NULL;
+ }
+ else
+ {
+ AssertPtr(pCtxInt->pReqsWaitTail);
+
+ pCtxInt->pReqsWaitTail->pNext = pReqHead;
+ pReqHead->pPrev = pCtxInt->pReqsWaitTail;
+ }
+
+ /* Update tail. */
+ while (pReqHead->pNext)
+ {
+ RTFIELAIOREQ_ASSERT_STATE(pReqHead->pNext, SUBMITTED);
+ pReqHead = pReqHead->pNext;
+ }
+
+ pCtxInt->pReqsWaitTail = pReqHead;
+ pCtxInt->pReqsWaitTail->pNext = NULL;
+ }
+ }
+
+ /* Check if a request needs to be canceled. */
+ PRTFILEAIOREQINTERNAL pReqToCancel = ASMAtomicReadPtrT(&pCtxInt->pReqToCancel, PRTFILEAIOREQINTERNAL);
+ if (pReqToCancel)
+ {
+ /* The request can be in the array waiting for completion or still in the list because it is full. */
+ if (pReqToCancel->iWaitingList != RTFILEAIOCTX_WAIT_ENTRY_INVALID)
+ {
+ /* Put it out of the waiting list. */
+ pCtxInt->apReqs[pReqToCancel->iWaitingList] = pCtxInt->apReqs[--pCtxInt->iFirstFree];
+ pCtxInt->apReqs[pReqToCancel->iWaitingList]->iWaitingList = pReqToCancel->iWaitingList;
+ }
+ else
+ {
+ /* Unlink from the waiting list. */
+ PRTFILEAIOREQINTERNAL pPrev = pReqToCancel->pPrev;
+ PRTFILEAIOREQINTERNAL pNext = pReqToCancel->pNext;
+
+ if (pNext)
+ pNext->pPrev = pPrev;
+ else
+ {
+ /* We canceled the tail. */
+ pCtxInt->pReqsWaitTail = pPrev;
+ }
+
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ {
+ /* We canceled the head. */
+ pCtxInt->pReqsWaitHead = pNext;
+ }
+ }
+
+ ASMAtomicDecS32(&pCtxInt->cRequests);
+ AssertMsg(pCtxInt->cRequests >= 0, ("Canceled request not which is not in this context\n"));
+ RTSemEventSignal(pCtxInt->SemEventCancel);
+ }
+ }
+ else
+ {
+ if (ASMAtomicXchgBool(&pCtxInt->fWokenUp, false))
+ rc = VERR_INTERRUPTED;
+ }
+
+ return rc;
+}
+
+RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
+{
+ int rcBSD = 0;
+ AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
+
+#if defined(RT_OS_DARWIN)
+ int cReqsOutstandingMax = 0;
+ size_t cbParameter = sizeof(int);
+
+ rcBSD = sysctlbyname("kern.aioprocmax", /* name */
+ &cReqsOutstandingMax, /* Where to store the old value. */
+ &cbParameter, /* Size of the memory pointed to. */
+ NULL, /* Where the new value is located. */
+ 0); /* Where the size of the new value is stored. */
+ if (rcBSD == -1)
+ return RTErrConvertFromErrno(errno);
+
+ pAioLimits->cReqsOutstandingMax = cReqsOutstandingMax;
+ pAioLimits->cbBufferAlignment = 0;
+#elif defined(RT_OS_FREEBSD)
+ /*
+ * The AIO API is implemented in a kernel module which is not
+ * loaded by default.
+ * If it is loaded there are additional sysctl parameters.
+ */
+ int cReqsOutstandingMax = 0;
+ size_t cbParameter = sizeof(int);
+
+ rcBSD = sysctlbyname("vfs.aio.max_aio_per_proc", /* name */
+ &cReqsOutstandingMax, /* Where to store the old value. */
+ &cbParameter, /* Size of the memory pointed to. */
+ NULL, /* Where the new value is located. */
+ 0); /* Where the size of the new value is stored. */
+ if (rcBSD == -1)
+ {
+ /* ENOENT means the value is unknown thus the module is not loaded. */
+ if (errno == ENOENT)
+ return VERR_NOT_SUPPORTED;
+ else
+ return RTErrConvertFromErrno(errno);
+ }
+
+ pAioLimits->cReqsOutstandingMax = cReqsOutstandingMax;
+ pAioLimits->cbBufferAlignment = 0;
+#else
+ pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS;
+ pAioLimits->cbBufferAlignment = 0;
+#endif
+
+ return VINF_SUCCESS;
+}
+
+RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
+{
+ AssertPtrReturn(phReq, VERR_INVALID_POINTER);
+
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL));
+ if (RT_UNLIKELY(!pReqInt))
+ return VERR_NO_MEMORY;
+
+ pReqInt->pCtxInt = NULL;
+ pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
+ pReqInt->iWaitingList = RTFILEAIOCTX_WAIT_ENTRY_INVALID;
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ *phReq = (RTFILEAIOREQ)pReqInt;
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
+{
+ /*
+ * Validate the handle and ignore nil.
+ */
+ if (hReq == NIL_RTFILEAIOREQ)
+ return VINF_SUCCESS;
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+
+ /*
+ * Trash the magic and free it.
+ */
+ ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
+ RTMemFree(pReqInt);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Worker setting up the request.
+ */
+DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
+ unsigned uTransferDirection,
+ RTFOFF off, void *pvBuf, size_t cbTransfer,
+ void *pvUser)
+{
+ /*
+ * Validate the input.
+ */
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ Assert(hFile != NIL_RTFILE);
+ AssertPtr(pvBuf);
+ Assert(off >= 0);
+ Assert(cbTransfer > 0);
+
+ memset(&pReqInt->AioCB, 0, sizeof(struct aiocb));
+ pReqInt->fFlush = false;
+ pReqInt->AioCB.aio_lio_opcode = uTransferDirection;
+ pReqInt->AioCB.aio_fildes = RTFileToNative(hFile);
+ pReqInt->AioCB.aio_offset = off;
+ pReqInt->AioCB.aio_nbytes = cbTransfer;
+ pReqInt->AioCB.aio_buf = pvBuf;
+ pReqInt->pvUser = pvUser;
+ pReqInt->pCtxInt = NULL;
+ pReqInt->Rc = VERR_FILE_AIO_IN_PROGRESS;
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void *pvBuf, size_t cbRead, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, LIO_READ,
+ off, pvBuf, cbRead, pvUser);
+}
+
+
+RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void const *pvBuf, size_t cbWrite, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, LIO_WRITE,
+ off, (void *)pvBuf, cbWrite, pvUser);
+}
+
+
+RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)hReq;
+
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ Assert(hFile != NIL_RTFILE);
+
+ pReqInt->fFlush = true;
+ pReqInt->AioCB.aio_fildes = RTFileToNative(hFile);
+ pReqInt->AioCB.aio_offset = 0;
+ pReqInt->AioCB.aio_nbytes = 0;
+ pReqInt->AioCB.aio_buf = NULL;
+ pReqInt->pvUser = pvUser;
+ pReqInt->Rc = VERR_FILE_AIO_IN_PROGRESS;
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
+
+ return pReqInt->pvUser;
+}
+
+
+RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ ASMAtomicXchgBool(&pReqInt->fCanceled, true);
+
+ int rcPosix = aio_cancel(pReqInt->AioCB.aio_fildes, &pReqInt->AioCB);
+
+ if (rcPosix == AIO_CANCELED)
+ {
+ PRTFILEAIOCTXINTERNAL pCtxInt = pReqInt->pCtxInt;
+ /*
+ * Notify the waiting thread that the request was canceled.
+ */
+ AssertMsg(RT_VALID_PTR(pCtxInt), ("Invalid state. Request was canceled but wasn't submitted\n"));
+
+ Assert(!pCtxInt->pReqToCancel);
+ ASMAtomicWritePtr(&pCtxInt->pReqToCancel, pReqInt);
+ rtFileAioCtxWakeup(pCtxInt);
+
+ /* Wait for acknowledge. */
+ int rc = RTSemEventWait(pCtxInt->SemEventCancel, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+
+ ASMAtomicWriteNullPtr(&pCtxInt->pReqToCancel);
+ pReqInt->Rc = VERR_FILE_AIO_CANCELED;
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ return VINF_SUCCESS;
+ }
+ else if (rcPosix == AIO_ALLDONE)
+ return VERR_FILE_AIO_COMPLETED;
+ else if (rcPosix == AIO_NOTCANCELED)
+ return VERR_FILE_AIO_IN_PROGRESS;
+ else
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
+ AssertPtrNull(pcbTransfered);
+
+ if ( (RT_SUCCESS(pReqInt->Rc))
+ && (pcbTransfered))
+ *pcbTransfered = pReqInt->cbTransfered;
+
+ return pReqInt->Rc;
+}
+
+
+RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax,
+ uint32_t fFlags)
+{
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ unsigned cReqsWaitMax;
+
+ AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ if (cAioReqsMax == RTFILEAIO_UNLIMITED_REQS)
+ return VERR_OUT_OF_RANGE;
+
+ cReqsWaitMax = RT_MIN(cAioReqsMax, AIO_LISTIO_MAX);
+
+ pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ( sizeof(RTFILEAIOCTXINTERNAL)
+ + cReqsWaitMax * sizeof(PRTFILEAIOREQINTERNAL));
+ if (RT_UNLIKELY(!pCtxInt))
+ return VERR_NO_MEMORY;
+
+ /* Create event semaphore. */
+ int rc = RTSemEventCreate(&pCtxInt->SemEventCancel);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pCtxInt);
+ return rc;
+ }
+
+ pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
+ pCtxInt->cMaxRequests = cAioReqsMax;
+ pCtxInt->cReqsWaitMax = cReqsWaitMax;
+ pCtxInt->fFlags = fFlags;
+ *phAioCtx = (RTFILEAIOCTX)pCtxInt;
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
+{
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+
+ AssertPtrReturn(pCtxInt, VERR_INVALID_HANDLE);
+
+ if (RT_UNLIKELY(pCtxInt->cRequests))
+ return VERR_FILE_AIO_BUSY;
+
+ RTSemEventDestroy(pCtxInt->SemEventCancel);
+ RTMemFree(pCtxInt);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
+{
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+
+ if (hAioCtx == NIL_RTFILEAIOCTX)
+ return RTFILEAIO_UNLIMITED_REQS;
+ return pCtxInt->cMaxRequests;
+}
+
+RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
+{
+ NOREF(hAioCtx); NOREF(hFile);
+ return VINF_SUCCESS;
+}
+
+#ifdef LOG_ENABLED
+/**
+ * Dumps the state of a async I/O context.
+ */
+static void rtFileAioCtxDump(PRTFILEAIOCTXINTERNAL pCtxInt)
+{
+ LogFlow(("cRequests=%d\n", pCtxInt->cRequests));
+ LogFlow(("cMaxRequests=%u\n", pCtxInt->cMaxRequests));
+ LogFlow(("hThreadWait=%#p\n", pCtxInt->hThreadWait));
+ LogFlow(("fWokenUp=%RTbool\n", pCtxInt->fWokenUp));
+ LogFlow(("fWaiting=%RTbool\n", pCtxInt->fWaiting));
+ LogFlow(("fWokenUpInternal=%RTbool\n", pCtxInt->fWokenUpInternal));
+ for (unsigned i = 0; i < RT_ELEMENTS(pCtxInt->apReqsNewHead); i++)
+ LogFlow(("apReqsNewHead[%u]=%#p\n", i, pCtxInt->apReqsNewHead[i]));
+ LogFlow(("pReqToCancel=%#p\n", pCtxInt->pReqToCancel));
+ LogFlow(("pReqsWaitHead=%#p\n", pCtxInt->pReqsWaitHead));
+ LogFlow(("pReqsWaitTail=%#p\n", pCtxInt->pReqsWaitTail));
+ LogFlow(("cReqsWaitMax=%u\n", pCtxInt->cReqsWaitMax));
+ LogFlow(("iFirstFree=%u\n", pCtxInt->iFirstFree));
+ for (unsigned i = 0; i < pCtxInt->cReqsWaitMax; i++)
+ LogFlow(("apReqs[%u]=%#p\n", i, pCtxInt->apReqs[i]));
+}
+#endif
+
+RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+
+ /* Parameter checks */
+ AssertPtrReturn(pCtxInt, VERR_INVALID_HANDLE);
+ AssertReturn(cReqs != 0, VERR_INVALID_POINTER);
+ AssertPtrReturn(pahReqs, VERR_INVALID_PARAMETER);
+
+ rtFileAioCtxDump(pCtxInt);
+
+ /* Check that we don't exceed the limit */
+ if (ASMAtomicUoReadS32(&pCtxInt->cRequests) + cReqs > pCtxInt->cMaxRequests)
+ return VERR_FILE_AIO_LIMIT_EXCEEDED;
+
+ PRTFILEAIOREQINTERNAL pHead = NULL;
+
+ do
+ {
+ int rcPosix = 0;
+ size_t cReqsSubmit = 0;
+ size_t i = 0;
+ PRTFILEAIOREQINTERNAL pReqInt;
+
+ while ( (i < cReqs)
+ && (i < AIO_LISTIO_MAX))
+ {
+ pReqInt = pahReqs[i];
+ if (RTFILEAIOREQ_IS_NOT_VALID(pReqInt))
+ {
+ /* Undo everything and stop submitting. */
+ for (size_t iUndo = 0; iUndo < i; iUndo++)
+ {
+ pReqInt = pahReqs[iUndo];
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+ pReqInt->pCtxInt = NULL;
+
+ /* Unlink from the list again. */
+ PRTFILEAIOREQINTERNAL pNext, pPrev;
+ pNext = pReqInt->pNext;
+ pPrev = pReqInt->pPrev;
+ if (pNext)
+ pNext->pPrev = pPrev;
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ pHead = pNext;
+ }
+ rc = VERR_INVALID_HANDLE;
+ break;
+ }
+
+ pReqInt->pCtxInt = pCtxInt;
+
+ if (pReqInt->fFlush)
+ break;
+
+ /* Link them together. */
+ pReqInt->pNext = pHead;
+ if (pHead)
+ pHead->pPrev = pReqInt;
+ pReqInt->pPrev = NULL;
+ pHead = pReqInt;
+ RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
+
+ cReqsSubmit++;
+ i++;
+ }
+
+ if (cReqsSubmit)
+ {
+ rcPosix = lio_listio(LIO_NOWAIT, (struct aiocb **)pahReqs, cReqsSubmit, NULL);
+ if (RT_UNLIKELY(rcPosix < 0))
+ {
+ size_t cReqsSubmitted = cReqsSubmit;
+
+ if (errno == EAGAIN)
+ rc = VERR_FILE_AIO_INSUFFICIENT_RESSOURCES;
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ /* Check which ones were not submitted. */
+ for (i = 0; i < cReqsSubmit; i++)
+ {
+ pReqInt = pahReqs[i];
+
+ rcPosix = aio_error(&pReqInt->AioCB);
+
+ if ((rcPosix != EINPROGRESS) && (rcPosix != 0))
+ {
+ cReqsSubmitted--;
+
+#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+ if (errno == EINVAL)
+#else
+ if (rcPosix == EINVAL)
+#endif
+ {
+ /* Was not submitted. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+ }
+ else
+ {
+ /* An error occurred. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ /*
+ * Looks like Apple and glibc interpret the standard in different ways.
+ * glibc returns the error code which would be in errno but Apple returns
+ * -1 and sets errno to the appropriate value
+ */
+#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+ Assert(rcPosix == -1);
+ pReqInt->Rc = RTErrConvertFromErrno(errno);
+#elif defined(RT_OS_LINUX)
+ pReqInt->Rc = RTErrConvertFromErrno(rcPosix);
+#endif
+ pReqInt->cbTransfered = 0;
+ }
+ /* Unlink from the list. */
+ PRTFILEAIOREQINTERNAL pNext, pPrev;
+ pNext = pReqInt->pNext;
+ pPrev = pReqInt->pPrev;
+ if (pNext)
+ pNext->pPrev = pPrev;
+ if (pPrev)
+ pPrev->pNext = pNext;
+ else
+ pHead = pNext;
+
+ pReqInt->pNext = NULL;
+ pReqInt->pPrev = NULL;
+ }
+ }
+ ASMAtomicAddS32(&pCtxInt->cRequests, cReqsSubmitted);
+ AssertMsg(pCtxInt->cRequests >= 0, ("Adding requests resulted in overflow\n"));
+ break;
+ }
+
+ ASMAtomicAddS32(&pCtxInt->cRequests, cReqsSubmit);
+ AssertMsg(pCtxInt->cRequests >= 0, ("Adding requests resulted in overflow\n"));
+ cReqs -= cReqsSubmit;
+ pahReqs += cReqsSubmit;
+ }
+
+ /*
+ * Check if we have a flush request now.
+ * If not we hit the AIO_LISTIO_MAX limit
+ * and will continue submitting requests
+ * above.
+ */
+ if (cReqs && RT_SUCCESS_NP(rc))
+ {
+ pReqInt = pahReqs[0];
+
+ if (pReqInt->fFlush)
+ {
+ /*
+ * lio_listio does not work with flush requests so
+ * we have to use aio_fsync directly.
+ */
+ rcPosix = aio_fsync(O_SYNC, &pReqInt->AioCB);
+ if (RT_UNLIKELY(rcPosix < 0))
+ {
+ if (errno == EAGAIN)
+ {
+ rc = VERR_FILE_AIO_INSUFFICIENT_RESSOURCES;
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ pReqInt->Rc = rc;
+ }
+ pReqInt->cbTransfered = 0;
+ break;
+ }
+
+ /* Link them together. */
+ pReqInt->pNext = pHead;
+ if (pHead)
+ pHead->pPrev = pReqInt;
+ pReqInt->pPrev = NULL;
+ pHead = pReqInt;
+ RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
+
+ ASMAtomicIncS32(&pCtxInt->cRequests);
+ AssertMsg(pCtxInt->cRequests >= 0, ("Adding requests resulted in overflow\n"));
+ cReqs--;
+ pahReqs++;
+ }
+ }
+ } while ( cReqs
+ && RT_SUCCESS_NP(rc));
+
+ if (pHead)
+ {
+ /*
+ * Forward successfully submitted requests to the thread waiting for requests.
+ * We search for a free slot first and if we don't find one
+ * we will grab the first one and append our list to the existing entries.
+ */
+ unsigned iSlot = 0;
+ while ( (iSlot < RT_ELEMENTS(pCtxInt->apReqsNewHead))
+ && !ASMAtomicCmpXchgPtr(&pCtxInt->apReqsNewHead[iSlot], pHead, NULL))
+ iSlot++;
+
+ if (iSlot == RT_ELEMENTS(pCtxInt->apReqsNewHead))
+ {
+ /* Nothing found. */
+ PRTFILEAIOREQINTERNAL pOldHead = ASMAtomicXchgPtrT(&pCtxInt->apReqsNewHead[0], NULL, PRTFILEAIOREQINTERNAL);
+
+ /* Find the end of the current head and link the old list to the current. */
+ PRTFILEAIOREQINTERNAL pTail = pHead;
+ while (pTail->pNext)
+ pTail = pTail->pNext;
+
+ pTail->pNext = pOldHead;
+
+ ASMAtomicWritePtr(&pCtxInt->apReqsNewHead[0], pHead);
+ }
+
+ /* Set the internal wakeup flag and wakeup the thread if possible. */
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUpInternal, true);
+ if (!fWokenUp)
+ rtFileAioCtxWakeup(pCtxInt);
+ }
+
+ rtFileAioCtxDump(pCtxInt);
+
+ return rc;
+}
+
+
+RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
+ PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
+{
+ int rc = VINF_SUCCESS;
+ int cRequestsCompleted = 0;
+ PRTFILEAIOCTXINTERNAL pCtxInt = (PRTFILEAIOCTXINTERNAL)hAioCtx;
+ struct timespec Timeout;
+ struct timespec *pTimeout = NULL;
+ uint64_t StartNanoTS = 0;
+
+ LogFlowFunc(("hAioCtx=%#p cMinReqs=%zu cMillies=%u pahReqs=%#p cReqs=%zu pcbReqs=%#p\n",
+ hAioCtx, cMinReqs, cMillies, pahReqs, cReqs, pcReqs));
+
+ /* Check parameters. */
+ AssertPtrReturn(pCtxInt, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
+
+ rtFileAioCtxDump(pCtxInt);
+
+ int32_t cRequestsWaiting = ASMAtomicReadS32(&pCtxInt->cRequests);
+
+ if ( RT_UNLIKELY(cRequestsWaiting <= 0)
+ && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
+ return VERR_FILE_AIO_NO_REQUEST;
+
+ if (RT_UNLIKELY(cMinReqs > (uint32_t)cRequestsWaiting))
+ return VERR_INVALID_PARAMETER;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ Timeout.tv_sec = cMillies / 1000;
+ Timeout.tv_nsec = (cMillies % 1000) * 1000000;
+ pTimeout = &Timeout;
+ StartNanoTS = RTTimeNanoTS();
+ }
+
+ /* Wait for at least one. */
+ if (!cMinReqs)
+ cMinReqs = 1;
+
+ /* For the wakeup call. */
+ Assert(pCtxInt->hThreadWait == NIL_RTTHREAD);
+ ASMAtomicWriteHandle(&pCtxInt->hThreadWait, RTThreadSelf());
+
+ /* Update the waiting list once before we enter the loop. */
+ rc = rtFileAioCtxProcessEvents(pCtxInt);
+
+ while ( cMinReqs
+ && RT_SUCCESS_NP(rc))
+ {
+#ifdef RT_STRICT
+ if (RT_UNLIKELY(!pCtxInt->iFirstFree))
+ {
+ for (unsigned i = 0; i < pCtxInt->cReqsWaitMax; i++)
+ RTAssertMsg2Weak("wait[%d] = %#p\n", i, pCtxInt->apReqs[i]);
+
+ AssertMsgFailed(("No request to wait for. pReqsWaitHead=%#p pReqsWaitTail=%#p\n",
+ pCtxInt->pReqsWaitHead, pCtxInt->pReqsWaitTail));
+ }
+#endif
+
+ LogFlow(("Waiting for %d requests to complete\n", pCtxInt->iFirstFree));
+ rtFileAioCtxDump(pCtxInt);
+
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, true);
+ int rcPosix = aio_suspend((const struct aiocb * const *)pCtxInt->apReqs,
+ pCtxInt->iFirstFree, pTimeout);
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, false);
+ if (rcPosix < 0)
+ {
+ LogFlow(("aio_suspend failed %d nent=%u\n", errno, pCtxInt->iFirstFree));
+ /* Check that this is an external wakeup event. */
+ if (errno == EINTR)
+ rc = rtFileAioCtxProcessEvents(pCtxInt);
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ {
+ /* Requests finished. */
+ unsigned iReqCurr = 0;
+ unsigned cDone = 0;
+
+ /* Remove completed requests from the waiting list. */
+ while ( (iReqCurr < pCtxInt->iFirstFree)
+ && (cDone < cReqs))
+ {
+ PRTFILEAIOREQINTERNAL pReq = pCtxInt->apReqs[iReqCurr];
+ int rcReq = aio_error(&pReq->AioCB);
+
+ if (rcReq != EINPROGRESS)
+ {
+ /* Completed store the return code. */
+ if (rcReq == 0)
+ {
+ pReq->Rc = VINF_SUCCESS;
+ /* Call aio_return() to free resources. */
+ pReq->cbTransfered = aio_return(&pReq->AioCB);
+ }
+ else
+ {
+#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+ pReq->Rc = RTErrConvertFromErrno(errno);
+#else
+ pReq->Rc = RTErrConvertFromErrno(rcReq);
+#endif
+ }
+
+ /* Mark the request as finished. */
+ RTFILEAIOREQ_SET_STATE(pReq, COMPLETED);
+ cDone++;
+
+ /* If there are other entries waiting put the head into the now free entry. */
+ if (pCtxInt->pReqsWaitHead)
+ {
+ PRTFILEAIOREQINTERNAL pReqInsert = pCtxInt->pReqsWaitHead;
+
+ pCtxInt->pReqsWaitHead = pReqInsert->pNext;
+ if (!pCtxInt->pReqsWaitHead)
+ {
+ /* List is empty now. Clear tail too. */
+ pCtxInt->pReqsWaitTail = NULL;
+ }
+
+ pReqInsert->iWaitingList = pReq->iWaitingList;
+ pCtxInt->apReqs[pReqInsert->iWaitingList] = pReqInsert;
+ iReqCurr++;
+ }
+ else
+ {
+ /*
+ * Move the last entry into the current position to avoid holes
+ * but only if it is not the last element already.
+ */
+ if (pReq->iWaitingList < pCtxInt->iFirstFree - 1)
+ {
+ pCtxInt->apReqs[pReq->iWaitingList] = pCtxInt->apReqs[--pCtxInt->iFirstFree];
+ pCtxInt->apReqs[pReq->iWaitingList]->iWaitingList = pReq->iWaitingList;
+ }
+ else
+ pCtxInt->iFirstFree--;
+
+ pCtxInt->apReqs[pCtxInt->iFirstFree] = NULL;
+ }
+
+ /* Put the request into the completed list. */
+ pahReqs[cRequestsCompleted++] = pReq;
+ pReq->iWaitingList = RTFILEAIOCTX_WAIT_ENTRY_INVALID;
+ }
+ else
+ iReqCurr++;
+ }
+
+ AssertMsg((cDone <= cReqs), ("Overflow cReqs=%u cMinReqs=%u cDone=%u\n",
+ cReqs, cDone));
+ cReqs -= cDone;
+ cMinReqs = RT_MAX(cMinReqs, cDone) - cDone;
+ ASMAtomicSubS32(&pCtxInt->cRequests, cDone);
+
+ AssertMsg(pCtxInt->cRequests >= 0, ("Finished more requests than currently active\n"));
+
+ if (!cMinReqs)
+ break;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ uint64_t TimeDiff;
+
+ /* Recalculate the timeout. */
+ TimeDiff = RTTimeSystemNanoTS() - StartNanoTS;
+ Timeout.tv_sec = Timeout.tv_sec - (TimeDiff / 1000000);
+ Timeout.tv_nsec = Timeout.tv_nsec - (TimeDiff % 1000000);
+ }
+
+ /* Check for new elements. */
+ rc = rtFileAioCtxProcessEvents(pCtxInt);
+ }
+ }
+
+ *pcReqs = cRequestsCompleted;
+ Assert(pCtxInt->hThreadWait == RTThreadSelf());
+ ASMAtomicWriteHandle(&pCtxInt->hThreadWait, NIL_RTTHREAD);
+
+ rtFileAioCtxDump(pCtxInt);
+
+ return rc;
+}
+
+
+RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
+{
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ /** @todo r=bird: Define the protocol for how to resume work after calling
+ * this function. */
+
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
+ if (!fWokenUp)
+ rtFileAioCtxWakeup(pCtxInt);
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/fileio-at-posix.cpp b/src/VBox/Runtime/r3/posix/fileio-at-posix.cpp
new file mode 100644
index 00000000..72d3cc05
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/fileio-at-posix.cpp
@@ -0,0 +1,107 @@
+/* $Id: fileio-at-posix.cpp $ */
+/** @file
+ * IPRT - File I/O, RTFileReadAt and RTFileWriteAt, posix.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/err.h>
+#include <iprt/log.h>
+
+
+
+RTDECL(int) RTFileReadAt(RTFILE hFile, RTFOFF off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ ssize_t cbRead = pread(RTFileToNative(hFile), pvBuf, cbToRead, off);
+ if (cbRead >= 0)
+ {
+ if (pcbRead)
+ /* caller can handle partial read. */
+ *pcbRead = cbRead;
+ else
+ {
+ /* Caller expects all to be read. */
+ while ((ssize_t)cbToRead > cbRead)
+ {
+ ssize_t cbReadPart = pread(RTFileToNative(hFile), (char*)pvBuf + cbRead, cbToRead - cbRead, off + cbRead);
+ if (cbReadPart <= 0)
+ {
+ if (cbReadPart == 0)
+ return VERR_EOF;
+ return RTErrConvertFromErrno(errno);
+ }
+ cbRead += cbReadPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(int) RTFileWriteAt(RTFILE hFile, RTFOFF off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ ssize_t cbWritten = pwrite(RTFileToNative(hFile), pvBuf, cbToWrite, off);
+ if (cbWritten >= 0)
+ {
+ if (pcbWritten)
+ /* caller can handle partial write. */
+ *pcbWritten = cbWritten;
+ else
+ {
+ /* Caller expects all to be write. */
+ while ((ssize_t)cbToWrite > cbWritten)
+ {
+ ssize_t cbWrittenPart = pwrite(RTFileToNative(hFile), (const char *)pvBuf + cbWritten, cbToWrite - cbWritten,
+ off + cbWritten);
+ if (cbWrittenPart < 0)
+ return cbWrittenPart < 0 ? RTErrConvertFromErrno(errno) : VERR_TRY_AGAIN;
+ cbWritten += cbWrittenPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
diff --git a/src/VBox/Runtime/r3/posix/fileio-posix.cpp b/src/VBox/Runtime/r3/posix/fileio-posix.cpp
new file mode 100644
index 00000000..cd7d238f
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/fileio-posix.cpp
@@ -0,0 +1,934 @@
+/* $Id: fileio-posix.cpp $ */
+/** @file
+ * IPRT - File I/O, POSIX, Part 1.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#ifdef _MSC_VER
+# include <io.h>
+# include <stdio.h>
+#else
+# include <unistd.h>
+# include <sys/time.h>
+#endif
+#ifdef RT_OS_LINUX
+# include <sys/file.h>
+#endif
+#if defined(RT_OS_OS2) && (!defined(__INNOTEK_LIBC__) || __INNOTEK_LIBC__ < 0x006)
+# include <io.h>
+#endif
+#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+# include <sys/disk.h>
+#endif
+#ifdef RT_OS_SOLARIS
+# include <stropts.h>
+# include <sys/dkio.h>
+# include <sys/vtoc.h>
+#endif /* RT_OS_SOLARIS */
+
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/thread.h>
+#include "internal/file.h"
+#include "internal/fs.h"
+#include "internal/path.h"
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Default file permissions for newly created files. */
+#if defined(S_IRUSR) && defined(S_IWUSR)
+# define RT_FILE_PERMISSION (S_IRUSR | S_IWUSR)
+#else
+# define RT_FILE_PERMISSION (00600)
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifdef O_CLOEXEC
+static int volatile g_fHave_O_CLOEXEC = 0; /* {-1,0,1}; since Linux 2.6.23 */
+#endif
+
+
+
+RTDECL(bool) RTFileExists(const char *pszPath)
+{
+ bool fRc = false;
+ char const *pszNativePath;
+ int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat s;
+ fRc = !stat(pszNativePath, &s)
+ && S_ISREG(s.st_mode);
+
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+
+ LogFlow(("RTFileExists(%p={%s}): returns %RTbool\n", pszPath, pszPath, fRc));
+ return fRc;
+}
+
+
+#ifdef O_CLOEXEC
+/** Worker for RTFileOpenEx that detects whether the kernel supports
+ * O_CLOEXEC or not, setting g_fHave_O_CLOEXEC to 1 or -1 accordingly. */
+static int rtFileOpenExDetectCloExecSupport(void)
+{
+ /*
+ * Open /dev/null with O_CLOEXEC and see if FD_CLOEXEC is set or not.
+ */
+ int fHave_O_CLOEXEC = -1;
+ int fd = open("/dev/null", O_RDONLY | O_CLOEXEC, 0);
+ if (fd >= 0)
+ {
+ int fFlags = fcntl(fd, F_GETFD, 0);
+ fHave_O_CLOEXEC = fFlags > 0 && (fFlags & FD_CLOEXEC) ? 1 : -1;
+ close(fd);
+ }
+ else
+ AssertMsg(errno == EINVAL, ("%d\n", errno));
+ g_fHave_O_CLOEXEC = fHave_O_CLOEXEC;
+ return fHave_O_CLOEXEC;
+}
+#endif
+
+
+RTR3DECL(int) RTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen)
+{
+ return RTFileOpenEx(pszFilename, fOpen, pFile, NULL);
+}
+
+
+RTDECL(int) RTFileOpenEx(const char *pszFilename, uint64_t fOpen, PRTFILE phFile, PRTFILEACTION penmActionTaken)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phFile, VERR_INVALID_POINTER);
+ *phFile = NIL_RTFILE;
+ if (penmActionTaken)
+ *penmActionTaken = RTFILEACTION_INVALID;
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+
+ /*
+ * Merge forced open flags and validate them.
+ */
+ int rc = rtFileRecalcAndValidateFlags(&fOpen);
+ if (RT_FAILURE(rc))
+ return rc;
+#ifndef O_NONBLOCK
+ AssertReturn(!(fOpen & RTFILE_O_NON_BLOCK), VERR_INVALID_FLAGS);
+#endif
+#if defined(RT_OS_OS2) /* Cannot delete open files on OS/2. */
+ AssertReturn(!(fOpen & RTFILE_O_TEMP_AUTO_DELETE), VERR_NOT_SUPPORTED);
+#endif
+
+ /*
+ * Calculate open mode flags.
+ */
+ int fOpenMode = 0;
+#ifdef O_BINARY
+ fOpenMode |= O_BINARY; /* (pc) */
+#endif
+#ifdef O_LARGEFILE
+ fOpenMode |= O_LARGEFILE; /* (linux, solaris) */
+#endif
+#ifdef O_NOINHERIT
+ if (!(fOpen & RTFILE_O_INHERIT))
+ fOpenMode |= O_NOINHERIT;
+#endif
+#ifdef O_CLOEXEC
+ int fHave_O_CLOEXEC = g_fHave_O_CLOEXEC;
+ if ( !(fOpen & RTFILE_O_INHERIT)
+ && ( fHave_O_CLOEXEC > 0
+ || ( fHave_O_CLOEXEC == 0
+ && (fHave_O_CLOEXEC = rtFileOpenExDetectCloExecSupport()) > 0)))
+ fOpenMode |= O_CLOEXEC;
+#endif
+#ifdef O_NONBLOCK
+ if (fOpen & RTFILE_O_NON_BLOCK)
+ fOpenMode |= O_NONBLOCK;
+#endif
+#ifdef O_SYNC
+ if (fOpen & RTFILE_O_WRITE_THROUGH)
+ fOpenMode |= O_SYNC;
+#endif
+#if defined(O_DIRECT) && defined(RT_OS_LINUX)
+ /* O_DIRECT is mandatory to get async I/O working on Linux. */
+ if (fOpen & RTFILE_O_ASYNC_IO)
+ fOpenMode |= O_DIRECT;
+#endif
+#if defined(O_DIRECT) && (defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD))
+ /* Disable the kernel cache. */
+ if (fOpen & RTFILE_O_NO_CACHE)
+ fOpenMode |= O_DIRECT;
+#endif
+
+ /* create/truncate file */
+ switch (fOpen & RTFILE_O_ACTION_MASK)
+ {
+ case RTFILE_O_OPEN: break;
+ case RTFILE_O_OPEN_CREATE: fOpenMode |= O_CREAT; break;
+ case RTFILE_O_CREATE: fOpenMode |= O_CREAT | O_EXCL; break;
+ case RTFILE_O_CREATE_REPLACE: fOpenMode |= O_CREAT | O_TRUNC; break; /** @todo replacing needs fixing, this is *not* a 1:1 mapping! */
+ default:
+ AssertMsgFailed(("fOpen=%#llx\n", fOpen));
+ fOpen = (fOpen & ~RTFILE_O_ACTION_MASK) | RTFILE_O_OPEN;
+ break;
+
+ }
+ if ( (fOpen & RTFILE_O_TRUNCATE)
+ && (fOpen & RTFILE_O_ACTION_MASK) != RTFILE_O_CREATE)
+ fOpenMode |= O_TRUNC;
+
+ switch (fOpen & RTFILE_O_ACCESS_MASK)
+ {
+ case RTFILE_O_READ:
+ fOpenMode |= O_RDONLY; /* RTFILE_O_APPEND is ignored. */
+ break;
+ case RTFILE_O_WRITE:
+ fOpenMode |= fOpen & RTFILE_O_APPEND ? O_APPEND | O_WRONLY : O_WRONLY;
+ break;
+ case RTFILE_O_READWRITE:
+ fOpenMode |= fOpen & RTFILE_O_APPEND ? O_APPEND | O_RDWR : O_RDWR;
+ break;
+ default:
+ AssertMsgFailedReturn(("RTFileOpen received an invalid RW value, fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
+ }
+
+ /* File mode. */
+ int fMode = (fOpen & RTFILE_O_CREATE_MODE_MASK)
+ ? (fOpen & RTFILE_O_CREATE_MODE_MASK) >> RTFILE_O_CREATE_MODE_SHIFT
+ : RT_FILE_PERMISSION;
+
+ /** @todo sharing? */
+
+ /*
+ * Open/create the file.
+ */
+ char const *pszNativeFilename;
+ rc = rtPathToNative(&pszNativeFilename, pszFilename, NULL);
+ if (RT_FAILURE(rc))
+ return (rc);
+
+ int fh;
+ int iErr;
+ if (!penmActionTaken)
+ {
+ fh = open(pszNativeFilename, fOpenMode, fMode);
+ iErr = errno;
+ }
+ else
+ {
+ /* We need to know exactly which action was taken by open, Windows &
+ OS/2 style. Can be tedious and subject to races: */
+ switch (fOpen & RTFILE_O_ACTION_MASK)
+ {
+ case RTFILE_O_OPEN:
+ Assert(!(fOpenMode & O_CREAT));
+ Assert(!(fOpenMode & O_EXCL));
+ fh = open(pszNativeFilename, fOpenMode, fMode);
+ iErr = errno;
+ if (fh >= 0)
+ *penmActionTaken = fOpenMode & O_TRUNC ? RTFILEACTION_TRUNCATED : RTFILEACTION_OPENED;
+ break;
+
+ case RTFILE_O_CREATE:
+ Assert(fOpenMode & O_CREAT);
+ Assert(fOpenMode & O_EXCL);
+ fh = open(pszNativeFilename, fOpenMode, fMode);
+ iErr = errno;
+ if (fh >= 0)
+ *penmActionTaken = RTFILEACTION_CREATED;
+ else if (iErr == EEXIST)
+ *penmActionTaken = RTFILEACTION_ALREADY_EXISTS;
+ break;
+
+ case RTFILE_O_OPEN_CREATE:
+ case RTFILE_O_CREATE_REPLACE:
+ {
+ Assert(fOpenMode & O_CREAT);
+ Assert(!(fOpenMode & O_EXCL));
+ int iTries = 64;
+ while (iTries-- > 0)
+ {
+ /* Yield the CPU if we've raced too long. */
+ if (iTries < 4)
+ RTThreadSleep(2 - (iTries & 1));
+
+ /* Try exclusive creation first: */
+ fh = open(pszNativeFilename, fOpenMode | O_EXCL, fMode);
+ iErr = errno;
+ if (fh >= 0)
+ {
+ *penmActionTaken = RTFILEACTION_CREATED;
+ break;
+ }
+ if (iErr != EEXIST)
+ break;
+
+ /* If the file exists, try open it: */
+ fh = open(pszNativeFilename, fOpenMode & ~O_CREAT, fMode);
+ iErr = errno;
+ if (fh >= 0)
+ {
+ if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
+ *penmActionTaken = fOpenMode & O_TRUNC ? RTFILEACTION_TRUNCATED : RTFILEACTION_OPENED;
+ else
+ *penmActionTaken = RTFILEACTION_REPLACED;
+ break;
+ }
+ if (iErr != ENOENT)
+ break;
+ }
+ Assert(iTries >= 0);
+ if (iTries < 0)
+ {
+ /* Thanks for the race, but we need to get on with things. */
+ fh = open(pszNativeFilename, fOpenMode, fMode);
+ iErr = errno;
+ if (fh >= 0)
+ *penmActionTaken = RTFILEACTION_OPENED;
+ }
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("fOpen=%#llx fOpenMode=%#x\n", fOpen, fOpenMode));
+ iErr = EINVAL;
+ fh = -1;
+ break;
+ }
+ }
+
+ rtPathFreeNative(pszNativeFilename, pszFilename);
+ if (fh >= 0)
+ {
+ iErr = 0;
+
+ /*
+ * If temporary file, delete it.
+ */
+ if (fOpen & RTFILE_O_TEMP_AUTO_DELETE)
+ {
+ /** @todo Use funlinkat/funlink or similar here when available! Or better,
+ * use O_TMPFILE, only that may require fallback as not supported by
+ * all file system on linux. */
+ iErr = unlink(pszNativeFilename);
+ Assert(iErr == 0);
+ }
+
+ /*
+ * Mark the file handle close on exec, unless inherit is specified.
+ */
+ if ( !(fOpen & RTFILE_O_INHERIT)
+#ifdef O_NOINHERIT
+ && !(fOpenMode & O_NOINHERIT) /* Take care since it might be a zero value dummy. */
+#endif
+#ifdef O_CLOEXEC
+ && fHave_O_CLOEXEC <= 0
+#endif
+ )
+ iErr = fcntl(fh, F_SETFD, FD_CLOEXEC) >= 0 ? 0 : errno;
+
+ /*
+ * Switch direct I/O on now if requested and required.
+ */
+#if defined(RT_OS_DARWIN) \
+ || (defined(RT_OS_SOLARIS) && !defined(IN_GUEST))
+ if (iErr == 0 && (fOpen & RTFILE_O_NO_CACHE))
+ {
+# if defined(RT_OS_DARWIN)
+ iErr = fcntl(fh, F_NOCACHE, 1) >= 0 ? 0 : errno;
+# else
+ iErr = directio(fh, DIRECTIO_ON) >= 0 ? 0 : errno;
+# endif
+ }
+#endif
+
+ /*
+ * Implement / emulate file sharing.
+ *
+ * We need another mode which allows skipping this stuff completely
+ * and do things the UNIX way. So for the present this is just a debug
+ * aid that can be enabled by developers too lazy to test on Windows.
+ */
+#if 0 && defined(RT_OS_LINUX)
+ if (iErr == 0)
+ {
+ /* This approach doesn't work because only knfsd checks for these
+ buggers. :-( */
+ int iLockOp;
+ switch (fOpen & RTFILE_O_DENY_MASK)
+ {
+ default:
+ AssertFailed();
+ case RTFILE_O_DENY_NONE:
+ case RTFILE_O_DENY_NOT_DELETE:
+ iLockOp = LOCK_MAND | LOCK_READ | LOCK_WRITE;
+ break;
+ case RTFILE_O_DENY_READ:
+ case RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
+ iLockOp = LOCK_MAND | LOCK_WRITE;
+ break;
+ case RTFILE_O_DENY_WRITE:
+ case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_NOT_DELETE:
+ iLockOp = LOCK_MAND | LOCK_READ;
+ break;
+ case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
+ case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
+ iLockOp = LOCK_MAND;
+ break;
+ }
+ iErr = flock(fh, iLockOp | LOCK_NB);
+ if (iErr != 0)
+ iErr = errno == EAGAIN ? ETXTBSY : 0;
+ }
+#endif /* 0 && RT_OS_LINUX */
+#if defined(DEBUG_bird) && !defined(RT_OS_SOLARIS)
+ if (iErr == 0)
+ {
+ /* This emulation is incomplete but useful. */
+ switch (fOpen & RTFILE_O_DENY_MASK)
+ {
+ default:
+ AssertFailed();
+ case RTFILE_O_DENY_NONE:
+ case RTFILE_O_DENY_NOT_DELETE:
+ case RTFILE_O_DENY_READ:
+ case RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
+ break;
+ case RTFILE_O_DENY_WRITE:
+ case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_NOT_DELETE:
+ case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
+ case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ | RTFILE_O_DENY_NOT_DELETE:
+ if (fOpen & RTFILE_O_WRITE)
+ {
+ iErr = flock(fh, LOCK_EX | LOCK_NB);
+ if (iErr != 0)
+ iErr = errno == EAGAIN ? ETXTBSY : 0;
+ }
+ break;
+ }
+ }
+#endif
+#ifdef RT_OS_SOLARIS
+ /** @todo Use fshare_t and associates, it's a perfect match. see sys/fcntl.h */
+#endif
+
+ /*
+ * We're done.
+ */
+ if (iErr == 0)
+ {
+ *phFile = (RTFILE)(uintptr_t)fh;
+ Assert((intptr_t)*phFile == fh);
+ LogFlow(("RTFileOpen(%p:{%RTfile}, %p:{%s}, %#llx): returns %Rrc\n",
+ phFile, *phFile, pszFilename, pszFilename, fOpen, rc));
+ return VINF_SUCCESS;
+ }
+
+ close(fh);
+ }
+ return RTErrConvertFromErrno(iErr);
+}
+
+
+RTR3DECL(int) RTFileOpenBitBucket(PRTFILE phFile, uint64_t fAccess)
+{
+ AssertReturn( fAccess == RTFILE_O_READ
+ || fAccess == RTFILE_O_WRITE
+ || fAccess == RTFILE_O_READWRITE,
+ VERR_INVALID_PARAMETER);
+ return RTFileOpen(phFile, "/dev/null", fAccess | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
+}
+
+
+RTR3DECL(int) RTFileClose(RTFILE hFile)
+{
+ if (hFile == NIL_RTFILE)
+ return VINF_SUCCESS;
+ if (close(RTFileToNative(hFile)) == 0)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTFileFromNative(PRTFILE pFile, RTHCINTPTR uNative)
+{
+ AssertCompile(sizeof(uNative) == sizeof(*pFile));
+ if (uNative < 0)
+ {
+ AssertMsgFailed(("%p\n", uNative));
+ *pFile = NIL_RTFILE;
+ return VERR_INVALID_HANDLE;
+ }
+ *pFile = (RTFILE)uNative;
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(RTHCINTPTR) RTFileToNative(RTFILE hFile)
+{
+ AssertReturn(hFile != NIL_RTFILE, -1);
+ return (intptr_t)hFile;
+}
+
+
+RTFILE rtFileGetStandard(RTHANDLESTD enmStdHandle)
+{
+ int fd;
+ switch (enmStdHandle)
+ {
+ case RTHANDLESTD_INPUT: fd = 0; break;
+ case RTHANDLESTD_OUTPUT: fd = 1; break;
+ case RTHANDLESTD_ERROR: fd = 2; break;
+ default:
+ AssertFailedReturn(NIL_RTFILE);
+ }
+
+ struct stat st;
+ int rc = fstat(fd, &st);
+ if (rc == -1)
+ return NIL_RTFILE;
+ return (RTFILE)(intptr_t)fd;
+}
+
+
+RTR3DECL(int) RTFileDelete(const char *pszFilename)
+{
+ char const *pszNativeFilename;
+ int rc = rtPathToNative(&pszNativeFilename, pszFilename, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (unlink(pszNativeFilename) != 0)
+ rc = RTErrConvertFromErrno(errno);
+ rtPathFreeNative(pszNativeFilename, pszFilename);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
+{
+ static const unsigned aSeekRecode[] =
+ {
+ SEEK_SET,
+ SEEK_CUR,
+ SEEK_END,
+ };
+
+ /*
+ * Validate input.
+ */
+ if (uMethod > RTFILE_SEEK_END)
+ {
+ AssertMsgFailed(("Invalid uMethod=%d\n", uMethod));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* check that within off_t range. */
+ if ( sizeof(off_t) < sizeof(offSeek)
+ && ( (offSeek > 0 && (unsigned)(offSeek >> 32) != 0)
+ || (offSeek < 0 && (unsigned)(-offSeek >> 32) != 0)))
+ {
+ AssertMsgFailed(("64-bit search not supported\n"));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ off_t offCurrent = lseek(RTFileToNative(hFile), (off_t)offSeek, aSeekRecode[uMethod]);
+ if (offCurrent != ~0)
+ {
+ if (poffActual)
+ *poffActual = (uint64_t)offCurrent;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ if (cbToRead <= 0)
+ {
+ if (pcbRead)
+ *pcbRead = 0;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Attempt read.
+ */
+ ssize_t cbRead = read(RTFileToNative(hFile), pvBuf, cbToRead);
+ if (cbRead >= 0)
+ {
+ if (pcbRead)
+ /* caller can handle partial read. */
+ *pcbRead = cbRead;
+ else
+ {
+ /* Caller expects all to be read. */
+ while ((ssize_t)cbToRead > cbRead)
+ {
+ ssize_t cbReadPart = read(RTFileToNative(hFile), (char*)pvBuf + cbRead, cbToRead - cbRead);
+ if (cbReadPart <= 0)
+ {
+ if (cbReadPart == 0)
+ return VERR_EOF;
+ return RTErrConvertFromErrno(errno);
+ }
+ cbRead += cbReadPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ if (cbToWrite <= 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Attempt write.
+ */
+ ssize_t cbWritten = write(RTFileToNative(hFile), pvBuf, cbToWrite);
+ if (cbWritten >= 0)
+ {
+ if (pcbWritten)
+ /* caller can handle partial write. */
+ *pcbWritten = cbWritten;
+ else
+ {
+ /* Caller expects all to be write. */
+ while ((ssize_t)cbToWrite > cbWritten)
+ {
+ ssize_t cbWrittenPart = write(RTFileToNative(hFile), (const char *)pvBuf + cbWritten, cbToWrite - cbWritten);
+ if (cbWrittenPart <= 0)
+ return cbWrittenPart < 0 ? RTErrConvertFromErrno(errno) : VERR_TRY_AGAIN;
+ cbWritten += cbWrittenPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTFileSetSize(RTFILE hFile, uint64_t cbSize)
+{
+ /*
+ * Validate offset.
+ */
+ if ( sizeof(off_t) < sizeof(cbSize)
+ && (cbSize >> 32) != 0)
+ {
+ AssertMsgFailed(("64-bit filesize not supported! cbSize=%lld\n", cbSize));
+ return VERR_NOT_SUPPORTED;
+ }
+
+#if defined(_MSC_VER) || (defined(RT_OS_OS2) && (!defined(__INNOTEK_LIBC__) || __INNOTEK_LIBC__ < 0x006))
+ if (chsize(RTFileToNative(hFile), (off_t)cbSize) == 0)
+#else
+ /* This relies on a non-standard feature of FreeBSD, Linux, and OS/2
+ * LIBC v0.6 and higher. (SuS doesn't define ftruncate() and size bigger
+ * than the file.)
+ */
+ if (ftruncate(RTFileToNative(hFile), (off_t)cbSize) == 0)
+#endif
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTFileQuerySize(RTFILE hFile, uint64_t *pcbSize)
+{
+ /*
+ * Ask fstat() first.
+ */
+ struct stat st;
+ if (!fstat(RTFileToNative(hFile), &st))
+ {
+ *pcbSize = st.st_size;
+ if ( st.st_size != 0
+#if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN)
+ || (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
+#elif defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_DARWIN)
+ || !S_ISCHR(st.st_mode)
+#else
+ || !S_ISBLK(st.st_mode)
+#endif
+ )
+ return VINF_SUCCESS;
+
+ /*
+ * It could be a block device. Try determin the size by I/O control
+ * query or seek.
+ */
+#ifdef RT_OS_DARWIN
+ uint64_t cBlocks;
+ if (!ioctl(RTFileToNative(hFile), DKIOCGETBLOCKCOUNT, &cBlocks))
+ {
+ uint32_t cbBlock;
+ if (!ioctl(RTFileToNative(hFile), DKIOCGETBLOCKSIZE, &cbBlock))
+ {
+ *pcbSize = cBlocks * cbBlock;
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* Always fail block devices. Character devices doesn't all need to be
+ /dev/rdisk* nodes, they should return ENOTTY but /dev/null returns ENODEV
+ and we include EINVAL just in case. */
+ if (!S_ISBLK(st.st_mode) && (errno == ENOTTY || errno == ENODEV || errno == EINVAL))
+ return VINF_SUCCESS;
+
+#elif defined(RT_OS_SOLARIS)
+ struct dk_minfo MediaInfo;
+ if (!ioctl(RTFileToNative(hFile), DKIOCGMEDIAINFO, &MediaInfo))
+ {
+ *pcbSize = MediaInfo.dki_capacity * MediaInfo.dki_lbsize;
+ return VINF_SUCCESS;
+ }
+ /* might not be a block device. */
+ if (errno == EINVAL || errno == ENOTTY)
+ return VINF_SUCCESS;
+
+#elif defined(RT_OS_FREEBSD)
+ off_t cbMedia = 0;
+ if (!ioctl(RTFileToNative(hFile), DIOCGMEDIASIZE, &cbMedia))
+ {
+ *pcbSize = cbMedia;
+ return VINF_SUCCESS;
+ }
+ /* might not be a block device. */
+ if (errno == EINVAL || errno == ENOTTY)
+ return VINF_SUCCESS;
+
+#else
+ /* PORTME! Avoid this path when possible. */
+ uint64_t offSaved = UINT64_MAX;
+ int rc = RTFileSeek(hFile, 0, RTFILE_SEEK_CURRENT, &offSaved);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, pcbSize);
+ int rc2 = RTFileSeek(hFile, offSaved, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ return rc2;
+ }
+#endif
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTFileQueryMaxSizeEx(RTFILE hFile, PRTFOFF pcbMax)
+{
+ /*
+ * Save the current location
+ */
+ uint64_t offOld = UINT64_MAX;
+ int rc = RTFileSeek(hFile, 0, RTFILE_SEEK_CURRENT, &offOld);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t offLow = 0;
+ uint64_t offHigh = INT64_MAX; /* we don't need bigger files */
+ /** @todo Unfortunately this does not work for certain file system types,
+ * for instance cifs mounts. Even worse, statvfs.f_fsid returns 0 for such
+ * file systems. */
+
+ /*
+ * Quickly guess the order of magnitude for offHigh and offLow.
+ */
+ {
+ uint64_t offHighPrev = offHigh;
+ while (offHigh >= INT32_MAX)
+ {
+ rc = RTFileSeek(hFile, offHigh, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ offLow = offHigh;
+ offHigh = offHighPrev;
+ break;
+ }
+ else
+ {
+ offHighPrev = offHigh;
+ offHigh >>= 8;
+ }
+ }
+ }
+
+ /*
+ * Sanity: if the seek to the initial offHigh (INT64_MAX) works, then
+ * this algorithm cannot possibly work. Declare defeat.
+ */
+ if (offLow == offHigh)
+ {
+ rc = RTFileSeek(hFile, offOld, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_SUCCESS(rc))
+ rc = VERR_NOT_IMPLEMENTED;
+
+ return rc;
+ }
+
+ /*
+ * Perform a binary search for the max file size.
+ */
+ while (offLow <= offHigh)
+ {
+ uint64_t offMid = offLow + (offHigh - offLow) / 2;
+ rc = RTFileSeek(hFile, offMid, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_FAILURE(rc))
+ offHigh = offMid - 1;
+ else
+ offLow = offMid + 1;
+ }
+
+ if (pcbMax)
+ *pcbMax = RT_MIN(offLow, offHigh);
+ return RTFileSeek(hFile, offOld, RTFILE_SEEK_BEGIN, NULL);
+}
+
+
+RTR3DECL(bool) RTFileIsValid(RTFILE hFile)
+{
+ if (hFile != NIL_RTFILE)
+ {
+ int fFlags = fcntl(RTFileToNative(hFile), F_GETFD);
+ if (fFlags >= 0)
+ return true;
+ }
+ return false;
+}
+
+
+RTR3DECL(int) RTFileFlush(RTFILE hFile)
+{
+ if (!fsync(RTFileToNative(hFile)))
+ return VINF_SUCCESS;
+ /* Ignore EINVAL here as that's what returned for pseudo ttys
+ and other odd handles. */
+ if (errno == EINVAL)
+ return VINF_NOT_SUPPORTED;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTFileIoCtl(RTFILE hFile, unsigned long ulRequest, void *pvData, unsigned cbData, int *piRet)
+{
+ NOREF(cbData);
+ int rc = ioctl(RTFileToNative(hFile), ulRequest, pvData);
+ if (piRet)
+ *piRet = rc;
+ return rc >= 0 ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTFileSetMode(RTFILE hFile, RTFMODE fMode)
+{
+ /*
+ * Normalize the mode and call the API.
+ */
+ fMode = rtFsModeNormalize(fMode, NULL, 0, RTFS_TYPE_FILE);
+ if (!rtFsModeIsValid(fMode))
+ return VERR_INVALID_PARAMETER;
+
+ if (fchmod(RTFileToNative(hFile), fMode & RTFS_UNIX_MASK))
+ {
+ int rc = RTErrConvertFromErrno(errno);
+ Log(("RTFileSetMode(%RTfile,%RTfmode): returns %Rrc\n", hFile, fMode, rc));
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTFileSetOwner(RTFILE hFile, uint32_t uid, uint32_t gid)
+{
+ uid_t uidNative = uid != NIL_RTUID ? (uid_t)uid : (uid_t)-1;
+ AssertReturn(uid == uidNative, VERR_INVALID_PARAMETER);
+ gid_t gidNative = gid != NIL_RTGID ? (gid_t)gid : (gid_t)-1;
+ AssertReturn(gid == gidNative, VERR_INVALID_PARAMETER);
+
+ if (fchown(RTFileToNative(hFile), uidNative, gidNative))
+ return RTErrConvertFromErrno(errno);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTFileRename(const char *pszSrc, const char *pszDst, unsigned fRename)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Take common cause with RTPathRename.
+ */
+ int rc = rtPathPosixRename(pszSrc, pszDst, fRename, RTFS_TYPE_FILE);
+
+ LogFlow(("RTDirRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
+ pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/fileio-sg-at-posix.cpp b/src/VBox/Runtime/r3/posix/fileio-sg-at-posix.cpp
new file mode 100644
index 00000000..5fdea41a
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/fileio-sg-at-posix.cpp
@@ -0,0 +1,298 @@
+/* $Id: fileio-sg-at-posix.cpp $ */
+/** @file
+ * IPRT - File I/O, RTFileSgReadAt & RTFileSgWriteAt, posixy.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+/*
+ * Determin whether we've got preadv and pwritev.
+ */
+#include <iprt/cdefs.h>
+#ifdef RT_OS_LINUX
+/* Linux has these since glibc 2.10 and Linux 2.6.30: */
+# include <features.h>
+# ifdef __GLIBC_PREREQ
+# if __GLIBC_PREREQ(2,10)
+# define HAVE_PREADV_AND_PWRITEV 1
+#else
+# endif
+# endif
+
+#elif defined(RT_OS_FREEBSD)
+/* FreeBSD has these since 6.0: */
+# include <osreldate.h>
+# ifdef __FreeBSD_version
+# if __FreeBSD_version >= 600000
+# define HAVE_PREADV_AND_PWRITEV 1
+# endif
+# endif
+
+#endif
+
+#ifndef HAVE_PREADV_AND_PWRITEV
+
+# include "../../generic/fileio-sg-at-generic.cpp"
+
+#else /* HAVE_PREADV_AND_PWRITEV - rest of the file */
+
+# include <errno.h>
+# include <sys/types.h>
+# include <sys/uio.h>
+# include <unistd.h>
+# include <limits.h>
+# if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_OPENBSD)
+# include <sys/syslimits.h>
+# endif
+
+# include "internal/iprt.h"
+# include <iprt/file.h>
+
+# include <iprt/assert.h>
+# include <iprt/err.h>
+# include <iprt/log.h>
+
+# ifndef UIO_MAXIOV
+# ifdef IOV_MAX
+# define UIO_MAXIOV IOV_MAX
+# else
+# error "UIO_MAXIOV and IOV_MAX are undefined"
+# endif
+# endif
+
+
+/* These assumptions simplifies things a lot here. */
+AssertCompileMembersSameSizeAndOffset(struct iovec, iov_base, RTSGSEG, pvSeg);
+AssertCompileMembersSameSizeAndOffset(struct iovec, iov_len, RTSGSEG, cbSeg);
+
+
+RTDECL(int) RTFileSgReadAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToRead, size_t *pcbRead)
+{
+ /*
+ * Make sure we set pcbRead.
+ */
+ if (pcbRead)
+ *pcbRead = 0;
+
+ /*
+ * Special case: Zero read == seek.
+ */
+ if (cbToRead == 0)
+ return RTFileSeek(hFile, off, RTFILE_SEEK_BEGIN, NULL);
+
+ /*
+ * We can use the segment array directly if we're at the start of the
+ * current S/G segment and cbToRead matches the remainder exactly.
+ */
+ size_t cbTotalRead = 0;
+
+ size_t const cbSgBufLeft = RTSgBufCalcLengthLeft(pSgBuf);
+ AssertMsgReturn(cbSgBufLeft >= cbToRead, ("%#zx vs %#zx\n", cbSgBufLeft, cbToRead), VERR_INVALID_PARAMETER);
+
+ if (cbToRead == cbSgBufLeft)
+ while (RTSgBufIsAtStartOfSegment(pSgBuf))
+ {
+ size_t const cSegsLeft = pSgBuf->cSegs - pSgBuf->idxSeg;
+ ssize_t cbThisRead = preadv(RTFileToNative(hFile), (const struct iovec *)&pSgBuf->paSegs[pSgBuf->idxSeg],
+ RT_MIN(cSegsLeft, UIO_MAXIOV), off);
+ if (cbThisRead >= 0)
+ {
+ AssertStmt((size_t)cbThisRead <= cbToRead, cbThisRead = cbToRead);
+
+ RTSgBufAdvance(pSgBuf, cbThisRead);
+ cbTotalRead += cbThisRead;
+ cbToRead -= cbThisRead;
+ if (cbToRead == 0)
+ {
+ if (pcbRead)
+ *pcbRead = cbTotalRead;
+ return VINF_SUCCESS;
+ }
+
+ if ( pcbRead
+ && ( cSegsLeft <= UIO_MAXIOV
+ || cbThisRead == 0 /* typically EOF */ ))
+ {
+ *pcbRead = cbTotalRead;
+ return VINF_SUCCESS;
+ }
+ if (cbThisRead == 0)
+ return VERR_EOF;
+
+ off += cbThisRead;
+ }
+ else if (cbTotalRead > 0 && pcbRead)
+ {
+ *pcbRead = cbTotalRead;
+ return VINF_SUCCESS;
+ }
+ else
+ return RTErrConvertFromErrno(errno);
+ }
+
+ /*
+ * Unaligned start or not reading the whole buffer. For reasons of
+ * simplicity, we work the input segment by segment like the generic code.
+ */
+ int rc = VINF_SUCCESS;
+ while (cbToRead > 0)
+ {
+ size_t cbSeg;
+ void *pvSeg = RTSgBufGetCurrentSegment(pSgBuf, cbToRead, &cbSeg);
+ size_t cbThisRead = cbSeg;
+ rc = RTFileReadAt(hFile, off, pvSeg, cbSeg, pcbRead ? &cbThisRead : NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTSgBufAdvance(pSgBuf, cbThisRead);
+ cbTotalRead += cbThisRead;
+ }
+ else
+ break;
+ if ((size_t)cbThisRead < cbSeg)
+ {
+ AssertStmt(pcbRead, rc = VERR_INTERNAL_ERROR_2);
+ break;
+ }
+
+ Assert(cbSeg == cbThisRead);
+ cbToRead -= cbSeg;
+ off += cbSeg;
+ }
+ if (pcbRead)
+ *pcbRead = cbTotalRead;
+ return rc;
+}
+
+
+RTDECL(int) RTFileSgWriteAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ /*
+ * Make sure we set pcbWritten.
+ */
+ if (pcbWritten)
+ *pcbWritten = 0;
+
+ /*
+ * Special case: Zero write == seek.
+ */
+ if (cbToWrite == 0)
+ return RTFileSeek(hFile, off, RTFILE_SEEK_BEGIN, NULL);
+
+ /*
+ * We can use the segment array directly if we're at the start of the
+ * current S/G segment and cbToWrite matches the remainder exactly.
+ */
+ size_t cbTotalWritten = 0;
+
+ size_t const cbSgBufLeft = RTSgBufCalcLengthLeft(pSgBuf);
+ AssertMsgReturn(cbSgBufLeft >= cbToWrite, ("%#zx vs %#zx\n", cbSgBufLeft, cbToWrite), VERR_INVALID_PARAMETER);
+
+ if (cbToWrite == cbSgBufLeft)
+ while (RTSgBufIsAtStartOfSegment(pSgBuf))
+ {
+ size_t const cSegsLeft = pSgBuf->cSegs - pSgBuf->idxSeg;
+ ssize_t cbThisWritten = pwritev(RTFileToNative(hFile), (const struct iovec *)&pSgBuf->paSegs[pSgBuf->idxSeg],
+ RT_MIN(cSegsLeft, UIO_MAXIOV), off);
+ if (cbThisWritten >= 0)
+ {
+ AssertStmt((size_t)cbThisWritten <= cbToWrite, cbThisWritten = cbToWrite);
+
+ RTSgBufAdvance(pSgBuf, cbThisWritten);
+ cbTotalWritten += cbThisWritten;
+ cbToWrite -= cbThisWritten;
+ if (cbToWrite == 0)
+ {
+ if (pcbWritten)
+ *pcbWritten = cbTotalWritten;
+ return VINF_SUCCESS;
+ }
+
+ if ( pcbWritten
+ && ( cSegsLeft <= UIO_MAXIOV
+ || cbThisWritten == 0 /* non-file, full buffer/whatever */ ))
+ {
+ *pcbWritten = cbTotalWritten;
+ return VINF_SUCCESS;
+ }
+ if (cbThisWritten == 0)
+ return VERR_TRY_AGAIN;
+
+ off += cbThisWritten;
+ }
+ else if (cbTotalWritten > 0 && pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ return VINF_SUCCESS;
+ }
+ else
+ return RTErrConvertFromErrno(errno);
+ }
+
+ /*
+ * Unaligned start or not writing the whole buffer. For reasons of
+ * simplicity, we work the input segment by segment like the generic code.
+ */
+ int rc = VINF_SUCCESS;
+ while (cbToWrite > 0)
+ {
+ size_t cbSeg;
+ void *pvSeg = RTSgBufGetCurrentSegment(pSgBuf, cbToWrite, &cbSeg);
+ size_t cbThisWritten = cbSeg;
+ rc = RTFileWriteAt(hFile, off, pvSeg, cbSeg, pcbWritten ? &cbThisWritten : NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTSgBufAdvance(pSgBuf, cbThisWritten);
+ cbTotalWritten += cbThisWritten;
+ }
+ else
+ break;
+ if ((size_t)cbThisWritten < cbSeg)
+ {
+ AssertStmt(pcbWritten, rc = VERR_INTERNAL_ERROR_2);
+ break;
+ }
+
+ Assert(cbSeg == cbThisWritten);
+ cbToWrite -= cbSeg;
+ off += cbSeg;
+ }
+ if (pcbWritten)
+ *pcbWritten = cbTotalWritten;
+ return rc;
+}
+
+#endif /* HAVE_PREADV_AND_PWRITEV */
+
diff --git a/src/VBox/Runtime/r3/posix/fileio-sg-posix.cpp b/src/VBox/Runtime/r3/posix/fileio-sg-posix.cpp
new file mode 100644
index 00000000..c8c95786
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/fileio-sg-posix.cpp
@@ -0,0 +1,260 @@
+/* $Id: fileio-sg-posix.cpp $ */
+/** @file
+ * IPRT - File I/O, RTFileSgRead & RTFileSgWrite, posixy.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <limits.h>
+#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_OPENBSD)
+# include <sys/syslimits.h>
+#endif
+
+#include "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+
+#ifndef UIO_MAXIOV
+# ifdef IOV_MAX
+# define UIO_MAXIOV IOV_MAX
+# else
+# error "UIO_MAXIOV and IOV_MAX are undefined"
+# endif
+#endif
+
+
+/* These assumptions simplifies things a lot here. */
+AssertCompileMembersSameSizeAndOffset(struct iovec, iov_base, RTSGSEG, pvSeg);
+AssertCompileMembersSameSizeAndOffset(struct iovec, iov_len, RTSGSEG, cbSeg);
+
+
+RTDECL(int) RTFileSgRead(RTFILE hFile, PRTSGBUF pSgBuf, size_t cbToRead, size_t *pcbRead)
+{
+ /*
+ * Make sure we set pcbRead.
+ */
+ if (pcbRead)
+ *pcbRead = 0;
+
+ /*
+ * Special case: Zero read == nop.
+ */
+ if (cbToRead == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * We can use the segment array directly if we're at the start of the
+ * current S/G segment and cbToRead matches the remainder exactly.
+ */
+ size_t cbTotalRead = 0;
+
+ size_t const cbSgBufLeft = RTSgBufCalcLengthLeft(pSgBuf);
+ AssertMsgReturn(cbSgBufLeft >= cbToRead, ("%#zx vs %#zx\n", cbSgBufLeft, cbToRead), VERR_INVALID_PARAMETER);
+
+ if (cbToRead == cbSgBufLeft)
+ while (RTSgBufIsAtStartOfSegment(pSgBuf))
+ {
+ size_t const cSegsLeft = pSgBuf->cSegs - pSgBuf->idxSeg;
+ ssize_t cbThisRead = readv(RTFileToNative(hFile), (const struct iovec *)&pSgBuf->paSegs[pSgBuf->idxSeg],
+ RT_MIN(cSegsLeft, UIO_MAXIOV));
+ if (cbThisRead >= 0)
+ {
+ AssertStmt((size_t)cbThisRead <= cbToRead, cbThisRead = cbToRead);
+
+ RTSgBufAdvance(pSgBuf, cbThisRead);
+ cbTotalRead += cbThisRead;
+ cbToRead -= cbThisRead;
+ if (cbToRead == 0)
+ {
+ if (pcbRead)
+ *pcbRead = cbTotalRead;
+ return VINF_SUCCESS;
+ }
+
+ if ( pcbRead
+ && ( cSegsLeft <= UIO_MAXIOV
+ || cbThisRead == 0 /* typically EOF */ ))
+ {
+ *pcbRead = cbTotalRead;
+ return VINF_SUCCESS;
+ }
+ if (cbThisRead == 0)
+ return VERR_EOF;
+ }
+ else if (cbTotalRead > 0 && pcbRead)
+ {
+ *pcbRead = cbTotalRead;
+ return VINF_SUCCESS;
+ }
+ else
+ return RTErrConvertFromErrno(errno);
+ }
+
+ /*
+ * Unaligned start or not reading the whole buffer. For reasons of
+ * simplicity, we work the input segment by segment like the generic code.
+ */
+ int rc = VINF_SUCCESS;
+ while (cbToRead > 0)
+ {
+ size_t cbSeg;
+ void *pvSeg = RTSgBufGetCurrentSegment(pSgBuf, cbToRead, &cbSeg);
+ size_t cbThisRead = cbSeg;
+ rc = RTFileRead(hFile, pvSeg, cbSeg, pcbRead ? &cbThisRead : NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTSgBufAdvance(pSgBuf, cbThisRead);
+ cbTotalRead += cbThisRead;
+ }
+ else
+ break;
+ if ((size_t)cbThisRead < cbSeg)
+ {
+ AssertStmt(pcbRead, rc = VERR_INTERNAL_ERROR_2);
+ break;
+ }
+
+ Assert(cbSeg == cbThisRead);
+ cbToRead -= cbSeg;
+ }
+ if (pcbRead)
+ *pcbRead = cbTotalRead;
+ return rc;
+}
+
+
+RTDECL(int) RTFileSgWrite(RTFILE hFile, PRTSGBUF pSgBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ /*
+ * Make sure we set pcbWritten.
+ */
+ if (pcbWritten)
+ *pcbWritten = 0;
+
+ /*
+ * Special case: Zero write == nop.
+ */
+ if (cbToWrite == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * We can use the segment array directly if we're at the start of the
+ * current S/G segment and cbToWrite matches the remainder exactly.
+ */
+ size_t cbTotalWritten = 0;
+
+ size_t const cbSgBufLeft = RTSgBufCalcLengthLeft(pSgBuf);
+ AssertMsgReturn(cbSgBufLeft >= cbToWrite, ("%#zx vs %#zx\n", cbSgBufLeft, cbToWrite), VERR_INVALID_PARAMETER);
+
+ if (cbToWrite == cbSgBufLeft)
+ while (RTSgBufIsAtStartOfSegment(pSgBuf))
+ {
+ size_t const cSegsLeft = pSgBuf->cSegs - pSgBuf->idxSeg;
+ ssize_t cbThisWritten = writev(RTFileToNative(hFile), (const struct iovec *)&pSgBuf->paSegs[pSgBuf->idxSeg],
+ RT_MIN(cSegsLeft, UIO_MAXIOV));
+ if (cbThisWritten >= 0)
+ {
+ AssertStmt((size_t)cbThisWritten <= cbToWrite, cbThisWritten = cbToWrite);
+
+ RTSgBufAdvance(pSgBuf, cbThisWritten);
+ cbTotalWritten += cbThisWritten;
+ cbToWrite -= cbThisWritten;
+ if (cbToWrite == 0)
+ {
+ if (pcbWritten)
+ *pcbWritten = cbTotalWritten;
+ return VINF_SUCCESS;
+ }
+
+ if ( pcbWritten
+ && ( cSegsLeft <= UIO_MAXIOV
+ || cbThisWritten == 0 /* non-file, full buffer/whatever */ ))
+ {
+ *pcbWritten = cbTotalWritten;
+ return VINF_SUCCESS;
+ }
+ if (cbThisWritten == 0)
+ return VERR_TRY_AGAIN;
+ }
+ else if (cbTotalWritten > 0 && pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ return VINF_SUCCESS;
+ }
+ else
+ return RTErrConvertFromErrno(errno);
+ }
+
+ /*
+ * Unaligned start or not writing the whole buffer. For reasons of
+ * simplicity, we work the input segment by segment like the generic code.
+ */
+ int rc = VINF_SUCCESS;
+ while (cbToWrite > 0)
+ {
+ size_t cbSeg;
+ void *pvSeg = RTSgBufGetCurrentSegment(pSgBuf, cbToWrite, &cbSeg);
+ size_t cbThisWritten = cbSeg;
+ rc = RTFileWrite(hFile, pvSeg, cbSeg, pcbWritten ? &cbThisWritten : NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTSgBufAdvance(pSgBuf, cbThisWritten);
+ cbTotalWritten += cbThisWritten;
+ }
+ else
+ break;
+ if ((size_t)cbThisWritten < cbSeg)
+ {
+ AssertStmt(pcbWritten, rc = VERR_INTERNAL_ERROR_2);
+ break;
+ }
+
+ Assert(cbSeg == cbThisWritten);
+ cbToWrite -= cbSeg;
+ }
+ if (pcbWritten)
+ *pcbWritten = cbTotalWritten;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/fileio2-posix.cpp b/src/VBox/Runtime/r3/posix/fileio2-posix.cpp
new file mode 100644
index 00000000..4879f51e
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/fileio2-posix.cpp
@@ -0,0 +1,210 @@
+/* $Id: fileio2-posix.cpp $ */
+/** @file
+ * IPRT - File I/O, POSIX, Part 2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifdef _MSC_VER
+# include <io.h>
+# include <stdio.h>
+#else
+# include <unistd.h>
+# include <sys/time.h>
+#endif
+#ifdef RT_OS_LINUX
+# include <sys/file.h>
+#endif
+#if defined(RT_OS_OS2) && (!defined(__INNOTEK_LIBC__) || __INNOTEK_LIBC__ < 0x006)
+# include <io.h>
+#endif
+
+#ifdef RT_OS_SOLARIS
+# define futimes(filedes, timeval) futimesat(filedes, NULL, timeval)
+#endif
+
+#ifdef RT_OS_HAIKU
+# define USE_FUTIMENS
+#endif
+
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include "internal/file.h"
+#include "internal/fs.h"
+#include "internal/path.h"
+
+
+
+RTR3DECL(int) RTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pObjInfo, VERR_INVALID_PARAMETER);
+ if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING
+ || enmAdditionalAttribs > RTFSOBJATTRADD_LAST)
+ {
+ AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Query file info.
+ */
+ struct stat Stat;
+ if (fstat(RTFileToNative(hFile), &Stat))
+ {
+ int rc = RTErrConvertFromErrno(errno);
+ Log(("RTFileQueryInfo(%RTfile,,%d): returns %Rrc\n", hFile, enmAdditionalAttribs, rc));
+ return rc;
+ }
+
+ /*
+ * Setup the returned data.
+ */
+ rtFsConvertStatToObjInfo(pObjInfo, &Stat, NULL, 0);
+
+ /*
+ * Requested attributes (we cannot provide anything actually).
+ */
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ case RTFSOBJATTRADD_UNIX:
+ /* done */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ rtFsObjInfoAttrSetUnixOwner(pObjInfo, Stat.st_uid);
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ rtFsObjInfoAttrSetUnixGroup(pObjInfo, Stat.st_gid);
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ LogFlow(("RTFileQueryInfo(%RTfile,,%d): returns VINF_SUCCESS\n", hFile, enmAdditionalAttribs));
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTFileSetTimes(RTFILE hFile, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ NOREF(pChangeTime); NOREF(pBirthTime);
+
+ /*
+ * We can only set AccessTime and ModificationTime, so if neither
+ * are specified we can return immediately.
+ */
+ if (!pAccessTime && !pModificationTime)
+ return VINF_SUCCESS;
+
+#ifdef USE_FUTIMENS
+ struct timespec aTimespecs[2];
+ if (pAccessTime && pModificationTime)
+ {
+ memcpy(&aTimespecs[0], pAccessTime, sizeof(struct timespec));
+ memcpy(&aTimespecs[1], pModificationTime, sizeof(struct timespec));
+ }
+ else
+ {
+ RTFSOBJINFO ObjInfo;
+ int rc = RTFileQueryInfo(hFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
+ if (RT_FAILURE(rc))
+ return rc;
+ memcpy(&aTimespecs[0], pAccessTime ? pAccessTime : &ObjInfo.AccessTime, sizeof(struct timespec));
+ memcpy(&aTimespecs[1], pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, sizeof(struct timespec));
+ }
+
+ if (futimens(RTFileToNative(hFile), aTimespecs))
+ {
+ int rc = RTErrConvertFromErrno(errno);
+ Log(("RTFileSetTimes(%RTfile,%p,%p,,): returns %Rrc\n", hFile, pAccessTime, pModificationTime, rc));
+ return rc;
+ }
+#else
+ /*
+ * Convert the input to timeval, getting the missing one if necessary,
+ * and call the API which does the change.
+ */
+ struct timeval aTimevals[2];
+ if (pAccessTime && pModificationTime)
+ {
+ RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]);
+ RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
+ }
+ else
+ {
+ RTFSOBJINFO ObjInfo;
+ int rc = RTFileQueryInfo(hFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
+ if (RT_FAILURE(rc))
+ return rc;
+ RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
+ RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
+ }
+
+ /* XXX this falls back to utimes("/proc/self/fd/...",...) for older kernels/glibcs and this
+ * will not work for hardened builds where this directory is owned by root.root and mode 0500 */
+ if (futimes(RTFileToNative(hFile), aTimevals))
+ {
+ int rc = RTErrConvertFromErrno(errno);
+ Log(("RTFileSetTimes(%RTfile,%p,%p,,): returns %Rrc\n", hFile, pAccessTime, pModificationTime, rc));
+ return rc;
+ }
+#endif
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/filelock-posix.cpp b/src/VBox/Runtime/r3/posix/filelock-posix.cpp
new file mode 100644
index 00000000..3a0e230f
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/filelock-posix.cpp
@@ -0,0 +1,148 @@
+/* $Id: filelock-posix.cpp $ */
+/** @file
+ * IPRT - File Locking, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <iprt/file.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/file.h"
+#include "internal/fs.h"
+
+
+
+
+RTR3DECL(int) RTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /* Check arguments. */
+ if (fLock & ~RTFILE_LOCK_MASK)
+ {
+ AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Validate offset.
+ */
+ if ( sizeof(off_t) < sizeof(cbLock)
+ && ( (offLock >> 32) != 0
+ || (cbLock >> 32) != 0
+ || ((offLock + cbLock) >> 32) != 0))
+ {
+ AssertMsgFailed(("64-bit file i/o not supported! offLock=%lld cbLock=%lld\n", offLock, cbLock));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /* Prepare flock structure. */
+ struct flock fl;
+ Assert(RTFILE_LOCK_WRITE);
+ fl.l_type = (fLock & RTFILE_LOCK_WRITE) ? F_WRLCK : F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = (off_t)offLock;
+ fl.l_len = (off_t)cbLock;
+ fl.l_pid = 0;
+
+ Assert(RTFILE_LOCK_WAIT);
+ if (fcntl(RTFileToNative(hFile), (fLock & RTFILE_LOCK_WAIT) ? F_SETLKW : F_SETLK, &fl) >= 0)
+ return VINF_SUCCESS;
+
+ int iErr = errno;
+ if ( iErr == EAGAIN
+ || iErr == EACCES)
+ return VERR_FILE_LOCK_VIOLATION;
+
+ return RTErrConvertFromErrno(iErr);
+}
+
+
+RTR3DECL(int) RTFileChangeLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ /** @todo We never returns VERR_FILE_NOT_LOCKED for now. */
+ return RTFileLock(hFile, fLock, offLock, cbLock);
+}
+
+
+RTR3DECL(int) RTFileUnlock(RTFILE hFile, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /*
+ * Validate offset.
+ */
+ if ( sizeof(off_t) < sizeof(cbLock)
+ && ( (offLock >> 32) != 0
+ || (cbLock >> 32) != 0
+ || ((offLock + cbLock) >> 32) != 0))
+ {
+ AssertMsgFailed(("64-bit file i/o not supported! offLock=%lld cbLock=%lld\n", offLock, cbLock));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /* Prepare flock structure. */
+ struct flock fl;
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = (off_t)offLock;
+ fl.l_len = (off_t)cbLock;
+ fl.l_pid = 0;
+
+ if (fcntl(RTFileToNative(hFile), F_SETLK, &fl) >= 0)
+ return VINF_SUCCESS;
+
+ /** @todo check error codes for non existing lock. */
+ int iErr = errno;
+ if ( iErr == EAGAIN
+ || iErr == EACCES)
+ return VERR_FILE_LOCK_VIOLATION;
+
+ return RTErrConvertFromErrno(iErr);
+}
+
diff --git a/src/VBox/Runtime/r3/posix/fs-posix.cpp b/src/VBox/Runtime/r3/posix/fs-posix.cpp
new file mode 100644
index 00000000..04b3289a
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/fs-posix.cpp
@@ -0,0 +1,346 @@
+/* $Id: fs-posix.cpp $ */
+/** @file
+ * IPRT - File System, Linux.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include <sys/statvfs.h>
+#include <errno.h>
+#include <stdio.h>
+#ifdef RT_OS_LINUX
+# include <mntent.h>
+#endif
+#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+# include <sys/mount.h>
+#endif
+
+#include <iprt/fs.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include "internal/fs.h"
+#include "internal/path.h"
+
+
+
+RTR3DECL(int) RTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree,
+ uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the path and query the information.
+ */
+ char const *pszNativeFsPath;
+ int rc = rtPathToNative(&pszNativeFsPath, pszFsPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo I'm not quite sure if statvfs was properly specified by SuS, I have to check my own
+ * implementation and FreeBSD before this can eventually be promoted to posix. */
+ struct statvfs StatVFS;
+ RT_ZERO(StatVFS);
+ if (!statvfs(pszNativeFsPath, &StatVFS))
+ {
+ /*
+ * Calc the returned values.
+ */
+ if (pcbTotal)
+ *pcbTotal = (RTFOFF)StatVFS.f_blocks * StatVFS.f_frsize;
+ if (pcbFree)
+ *pcbFree = (RTFOFF)StatVFS.f_bavail * StatVFS.f_frsize;
+ if (pcbBlock)
+ *pcbBlock = StatVFS.f_frsize;
+ /* no idea how to get the sector... */
+ if (pcbSector)
+ *pcbSector = 512;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ rtPathFreeNative(pszNativeFsPath, pszFsPath);
+ }
+
+ LogFlow(("RTFsQuerySizes(%p:{%s}, %p:{%RTfoff}, %p:{%RTfoff}, %p:{%RX32}, %p:{%RX32}): returns %Rrc\n",
+ pszFsPath, pszFsPath, pcbTotal, pcbTotal ? *pcbTotal : 0, pcbFree, pcbFree ? *pcbFree : 0,
+ pcbBlock, pcbBlock ? *pcbBlock : 0, pcbSector, pcbSector ? *pcbSector : 0, rc));
+ return rc;
+}
+
+
+RTR3DECL(int) RTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pu32Serial, VERR_INVALID_POINTER);
+
+ /*
+ * Convert the path and query the stats.
+ * We're simply return the device id.
+ */
+ char const *pszNativeFsPath;
+ int rc = rtPathToNative(&pszNativeFsPath, pszFsPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat Stat;
+ if (!stat(pszNativeFsPath, &Stat))
+ {
+ if (pu32Serial)
+ *pu32Serial = (uint32_t)Stat.st_dev;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ rtPathFreeNative(pszNativeFsPath, pszFsPath);
+ }
+ LogFlow(("RTFsQuerySerial(%p:{%s}, %p:{%RX32}: returns %Rrc\n",
+ pszFsPath, pszFsPath, pu32Serial, pu32Serial ? *pu32Serial : 0, rc));
+ return rc;
+}
+
+
+RTR3DECL(int) RTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pProperties, VERR_INVALID_POINTER);
+
+ /*
+ * Convert the path and query the information.
+ */
+ char const *pszNativeFsPath;
+ int rc = rtPathToNative(&pszNativeFsPath, pszFsPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct statvfs StatVFS;
+ RT_ZERO(StatVFS);
+ if (!statvfs(pszNativeFsPath, &StatVFS))
+ {
+ /*
+ * Calc/fake the returned values.
+ */
+ pProperties->cbMaxComponent = StatVFS.f_namemax;
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ pProperties->fCaseSensitive = false;
+#else
+ pProperties->fCaseSensitive = true;
+#endif
+ pProperties->fCompressed = false;
+ pProperties->fFileCompression = false;
+ pProperties->fReadOnly = !!(StatVFS.f_flag & ST_RDONLY);
+ pProperties->fRemote = false;
+ pProperties->fSupportsUnicode = true;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ rtPathFreeNative(pszNativeFsPath, pszFsPath);
+ }
+
+ LogFlow(("RTFsQueryProperties(%p:{%s}, %p:{.cbMaxComponent=%u, .fReadOnly=%RTbool}): returns %Rrc\n",
+ pszFsPath, pszFsPath, pProperties, pProperties->cbMaxComponent, pProperties->fReadOnly, rc));
+ return rc;
+}
+
+
+RTR3DECL(bool) RTFsIsCaseSensitive(const char *pszFsPath)
+{
+ RT_NOREF_PV(pszFsPath);
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ return false;
+#else
+ return true;
+#endif
+}
+
+
+RTR3DECL(int) RTFsQueryType(const char *pszFsPath, PRTFSTYPE penmType)
+{
+ *penmType = RTFSTYPE_UNKNOWN;
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath, VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the path and query the stats.
+ * We're simply return the device id.
+ */
+ char const *pszNativeFsPath;
+ int rc = rtPathToNative(&pszNativeFsPath, pszFsPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat Stat;
+ if (!stat(pszNativeFsPath, &Stat))
+ {
+#if defined(RT_OS_LINUX)
+ FILE *mounted = setmntent("/proc/mounts", "r");
+ if (!mounted)
+ mounted = setmntent("/etc/mtab", "r");
+ if (mounted)
+ {
+ char szBuf[1024];
+ struct stat mntStat;
+ struct mntent mntEnt;
+ while (getmntent_r(mounted, &mntEnt, szBuf, sizeof(szBuf)))
+ {
+ if (!stat(mntEnt.mnt_dir, &mntStat))
+ {
+ if (mntStat.st_dev == Stat.st_dev)
+ {
+ if (!strcmp("ext4", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_EXT4;
+ else if (!strcmp("ext3", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_EXT3;
+ else if (!strcmp("ext2", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_EXT2;
+ else if (!strcmp("jfs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_JFS;
+ else if (!strcmp("xfs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_XFS;
+ else if (!strcmp("btrfs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_BTRFS;
+ else if ( !strcmp("vfat", mntEnt.mnt_type)
+ || !strcmp("msdos", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_FAT;
+ else if (!strcmp("ntfs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_NTFS;
+ else if (!strcmp("hpfs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_HPFS;
+ else if (!strcmp("ufs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_UFS;
+ else if (!strcmp("tmpfs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_TMPFS;
+ else if (!strcmp("hfsplus", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_HFS;
+ else if (!strcmp("udf", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_UDF;
+ else if (!strcmp("iso9660", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_ISO9660;
+ else if (!strcmp("smbfs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_SMBFS;
+ else if (!strcmp("cifs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_CIFS;
+ else if (!strcmp("nfs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_NFS;
+ else if (!strcmp("nfs4", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_NFS;
+ else if (!strcmp("ocfs2", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_OCFS2;
+ else if (!strcmp("sysfs", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_SYSFS;
+ else if (!strcmp("proc", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_PROC;
+ else if ( !strcmp("fuse", mntEnt.mnt_type)
+ || !strncmp("fuse.", mntEnt.mnt_type, 5)
+ || !strcmp("fuseblk", mntEnt.mnt_type))
+ *penmType = RTFSTYPE_FUSE;
+ else
+ {
+ /* sometimes there are more than one entry for the same partition */
+ continue;
+ }
+ break;
+ }
+ }
+ }
+ endmntent(mounted);
+ }
+
+#elif defined(RT_OS_SOLARIS)
+ /*
+ * Home directories are normally loopback mounted in Solaris 11 (st_fstype=="lofs")
+ * so statvfs(2) is needed to get the underlying file system information.
+ */
+ struct statvfs statvfsBuf;
+ if (!statvfs(pszNativeFsPath, &statvfsBuf))
+ {
+ if (!strcmp("zfs", statvfsBuf.f_basetype))
+ *penmType = RTFSTYPE_ZFS;
+ else if (!strcmp("ufs", statvfsBuf.f_basetype))
+ *penmType = RTFSTYPE_UFS;
+ else if (!strcmp("nfs", statvfsBuf.f_basetype))
+ *penmType = RTFSTYPE_NFS;
+ }
+
+#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
+ struct statfs statfsBuf;
+ if (!statfs(pszNativeFsPath, &statfsBuf))
+ {
+ if (!strcmp("hfs", statfsBuf.f_fstypename))
+ *penmType = RTFSTYPE_HFS;
+ else if (!strcmp("apfs", statfsBuf.f_fstypename)) /** @todo verify apfs signature. */
+ *penmType = RTFSTYPE_APFS;
+ else if ( !strcmp("fat", statfsBuf.f_fstypename)
+ || !strcmp("msdos", statfsBuf.f_fstypename))
+ *penmType = RTFSTYPE_FAT;
+ else if (!strcmp("ntfs", statfsBuf.f_fstypename))
+ *penmType = RTFSTYPE_NTFS;
+ else if (!strcmp("autofs", statfsBuf.f_fstypename))
+ *penmType = RTFSTYPE_AUTOFS;
+ else if (!strcmp("devfs", statfsBuf.f_fstypename))
+ *penmType = RTFSTYPE_DEVFS;
+ else if (!strcmp("nfs", statfsBuf.f_fstypename))
+ *penmType = RTFSTYPE_NFS;
+ else if (!strcmp("ufs", statfsBuf.f_fstypename))
+ *penmType = RTFSTYPE_UFS;
+ else if (!strcmp("zfs", statfsBuf.f_fstypename))
+ *penmType = RTFSTYPE_ZFS;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+#endif
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ rtPathFreeNative(pszNativeFsPath, pszFsPath);
+ }
+
+ return rc;
+}
diff --git a/src/VBox/Runtime/r3/posix/fs2-posix.cpp b/src/VBox/Runtime/r3/posix/fs2-posix.cpp
new file mode 100644
index 00000000..adbdecfe
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/fs2-posix.cpp
@@ -0,0 +1,165 @@
+/* $Id: fs2-posix.cpp $ */
+/** @file
+ * IPRT - File System Helpers, POSIX, Part 2.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTTIME_INCL_TIMESPEC
+#include <sys/time.h>
+#include <sys/param.h>
+#ifndef DEV_BSIZE
+# include <sys/stat.h>
+# if defined(RT_OS_HAIKU) && !defined(S_BLKSIZE)
+# define S_BLKSIZE 512
+# endif
+# define DEV_BSIZE S_BLKSIZE /** @todo bird: add DEV_BSIZE to sys/param.h on OS/2. */
+#endif
+
+#include <iprt/fs.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/time.h>
+#include "internal/fs.h"
+
+
+/**
+ * Internal worker function which setups RTFSOBJINFO based on a UNIX stat struct.
+ *
+ * @param pObjInfo The file system object info structure to setup.
+ * @param pStat The stat structure to use.
+ * @param pszName The filename which this applies to (exe/hidden check).
+ * @param cbName The length of that filename. (optional, set 0)
+ */
+void rtFsConvertStatToObjInfo(PRTFSOBJINFO pObjInfo, const struct stat *pStat, const char *pszName, unsigned cbName)
+{
+ pObjInfo->cbObject = pStat->st_size;
+ pObjInfo->cbAllocated = pStat->st_blocks * DEV_BSIZE;
+
+#ifdef HAVE_STAT_NSEC
+ RTTimeSpecAddNano(RTTimeSpecSetSeconds(&pObjInfo->AccessTime, pStat->st_atime), pStat->st_atimensec);
+ RTTimeSpecAddNano(RTTimeSpecSetSeconds(&pObjInfo->ModificationTime, pStat->st_mtime), pStat->st_mtimensec);
+ RTTimeSpecAddNano(RTTimeSpecSetSeconds(&pObjInfo->ChangeTime, pStat->st_ctime), pStat->st_ctimensec);
+# ifdef HAVE_STAT_BIRTHTIME
+ RTTimeSpecAddNano(RTTimeSpecSetSeconds(&pObjInfo->BirthTime, pStat->st_birthtime), pStat->st_birthtimensec);
+# endif
+
+#elif defined(HAVE_STAT_TIMESPEC_BRIEF)
+ RTTimeSpecSetTimespec(&pObjInfo->AccessTime, &pStat->st_atim);
+ RTTimeSpecSetTimespec(&pObjInfo->ModificationTime, &pStat->st_mtim);
+ RTTimeSpecSetTimespec(&pObjInfo->ChangeTime, &pStat->st_ctim);
+# ifdef HAVE_STAT_BIRTHTIME
+ RTTimeSpecSetTimespec(&pObjInfo->BirthTime, &pStat->st_birthtim);
+# endif
+
+#elif defined(HAVE_STAT_TIMESPEC)
+ RTTimeSpecSetTimespec(&pObjInfo->AccessTime, pStat->st_atimespec);
+ RTTimeSpecSetTimespec(&pObjInfo->ModificationTime, pStat->st_mtimespec);
+ RTTimeSpecSetTimespec(&pObjInfo->ChangeTime, pStat->st_ctimespec);
+# ifdef HAVE_STAT_BIRTHTIME
+ RTTimeSpecSetTimespec(&pObjInfo->BirthTime, pStat->st_birthtimespec);
+# endif
+
+#else /* just the normal stuff */
+ RTTimeSpecSetSeconds(&pObjInfo->AccessTime, pStat->st_atime);
+ RTTimeSpecSetSeconds(&pObjInfo->ModificationTime, pStat->st_mtime);
+ RTTimeSpecSetSeconds(&pObjInfo->ChangeTime, pStat->st_ctime);
+# ifdef HAVE_STAT_BIRTHTIME
+ RTTimeSpecSetSeconds(&pObjInfo->BirthTime, pStat->st_birthtime);
+# endif
+#endif
+#ifndef HAVE_STAT_BIRTHTIME
+ pObjInfo->BirthTime = pObjInfo->ChangeTime;
+#endif
+
+ /* the file mode */
+ RTFMODE fMode = pStat->st_mode & RTFS_UNIX_MASK;
+ Assert(RTFS_UNIX_ISUID == S_ISUID);
+ Assert(RTFS_UNIX_ISGID == S_ISGID);
+#ifdef S_ISTXT
+ Assert(RTFS_UNIX_ISTXT == S_ISTXT);
+#elif defined(S_ISVTX)
+ Assert(RTFS_UNIX_ISTXT == S_ISVTX);
+#else
+#error "S_ISVTX / S_ISTXT isn't defined"
+#endif
+ Assert(RTFS_UNIX_IRWXU == S_IRWXU);
+ Assert(RTFS_UNIX_IRUSR == S_IRUSR);
+ Assert(RTFS_UNIX_IWUSR == S_IWUSR);
+ Assert(RTFS_UNIX_IXUSR == S_IXUSR);
+ Assert(RTFS_UNIX_IRWXG == S_IRWXG);
+ Assert(RTFS_UNIX_IRGRP == S_IRGRP);
+ Assert(RTFS_UNIX_IWGRP == S_IWGRP);
+ Assert(RTFS_UNIX_IXGRP == S_IXGRP);
+ Assert(RTFS_UNIX_IRWXO == S_IRWXO);
+ Assert(RTFS_UNIX_IROTH == S_IROTH);
+ Assert(RTFS_UNIX_IWOTH == S_IWOTH);
+ Assert(RTFS_UNIX_IXOTH == S_IXOTH);
+ Assert(RTFS_TYPE_FIFO == S_IFIFO);
+ Assert(RTFS_TYPE_DEV_CHAR == S_IFCHR);
+ Assert(RTFS_TYPE_DIRECTORY == S_IFDIR);
+ Assert(RTFS_TYPE_DEV_BLOCK == S_IFBLK);
+ Assert(RTFS_TYPE_FILE == S_IFREG);
+ Assert(RTFS_TYPE_SYMLINK == S_IFLNK);
+ Assert(RTFS_TYPE_SOCKET == S_IFSOCK);
+#ifdef S_IFWHT
+ Assert(RTFS_TYPE_WHITEOUT == S_IFWHT);
+#endif
+ Assert(RTFS_TYPE_MASK == S_IFMT);
+
+ pObjInfo->Attr.fMode = rtFsModeFromUnix(fMode, pszName, cbName, 0);
+
+ /* additional unix attribs */
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pObjInfo->Attr.u.Unix.uid = pStat->st_uid;
+ pObjInfo->Attr.u.Unix.gid = pStat->st_gid;
+ pObjInfo->Attr.u.Unix.cHardlinks = pStat->st_nlink;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = pStat->st_dev;
+ pObjInfo->Attr.u.Unix.INodeId = pStat->st_ino;
+#ifdef HAVE_STAT_FLAGS
+ pObjInfo->Attr.u.Unix.fFlags = pStat->st_flags;
+#else
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+#endif
+#ifdef HAVE_STAT_GEN
+ pObjInfo->Attr.u.Unix.GenerationId = pStat->st_gen;
+#else
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+#endif
+ pObjInfo->Attr.u.Unix.Device = pStat->st_rdev;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/fs3-posix.cpp b/src/VBox/Runtime/r3/posix/fs3-posix.cpp
new file mode 100644
index 00000000..368ae8e3
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/fs3-posix.cpp
@@ -0,0 +1,94 @@
+/* $Id: fs3-posix.cpp $ */
+/** @file
+ * IPRT - File System Helpers, POSIX, Part 3.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/fs.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include "internal/fs.h"
+
+#include <sys/time.h>
+#include <grp.h>
+#include <pwd.h>
+
+
+/**
+ * Set user-owner additional attributes.
+ *
+ * @param pObjInfo The object info to fill add attrs for.
+ * @param uid The user id.
+ */
+void rtFsObjInfoAttrSetUnixOwner(PRTFSOBJINFO pObjInfo, RTUID uid)
+{
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pObjInfo->Attr.u.UnixOwner.uid = uid;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
+
+ char achBuf[_4K];
+ struct passwd Pwd;
+ struct passwd *pPwd;
+ int rc = getpwuid_r(uid, &Pwd, achBuf, sizeof(achBuf), &pPwd);
+ if (!rc && pPwd)
+ RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName), pPwd->pw_name);
+}
+
+
+/**
+ * Set user-group additional attributes.
+ *
+ * @param pObjInfo The object info to fill add attrs for.
+ * @param gid The group id.
+ */
+void rtFsObjInfoAttrSetUnixGroup(PRTFSOBJINFO pObjInfo, RTUID gid)
+{
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pObjInfo->Attr.u.UnixGroup.gid = gid;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+
+ char achBuf[_4K];
+ struct group Grp;
+ struct group *pGrp;
+
+ int rc = getgrgid_r(gid, &Grp, achBuf, sizeof(achBuf), &pGrp);
+ if (!rc && pGrp)
+ RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName), pGrp->gr_name);
+}
+
diff --git a/src/VBox/Runtime/r3/posix/ldrNative-posix.cpp b/src/VBox/Runtime/r3/posix/ldrNative-posix.cpp
new file mode 100644
index 00000000..9b33a0ff
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/ldrNative-posix.cpp
@@ -0,0 +1,207 @@
+/* $Id: ldrNative-posix.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader, POSIX native.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <dlfcn.h>
+
+#include <iprt/ldr.h>
+#include <iprt/assert.h>
+#include <iprt/path.h>
+#include <iprt/alloca.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/ldr.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+static const char g_szSuff[] = ".DLL";
+#elif defined(RT_OS_L4)
+static const char g_szSuff[] = ".s.so";
+#elif defined(RT_OS_DARWIN)
+static const char g_szSuff[] = ".dylib";
+#else
+static const char g_szSuff[] = ".so";
+#endif
+
+
+DECLHIDDEN(int) rtldrNativeLoad(const char *pszFilename, uintptr_t *phHandle, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ /*
+ * Do we need to add an extension?
+ */
+ if (!RTPathHasSuffix(pszFilename) && !(fFlags & RTLDRLOAD_FLAGS_NO_SUFFIX))
+ {
+ size_t cch = strlen(pszFilename);
+ char *psz = (char *)alloca(cch + sizeof(g_szSuff));
+ if (!psz)
+ return RTErrInfoSet(pErrInfo, VERR_NO_MEMORY, "alloca failed");
+ memcpy(psz, pszFilename, cch);
+ memcpy(psz + cch, g_szSuff, sizeof(g_szSuff));
+ pszFilename = psz;
+ }
+
+ /*
+ * Attempt load.
+ */
+ int fFlagsNative = RTLD_NOW;
+ if (fFlags & RTLDRLOAD_FLAGS_GLOBAL)
+ fFlagsNative |= RTLD_GLOBAL;
+ else
+ fFlagsNative |= RTLD_LOCAL;
+ void *pvMod = dlopen(pszFilename, fFlagsNative);
+ if (pvMod)
+ {
+ *phHandle = (uintptr_t)pvMod;
+ return VINF_SUCCESS;
+ }
+
+ const char *pszDlError = dlerror();
+ RTErrInfoSet(pErrInfo, VERR_FILE_NOT_FOUND, pszDlError);
+ LogRel(("rtldrNativeLoad: dlopen('%s', RTLD_NOW | RTLD_LOCAL) failed: %s\n", pszFilename, pszDlError));
+ return VERR_FILE_NOT_FOUND;
+}
+
+
+DECLCALLBACK(int) rtldrNativeGetSymbol(PRTLDRMODINTERNAL pMod, const char *pszSymbol, void **ppvValue)
+{
+ PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod;
+#ifdef RT_OS_OS2
+ /* Prefix the symbol with an underscore (assuming __cdecl/gcc-default). */
+ size_t cch = strlen(pszSymbol);
+ char *psz = (char *)alloca(cch + 2);
+ psz[0] = '_';
+ memcpy(psz + 1, pszSymbol, cch + 1);
+ pszSymbol = psz;
+#endif
+ *ppvValue = dlsym((void *)pModNative->hNative, pszSymbol);
+ if (*ppvValue)
+ return VINF_SUCCESS;
+ return VERR_SYMBOL_NOT_FOUND;
+}
+
+
+DECLCALLBACK(int) rtldrNativeClose(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod;
+#ifdef __SANITIZE_ADDRESS__
+ /* If we are compiled with enabled address sanitizer (gcc/llvm), don't
+ * unload the module to prevent <unknown module> in the stack trace */
+ pModNative->fFlags |= RTLDRLOAD_FLAGS_NO_UNLOAD;
+#endif
+ if ( (pModNative->fFlags & RTLDRLOAD_FLAGS_NO_UNLOAD)
+ || !dlclose((void *)pModNative->hNative))
+ {
+ pModNative->hNative = (uintptr_t)0;
+ return VINF_SUCCESS;
+ }
+ Log(("rtldrNativeFree: dlclose(%p) failed: %s\n", pModNative->hNative, dlerror()));
+ return VERR_GENERAL_FAILURE;
+}
+
+
+DECLHIDDEN(int) rtldrNativeLoadSystem(const char *pszFilename, const char *pszExt, uint32_t fFlags, PRTLDRMOD phLdrMod)
+{
+ /*
+ * For the present we ASSUME that we can trust dlopen to load what we want
+ * when not specifying a path. There seems to be very little we can do to
+ * restrict the places dlopen will search for library without doing
+ * auditing (linux) or something like that.
+ */
+ Assert(strchr(pszFilename, '/') == NULL);
+
+ uint32_t const fFlags2 = fFlags & ~(RTLDRLOAD_FLAGS_SO_VER_BEGIN_MASK | RTLDRLOAD_FLAGS_SO_VER_END_MASK);
+
+ /*
+ * If no suffix is given and we haven't got any RTLDRLOAD_FLAGS_SO_VER_ range to work
+ * with, we can call RTLdrLoadEx directly.
+ */
+ if (!pszExt)
+ {
+#if !defined(RT_OS_DARWIN) && !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS)
+ if ( (fFlags & RTLDRLOAD_FLAGS_SO_VER_BEGIN_MASK) >> RTLDRLOAD_FLAGS_SO_VER_BEGIN_SHIFT
+ == (fFlags & RTLDRLOAD_FLAGS_SO_VER_END_MASK) >> RTLDRLOAD_FLAGS_SO_VER_END_SHIFT)
+#endif
+ return RTLdrLoadEx(pszFilename, phLdrMod, fFlags2, NULL);
+ pszExt = "";
+ }
+
+ /*
+ * Combine filename and suffix and then do the loading.
+ */
+ size_t const cchFilename = strlen(pszFilename);
+ size_t const cchSuffix = strlen(pszExt);
+ char *pszTmp = (char *)alloca(cchFilename + cchSuffix + 16 + 1);
+ memcpy(pszTmp, pszFilename, cchFilename);
+ memcpy(&pszTmp[cchFilename], pszExt, cchSuffix);
+ pszTmp[cchFilename + cchSuffix] = '\0';
+
+ int rc = RTLdrLoadEx(pszTmp, phLdrMod, fFlags2, NULL);
+
+#if !defined(RT_OS_DARWIN) && !defined(RT_OS_OS2) && !defined(RT_OS_WINDOWS)
+ /*
+ * If no version was given after the .so and do .so.MAJOR search according
+ * to the range in the fFlags.
+ */
+ if (RT_FAILURE(rc) && !(fFlags & RTLDRLOAD_FLAGS_NO_SUFFIX))
+ {
+ const char *pszActualSuff = RTPathSuffix(pszTmp);
+ if (pszActualSuff && strcmp(pszActualSuff, ".so") == 0)
+ {
+ int32_t const iBegin = (fFlags & RTLDRLOAD_FLAGS_SO_VER_BEGIN_MASK) >> RTLDRLOAD_FLAGS_SO_VER_BEGIN_SHIFT;
+ int32_t const iEnd = (fFlags & RTLDRLOAD_FLAGS_SO_VER_END_MASK) >> RTLDRLOAD_FLAGS_SO_VER_END_SHIFT;
+ int32_t const iIncr = iBegin <= iEnd ? 1 : -1;
+ for (int32_t iMajorVer = iBegin; iMajorVer != iEnd; iMajorVer += iIncr)
+ {
+ RTStrPrintf(&pszTmp[cchFilename + cchSuffix], 16 + 1, ".%d", iMajorVer);
+ rc = RTLdrLoadEx(pszTmp, phLdrMod, fFlags2, NULL);
+ if (RT_SUCCESS(rc))
+ break;
+ }
+ }
+ }
+#endif
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/localipc-posix.cpp b/src/VBox/Runtime/r3/posix/localipc-posix.cpp
new file mode 100644
index 00000000..131d78cf
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/localipc-posix.cpp
@@ -0,0 +1,1172 @@
+/* $Id: localipc-posix.cpp $ */
+/** @file
+ * IPRT - Local IPC Server & Client, Posix.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LOCALIPC
+#include "internal/iprt.h"
+#include <iprt/localipc.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/log.h>
+#include <iprt/poll.h>
+#include <iprt/socket.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/path.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#ifndef RT_OS_OS2
+# include <sys/poll.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#ifndef AF_LOCAL
+# define AF_LOCAL AF_UNIX
+#endif
+
+#include "internal/magics.h"
+#include "internal/path.h"
+#include "internal/socket.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Local IPC service instance, POSIX.
+ */
+typedef struct RTLOCALIPCSERVERINT
+{
+ /** The magic (RTLOCALIPCSERVER_MAGIC). */
+ uint32_t u32Magic;
+ /** The creation flags. */
+ uint32_t fFlags;
+ /** Critical section protecting the structure. */
+ RTCRITSECT CritSect;
+ /** The number of references to the instance. */
+ uint32_t volatile cRefs;
+ /** Indicates that there is a pending cancel request. */
+ bool volatile fCancelled;
+ /** The server socket. */
+ RTSOCKET hSocket;
+ /** Thread currently listening for clients. */
+ RTTHREAD hListenThread;
+ /** The name we bound the server to (native charset encoding). */
+ struct sockaddr_un Name;
+} RTLOCALIPCSERVERINT;
+/** Pointer to a local IPC server instance (POSIX). */
+typedef RTLOCALIPCSERVERINT *PRTLOCALIPCSERVERINT;
+
+
+/**
+ * Local IPC session instance, POSIX.
+ */
+typedef struct RTLOCALIPCSESSIONINT
+{
+ /** The magic (RTLOCALIPCSESSION_MAGIC). */
+ uint32_t u32Magic;
+ /** Critical section protecting the structure. */
+ RTCRITSECT CritSect;
+ /** The number of references to the instance. */
+ uint32_t volatile cRefs;
+ /** Indicates that there is a pending cancel request. */
+ bool volatile fCancelled;
+ /** Set if this is the server side, clear if the client. */
+ bool fServerSide;
+ /** The client socket. */
+ RTSOCKET hSocket;
+ /** Thread currently doing read related activites. */
+ RTTHREAD hWriteThread;
+ /** Thread currently doing write related activies. */
+ RTTHREAD hReadThread;
+} RTLOCALIPCSESSIONINT;
+/** Pointer to a local IPC session instance (Windows). */
+typedef RTLOCALIPCSESSIONINT *PRTLOCALIPCSESSIONINT;
+
+
+/** Local IPC name prefix for portable names. */
+#define RTLOCALIPC_POSIX_NAME_PREFIX "/tmp/.iprt-localipc-"
+
+
+/**
+ * Validates the user specified name.
+ *
+ * @returns IPRT status code.
+ * @param pszName The name to validate.
+ * @param fNative Whether it's a native name or a portable name.
+ */
+static int rtLocalIpcPosixValidateName(const char *pszName, bool fNative)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(*pszName, VERR_INVALID_NAME);
+
+ if (!fNative)
+ {
+ for (;;)
+ {
+ char ch = *pszName++;
+ if (!ch)
+ break;
+ AssertReturn(!RT_C_IS_CNTRL(ch), VERR_INVALID_NAME);
+ AssertReturn((unsigned)ch < 0x80, VERR_INVALID_NAME);
+ AssertReturn(ch != '\\', VERR_INVALID_NAME);
+ AssertReturn(ch != '/', VERR_INVALID_NAME);
+ }
+ }
+ else
+ {
+ int rc = RTStrValidateEncoding(pszName);
+ AssertRCReturn(rc, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Constructs a local (unix) domain socket name.
+ *
+ * @returns IPRT status code.
+ * @param pAddr The address structure to construct the name in.
+ * @param pcbAddr Where to return the address size.
+ * @param pszName The user specified name (valid).
+ * @param fNative Whether it's a native name or a portable name.
+ */
+static int rtLocalIpcPosixConstructName(struct sockaddr_un *pAddr, uint8_t *pcbAddr, const char *pszName, bool fNative)
+{
+ const char *pszNativeName;
+ int rc = rtPathToNative(&pszNativeName, pszName, NULL /*pszBasePath not support*/);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchNativeName = strlen(pszNativeName);
+ size_t cbFull = !fNative ? cchNativeName + sizeof(RTLOCALIPC_POSIX_NAME_PREFIX) : cchNativeName + 1;
+ if (cbFull <= sizeof(pAddr->sun_path))
+ {
+ RT_ZERO(*pAddr);
+#ifdef RT_OS_OS2 /* Size must be exactly right on OS/2. */
+ *pcbAddr = sizeof(*pAddr);
+#else
+ *pcbAddr = RT_UOFFSETOF(struct sockaddr_un, sun_path) + (uint8_t)cbFull;
+#endif
+#ifdef HAVE_SUN_LEN_MEMBER
+ pAddr->sun_len = *pcbAddr;
+#endif
+ pAddr->sun_family = AF_LOCAL;
+
+ if (!fNative)
+ {
+ memcpy(pAddr->sun_path, RTLOCALIPC_POSIX_NAME_PREFIX, sizeof(RTLOCALIPC_POSIX_NAME_PREFIX) - 1);
+ memcpy(&pAddr->sun_path[sizeof(RTLOCALIPC_POSIX_NAME_PREFIX) - 1], pszNativeName, cchNativeName + 1);
+ }
+ else
+ memcpy(pAddr->sun_path, pszNativeName, cchNativeName + 1);
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ rtPathFreeNative(pszNativeName, pszName);
+ }
+ return rc;
+}
+
+
+
+RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszName, uint32_t fFlags)
+{
+ /*
+ * Parameter validation.
+ */
+ AssertPtrReturn(phServer, VERR_INVALID_POINTER);
+ *phServer = NIL_RTLOCALIPCSERVER;
+ AssertReturn(!(fFlags & ~RTLOCALIPC_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
+ int rc = rtLocalIpcPosixValidateName(pszName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate memory for the instance and initialize it.
+ */
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTLOCALIPCSERVER_MAGIC;
+ pThis->fFlags = fFlags;
+ pThis->cRefs = 1;
+ pThis->fCancelled = false;
+ pThis->hListenThread = NIL_RTTHREAD;
+ rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the local (unix) socket and bind to it.
+ */
+ rc = rtSocketCreate(&pThis->hSocket, AF_LOCAL, SOCK_STREAM, 0 /*iProtocol*/, false /*fInheritable*/);
+ if (RT_SUCCESS(rc))
+ {
+ signal(SIGPIPE, SIG_IGN); /* Required on solaris, at least. */
+
+ uint8_t cbAddr;
+ rc = rtLocalIpcPosixConstructName(&pThis->Name, &cbAddr, pszName,
+ RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtSocketBindRawAddr(pThis->hSocket, &pThis->Name, cbAddr);
+ if (rc == VERR_NET_ADDRESS_IN_USE)
+ {
+ unlink(pThis->Name.sun_path);
+ rc = rtSocketBindRawAddr(pThis->hSocket, &pThis->Name, cbAddr);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtSocketListen(pThis->hSocket, 16);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlow(("RTLocalIpcServerCreate: Created %p (%s)\n", pThis, pThis->Name.sun_path));
+ *phServer = pThis;
+ return VINF_SUCCESS;
+ }
+ unlink(pThis->Name.sun_path);
+ }
+ }
+ RTSocketRelease(pThis->hSocket);
+ }
+ RTCritSectDelete(&pThis->CritSect);
+ }
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ Log(("RTLocalIpcServerCreate: failed, rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcServerGrantGroupAccess(RTLOCALIPCSERVER hServer, RTGID gid)
+{
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->Name.sun_path[0] != '\0', VERR_INVALID_STATE);
+
+ if (chown(pThis->Name.sun_path, (uid_t)-1, gid) == 0)
+ {
+ if (chmod(pThis->Name.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == 0)
+ {
+ LogRel2(("RTLocalIpcServerGrantGroupAccess: IPC socket %s access has been granted to group %RTgid\n",
+ pThis->Name.sun_path, gid));
+ return VINF_SUCCESS;
+ }
+ LogRel(("RTLocalIpcServerGrantGroupAccess: cannot grant IPC socket %s write permission to group %RTgid: errno=%d\n",
+ pThis->Name.sun_path, gid, errno));
+ }
+ else
+ LogRel(("RTLocalIpcServerGrantGroupAccess: cannot change IPC socket %s group ownership to %RTgid: errno=%d\n",
+ pThis->Name.sun_path, gid, errno));
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(int) RTLocalIpcServerSetAccessMode(RTLOCALIPCSERVER hServer, RTFMODE fMode)
+{
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->Name.sun_path[0] != '\0', VERR_INVALID_STATE);
+
+ if (chmod(pThis->Name.sun_path, fMode & RTFS_UNIX_ALL_ACCESS_PERMS) == 0)
+ return VINF_SUCCESS;
+
+ return RTErrConvertFromErrno(errno);
+}
+
+
+/**
+ * Retains a reference to the server instance.
+ *
+ * @returns
+ * @param pThis The server instance.
+ */
+DECLINLINE(void) rtLocalIpcServerRetain(PRTLOCALIPCSERVERINT pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2 && cRefs); RT_NOREF_PV(cRefs);
+}
+
+
+/**
+ * Server instance destructor.
+ *
+ * @returns VINF_OBJECT_DESTROYED
+ * @param pThis The server instance.
+ */
+static int rtLocalIpcServerDtor(PRTLOCALIPCSERVERINT pThis)
+{
+ pThis->u32Magic = ~RTLOCALIPCSERVER_MAGIC;
+ if (RTSocketRelease(pThis->hSocket) == 0)
+ Log(("rtLocalIpcServerDtor: Released socket\n"));
+ else
+ Log(("rtLocalIpcServerDtor: Socket still has references (impossible?)\n"));
+ RTCritSectDelete(&pThis->CritSect);
+ unlink(pThis->Name.sun_path);
+ RTMemFree(pThis);
+ return VINF_OBJECT_DESTROYED;
+}
+
+
+/**
+ * Releases a reference to the server instance.
+ *
+ * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
+ * @param pThis The server instance.
+ */
+DECLINLINE(int) rtLocalIpcServerRelease(PRTLOCALIPCSERVERINT pThis)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (!cRefs)
+ return rtLocalIpcServerDtor(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * The core of RTLocalIpcServerCancel, used by both the destroy and cancel APIs.
+ *
+ * @returns IPRT status code
+ * @param pThis The server instance.
+ */
+static int rtLocalIpcServerCancel(PRTLOCALIPCSERVERINT pThis)
+{
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fCancelled = true;
+ Log(("rtLocalIpcServerCancel:\n"));
+ if (pThis->hListenThread != NIL_RTTHREAD)
+ RTThreadPoke(pThis->hListenThread);
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+
+
+RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer)
+{
+ /*
+ * Validate input.
+ */
+ if (hServer == NIL_RTLOCALIPCSERVER)
+ return VINF_SUCCESS;
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the server, releasing the caller's reference to the instance
+ * data and making sure any other thread in the listen API will wake up.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC, RTLOCALIPCSERVER_MAGIC), VERR_WRONG_ORDER);
+
+ rtLocalIpcServerCancel(pThis);
+ return rtLocalIpcServerRelease(pThis);
+}
+
+
+RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the job.
+ */
+ rtLocalIpcServerRetain(pThis);
+ rtLocalIpcServerCancel(pThis);
+ rtLocalIpcServerRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Begin listening.
+ */
+ rtLocalIpcServerRetain(pThis);
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->hListenThread == NIL_RTTHREAD)
+ {
+ pThis->hListenThread = RTThreadSelf();
+
+ /*
+ * The listening retry loop.
+ */
+ for (;;)
+ {
+ if (!pThis->fCancelled)
+ {
+ rc = RTCritSectLeave(&pThis->CritSect);
+ AssertRCBreak(rc);
+
+ struct sockaddr_un Addr;
+ size_t cbAddr = sizeof(Addr);
+ RTSOCKET hClient;
+ Log(("RTLocalIpcServerListen: Calling rtSocketAccept...\n"));
+ rc = rtSocketAccept(pThis->hSocket, &hClient, (struct sockaddr *)&Addr, &cbAddr);
+ Log(("RTLocalIpcServerListen: rtSocketAccept returns %Rrc.\n", rc));
+
+ int rc2 = RTCritSectEnter(&pThis->CritSect);
+ AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a client session.
+ */
+ PRTLOCALIPCSESSIONINT pSession = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pSession));
+ if (pSession)
+ {
+ pSession->u32Magic = RTLOCALIPCSESSION_MAGIC;
+ pSession->cRefs = 1;
+ pSession->fCancelled = false;
+ pSession->fServerSide = true;
+ pSession->hSocket = hClient;
+ pSession->hReadThread = NIL_RTTHREAD;
+ pSession->hWriteThread = NIL_RTTHREAD;
+ rc = RTCritSectInit(&pSession->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("RTLocalIpcServerListen: Returning new client session: %p\n", pSession));
+ *phClientSession = pSession;
+ break;
+ }
+
+ RTMemFree(pSession);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else if ( rc != VERR_INTERRUPTED
+ && rc != VERR_TRY_AGAIN)
+ break;
+ }
+ else
+ {
+ rc = VERR_CANCELLED;
+ break;
+ }
+ }
+
+ pThis->hListenThread = NIL_RTTHREAD;
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_RESOURCE_BUSY;
+ }
+ int rc2 = RTCritSectLeave(&pThis->CritSect);
+ AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
+ }
+ rtLocalIpcServerRelease(pThis);
+
+ Log(("RTLocalIpcServerListen: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
+{
+ /*
+ * Parameter validation.
+ */
+ AssertPtrReturn(phSession, VERR_INVALID_POINTER);
+ *phSession = NIL_RTLOCALIPCSESSION;
+
+ AssertReturn(!(fFlags & ~RTLOCALIPC_C_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
+
+ int rc = rtLocalIpcPosixValidateName(pszName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate memory for the instance and initialize it.
+ */
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
+ pThis->cRefs = 1;
+ pThis->fCancelled = false;
+ pThis->fServerSide = false;
+ pThis->hSocket = NIL_RTSOCKET;
+ pThis->hReadThread = NIL_RTTHREAD;
+ pThis->hWriteThread = NIL_RTTHREAD;
+ rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the local (unix) socket and try connect to the server.
+ */
+ rc = rtSocketCreate(&pThis->hSocket, AF_LOCAL, SOCK_STREAM, 0 /*iProtocol*/, false /*fInheritable*/);
+ if (RT_SUCCESS(rc))
+ {
+ signal(SIGPIPE, SIG_IGN); /* Required on solaris, at least. */
+
+ struct sockaddr_un Addr;
+ uint8_t cbAddr;
+ rc = rtLocalIpcPosixConstructName(&Addr, &cbAddr, pszName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtSocketConnectRaw(pThis->hSocket, &Addr, cbAddr);
+ if (RT_SUCCESS(rc))
+ {
+ *phSession = pThis;
+ Log(("RTLocalIpcSessionConnect: Returns new session %p\n", pThis));
+ return VINF_SUCCESS;
+ }
+ }
+ RTSocketRelease(pThis->hSocket);
+ }
+ RTCritSectDelete(&pThis->CritSect);
+ }
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ Log(("RTLocalIpcSessionConnect: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Retains a reference to the session instance.
+ *
+ * @param pThis The server instance.
+ */
+DECLINLINE(void) rtLocalIpcSessionRetain(PRTLOCALIPCSESSIONINT pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2 && cRefs); RT_NOREF_PV(cRefs);
+}
+
+
+RTDECL(uint32_t) RTLocalIpcSessionRetain(RTLOCALIPCSESSION hSession)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2 && cRefs);
+ return cRefs;
+}
+
+
+/**
+ * Session instance destructor.
+ *
+ * @returns VINF_OBJECT_DESTROYED
+ * @param pThis The server instance.
+ */
+static int rtLocalIpcSessionDtor(PRTLOCALIPCSESSIONINT pThis)
+{
+ pThis->u32Magic = ~RTLOCALIPCSESSION_MAGIC;
+ if (RTSocketRelease(pThis->hSocket) == 0)
+ Log(("rtLocalIpcSessionDtor: Released socket\n"));
+ else
+ Log(("rtLocalIpcSessionDtor: Socket still has references (impossible?)\n"));
+ RTCritSectDelete(&pThis->CritSect);
+ RTMemFree(pThis);
+ return VINF_OBJECT_DESTROYED;
+}
+
+
+/**
+ * Releases a reference to the session instance.
+ *
+ * @returns VINF_SUCCESS or VINF_OBJECT_DESTROYED as appropriate.
+ * @param pThis The session instance.
+ */
+DECLINLINE(int) rtLocalIpcSessionRelease(PRTLOCALIPCSESSIONINT pThis)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (!cRefs)
+ return rtLocalIpcSessionDtor(pThis);
+ Log(("rtLocalIpcSessionRelease: %u refs left\n", cRefs));
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTLocalIpcSessionRelease(RTLOCALIPCSESSION hSession)
+{
+ if (hSession == NIL_RTLOCALIPCSESSION)
+ return 0;
+
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (cRefs)
+ Log(("RTLocalIpcSessionRelease: %u refs left\n", cRefs));
+ else
+ rtLocalIpcSessionDtor(pThis);
+ return cRefs;
+}
+
+
+/**
+ * The core of RTLocalIpcSessionCancel, used by both the destroy and cancel APIs.
+ *
+ * @returns IPRT status code
+ * @param pThis The session instance.
+ */
+static int rtLocalIpcSessionCancel(PRTLOCALIPCSESSIONINT pThis)
+{
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fCancelled = true;
+ Log(("rtLocalIpcSessionCancel:\n"));
+ if (pThis->hReadThread != NIL_RTTHREAD)
+ RTThreadPoke(pThis->hReadThread);
+ if (pThis->hWriteThread != NIL_RTTHREAD)
+ RTThreadPoke(pThis->hWriteThread);
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
+{
+ /*
+ * Validate input.
+ */
+ if (hSession == NIL_RTLOCALIPCSESSION)
+ return VINF_SUCCESS;
+ PRTLOCALIPCSESSIONINT pThis = hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the session, releasing the caller's reference to the instance
+ * data and making sure any other thread in the listen API will wake up.
+ */
+ Log(("RTLocalIpcSessionClose:\n"));
+
+ rtLocalIpcSessionCancel(pThis);
+ return rtLocalIpcSessionRelease(pThis);
+}
+
+
+RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSESSIONINT pThis = hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the job.
+ */
+ rtLocalIpcSessionRetain(pThis);
+ rtLocalIpcSessionCancel(pThis);
+ rtLocalIpcSessionRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks if the socket has has a HUP condition after reading zero bytes.
+ *
+ * @returns true if HUP, false if no.
+ * @param pThis The IPC session handle.
+ */
+static bool rtLocalIpcPosixHasHup(PRTLOCALIPCSESSIONINT pThis)
+{
+ int fdNative = RTSocketToNative(pThis->hSocket);
+
+#if !defined(RT_OS_OS2) && !defined(RT_OS_SOLARIS)
+ struct pollfd PollFd;
+ RT_ZERO(PollFd);
+ PollFd.fd = fdNative;
+ PollFd.events = POLLHUP | POLLERR;
+ if (poll(&PollFd, 1, 0) <= 0)
+ return false;
+ if (!(PollFd.revents & (POLLHUP | POLLERR)))
+ return false;
+#else /* RT_OS_OS2 || RT_OS_SOLARIS */
+ /*
+ * OS/2: No native poll, do zero byte send to check for EPIPE.
+ * Solaris: We don't get POLLHUP.
+ */
+ uint8_t bDummy;
+ ssize_t rcSend = send(fdNative, &bDummy, 0, 0);
+ if (rcSend >= 0 || (errno != EPIPE && errno != ECONNRESET))
+ return false;
+#endif /* RT_OS_OS2 || RT_OS_SOLARIS */
+
+ /*
+ * We've established EPIPE. Now make sure there aren't any last bytes to
+ * read that came in between the recv made by the caller and the disconnect.
+ */
+ uint8_t bPeek;
+ ssize_t rcRecv = recv(fdNative, &bPeek, 1, MSG_DONTWAIT | MSG_PEEK);
+ return rcRecv <= 0;
+}
+
+
+RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSESSIONINT pThis = hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the job.
+ */
+ rtLocalIpcSessionRetain(pThis);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->hReadThread == NIL_RTTHREAD)
+ {
+ pThis->hReadThread = RTThreadSelf();
+
+ for (;;)
+ {
+ if (!pThis->fCancelled)
+ {
+ rc = RTCritSectLeave(&pThis->CritSect);
+ AssertRCBreak(rc);
+
+ rc = RTSocketRead(pThis->hSocket, pvBuf, cbToRead, pcbRead);
+
+ /* Detect broken pipe. */
+ if (rc == VINF_SUCCESS)
+ {
+ if (!pcbRead || *pcbRead)
+ { /* likely */ }
+ else if (rtLocalIpcPosixHasHup(pThis))
+ rc = VERR_BROKEN_PIPE;
+ }
+ else if (rc == VERR_NET_CONNECTION_RESET_BY_PEER || rc == VERR_NET_SHUTDOWN)
+ rc = VERR_BROKEN_PIPE;
+
+ int rc2 = RTCritSectEnter(&pThis->CritSect);
+ AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
+
+ if ( rc == VERR_INTERRUPTED
+ || rc == VERR_TRY_AGAIN)
+ continue;
+ }
+ else
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ pThis->hReadThread = NIL_RTTHREAD;
+ }
+ int rc2 = RTCritSectLeave(&pThis->CritSect);
+ AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
+ }
+
+ rtLocalIpcSessionRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionReadNB(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSESSIONINT pThis = hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the job.
+ */
+ rtLocalIpcSessionRetain(pThis);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->hReadThread == NIL_RTTHREAD)
+ {
+ pThis->hReadThread = RTThreadSelf(); /* not really required, but whatever. */
+
+ for (;;)
+ {
+ if (!pThis->fCancelled)
+ {
+ rc = RTSocketReadNB(pThis->hSocket, pvBuf, cbToRead, pcbRead);
+
+ /* Detect broken pipe. */
+ if (rc == VINF_SUCCESS)
+ {
+ if (!pcbRead || *pcbRead)
+ { /* likely */ }
+ else if (rtLocalIpcPosixHasHup(pThis))
+ rc = VERR_BROKEN_PIPE;
+ }
+ else if (rc == VERR_NET_CONNECTION_RESET_BY_PEER || rc == VERR_NET_SHUTDOWN)
+ rc = VERR_BROKEN_PIPE;
+
+ if (rc == VERR_INTERRUPTED)
+ continue;
+ }
+ else
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ pThis->hReadThread = NIL_RTTHREAD;
+ }
+ int rc2 = RTCritSectLeave(&pThis->CritSect);
+ AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
+ }
+
+ rtLocalIpcSessionRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuf, size_t cbToWrite)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSESSIONINT pThis = hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the job.
+ */
+ rtLocalIpcSessionRetain(pThis);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->hWriteThread == NIL_RTTHREAD)
+ {
+ pThis->hWriteThread = RTThreadSelf();
+
+ for (;;)
+ {
+ if (!pThis->fCancelled)
+ {
+ rc = RTCritSectLeave(&pThis->CritSect);
+ AssertRCBreak(rc);
+
+ rc = RTSocketWrite(pThis->hSocket, pvBuf, cbToWrite);
+
+ int rc2 = RTCritSectEnter(&pThis->CritSect);
+ AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
+
+ if ( rc == VERR_INTERRUPTED
+ || rc == VERR_TRY_AGAIN)
+ continue;
+ }
+ else
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ pThis->hWriteThread = NIL_RTTHREAD;
+ }
+ int rc2 = RTCritSectLeave(&pThis->CritSect);
+ AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
+ }
+
+ rtLocalIpcSessionRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSESSIONINT pThis = hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * This is a no-op because apparently write doesn't return until the
+ * result is read. At least that's what the reply to a 2003-04-08 LKML
+ * posting title "fsync() on unix domain sockets?" indicates.
+ *
+ * For conformity, make sure there isn't any active writes concurrent to this call.
+ */
+ rtLocalIpcSessionRetain(pThis);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->hWriteThread == NIL_RTTHREAD)
+ rc = RTCritSectLeave(&pThis->CritSect);
+ else
+ {
+ rc = RTCritSectLeave(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ rc = VERR_RESOURCE_BUSY;
+ }
+ }
+
+ rtLocalIpcSessionRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSESSIONINT pThis = hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the job.
+ */
+ rtLocalIpcSessionRetain(pThis);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->hReadThread == NIL_RTTHREAD)
+ {
+ pThis->hReadThread = RTThreadSelf();
+ uint64_t const msStart = RTTimeMilliTS();
+ RTMSINTERVAL const cMsOriginalTimeout = cMillies;
+
+ for (;;)
+ {
+ if (!pThis->fCancelled)
+ {
+ rc = RTCritSectLeave(&pThis->CritSect);
+ AssertRCBreak(rc);
+
+ uint32_t fEvents = 0;
+#ifdef RT_OS_OS2
+ /* This doesn't give us any error condition on hangup, so use HUP check. */
+ Log(("RTLocalIpcSessionWaitForData: Calling RTSocketSelectOneEx...\n"));
+ rc = RTSocketSelectOneEx(pThis->hSocket, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, &fEvents, cMillies);
+ Log(("RTLocalIpcSessionWaitForData: RTSocketSelectOneEx returns %Rrc, fEvents=%#x\n", rc, fEvents));
+ if (RT_SUCCESS(rc) && fEvents == RTPOLL_EVT_READ && rtLocalIpcPosixHasHup(pThis))
+ rc = VERR_BROKEN_PIPE;
+#else
+/** @todo RTSocketPoll? */
+ /* POLLHUP will be set on hangup. */
+ struct pollfd PollFd;
+ RT_ZERO(PollFd);
+ PollFd.fd = RTSocketToNative(pThis->hSocket);
+ PollFd.events = POLLHUP | POLLERR | POLLIN;
+ Log(("RTLocalIpcSessionWaitForData: Calling poll...\n"));
+ int cFds = poll(&PollFd, 1, cMillies == RT_INDEFINITE_WAIT ? -1 : (int)cMillies);
+ if (cFds >= 1)
+ {
+ /* Linux & Darwin sets both POLLIN and POLLHUP when the pipe is
+ broken and but no more data to read. Google hints at NetBSD
+ returning more sane values (POLLIN till no more data, then
+ POLLHUP). Solairs OTOH, doesn't ever seem to return POLLHUP. */
+ fEvents = RTPOLL_EVT_READ;
+ if ( (PollFd.revents & (POLLHUP | POLLERR))
+ && !(PollFd.revents & POLLIN))
+ fEvents = RTPOLL_EVT_ERROR;
+# if defined(RT_OS_SOLARIS)
+ else if (PollFd.revents & POLLIN)
+# else
+ else if ((PollFd.revents & (POLLIN | POLLHUP)) == (POLLIN | POLLHUP))
+# endif
+ {
+ /* Check if there is actually data available. */
+ uint8_t bPeek;
+ ssize_t rcRecv = recv(PollFd.fd, &bPeek, 1, MSG_DONTWAIT | MSG_PEEK);
+ if (rcRecv <= 0)
+ fEvents = RTPOLL_EVT_ERROR;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else if (rc == 0)
+ rc = VERR_TIMEOUT;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ Log(("RTLocalIpcSessionWaitForData: poll returns %u (rc=%d), revents=%#x\n", cFds, rc, PollFd.revents));
+#endif
+
+ int rc2 = RTCritSectEnter(&pThis->CritSect);
+ AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
+
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->fCancelled)
+ rc = VERR_CANCELLED;
+ else if (fEvents & RTPOLL_EVT_ERROR)
+ rc = VERR_BROKEN_PIPE;
+ }
+ else if ( rc == VERR_INTERRUPTED
+ || rc == VERR_TRY_AGAIN)
+ {
+ /* Recalc cMillies. */
+ if (cMsOriginalTimeout != RT_INDEFINITE_WAIT)
+ {
+ uint64_t cMsElapsed = RTTimeMilliTS() - msStart;
+ cMillies = cMsElapsed >= cMsOriginalTimeout ? 0 : cMsOriginalTimeout - (RTMSINTERVAL)cMsElapsed;
+ }
+ continue;
+ }
+ }
+ else
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ pThis->hReadThread = NIL_RTTHREAD;
+ }
+ int rc2 = RTCritSectLeave(&pThis->CritSect);
+ AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
+ }
+
+ rtLocalIpcSessionRelease(pThis);
+ return rc;
+}
+
+
+/**
+ * Get IPC session socket peer credentials.
+ *
+ * @returns IPRT status code.
+ * @param hSession IPC session handle.
+ * @param pProcess Where to return the remote peer's PID (can be NULL).
+ * @param pUid Where to return the remote peer's UID (can be NULL).
+ * @param pGid Where to return the remote peer's GID (can be NULL).
+ */
+static int rtLocalIpcSessionQueryUcred(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess, PRTUID pUid, PRTGID pGid)
+{
+ PRTLOCALIPCSESSIONINT pThis = hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+#if defined(RT_OS_LINUX)
+ struct ucred PeerCred = { (pid_t)NIL_RTPROCESS, (uid_t)NIL_RTUID, (gid_t)NIL_RTGID };
+ socklen_t cbPeerCred = sizeof(PeerCred);
+
+ rtLocalIpcSessionRetain(pThis);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);;
+ if (RT_SUCCESS(rc))
+ {
+ if (getsockopt(RTSocketToNative(pThis->hSocket), SOL_SOCKET, SO_PEERCRED, &PeerCred, &cbPeerCred) >= 0)
+ {
+ if (pProcess)
+ *pProcess = PeerCred.pid;
+ if (pUid)
+ *pUid = PeerCred.uid;
+ if (pGid)
+ *pGid = PeerCred.gid;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ int rc2 = RTCritSectLeave(&pThis->CritSect);
+ AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
+ }
+
+ rtLocalIpcSessionRelease(pThis);
+
+ return rc;
+
+#else
+ /** @todo Implement on other platforms too (mostly platform specific this).
+ * Solaris: getpeerucred? Darwin: LOCALPEERCRED or getpeereid? */
+ RT_NOREF(pProcess, pUid, pGid);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
+{
+ return rtLocalIpcSessionQueryUcred(hSession, pProcess, NULL, NULL);
+}
+
+
+RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
+{
+ return rtLocalIpcSessionQueryUcred(hSession, NULL, pUid, NULL);
+}
+
+RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTGID pGid)
+{
+ return rtLocalIpcSessionQueryUcred(hSession, NULL, NULL, pGid);
+}
+
diff --git a/src/VBox/Runtime/r3/posix/path-posix.cpp b/src/VBox/Runtime/r3/posix/path-posix.cpp
new file mode 100644
index 00000000..04462315
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/path-posix.cpp
@@ -0,0 +1,418 @@
+/* $Id: path-posix.cpp $ */
+/** @file
+ * IPRT - Path Manipulation, POSIX, Part 1.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PATH
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include <iprt/path.h>
+#include <iprt/env.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/path.h"
+#include "internal/process.h"
+#include "internal/fs.h"
+
+
+
+RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath)
+{
+ /*
+ * Convert input.
+ */
+ char const *pszNativePath;
+ int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * On POSIX platforms the API doesn't take a length parameter, which makes it
+ * a little bit more work.
+ */
+ char szTmpPath[PATH_MAX + 1];
+ const char *psz = realpath(pszNativePath, szTmpPath);
+ if (psz)
+ rc = rtPathFromNativeCopy(pszRealPath, cchRealPath, szTmpPath, NULL);
+ else
+ rc = RTErrConvertFromErrno(errno);
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+
+ LogFlow(("RTPathReal(%p:{%s}, %p:{%s}, %u): returns %Rrc\n", pszPath, pszPath,
+ pszRealPath, RT_SUCCESS(rc) ? pszRealPath : "<failed>", cchRealPath, rc));
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathSetMode(const char *pszPath, RTFMODE fMode)
+{
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+
+ int rc;
+ fMode = rtFsModeNormalize(fMode, pszPath, 0, 0);
+ if (rtFsModeIsValidPermissions(fMode))
+ {
+ char const *pszNativePath;
+ rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (chmod(pszNativePath, fMode & RTFS_UNIX_MASK) != 0)
+ rc = RTErrConvertFromErrno(errno);
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+ }
+ else
+ {
+ AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
+ rc = VERR_INVALID_FMODE;
+ }
+ return rc;
+}
+
+
+/**
+ * Checks if two files are the one and same file.
+ */
+static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
+{
+ struct stat SrcStat;
+ if (lstat(pszNativeSrc, &SrcStat))
+ return false;
+ struct stat DstStat;
+ if (lstat(pszNativeDst, &DstStat))
+ return false;
+ Assert(SrcStat.st_dev && DstStat.st_dev);
+ Assert(SrcStat.st_ino && DstStat.st_ino);
+ if ( SrcStat.st_dev == DstStat.st_dev
+ && SrcStat.st_ino == DstStat.st_ino
+ && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
+ return true;
+ return false;
+}
+
+
+/**
+ * Worker for RTPathRename, RTDirRename, RTFileRename.
+ *
+ * @returns IPRT status code.
+ * @param pszSrc The source path.
+ * @param pszDst The destination path.
+ * @param fRename The rename flags.
+ * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
+ * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
+ * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
+ * not a directory (we are NOT checking whether it's a file).
+ */
+DECLHIDDEN(int) rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
+{
+ /*
+ * Convert the paths.
+ */
+ char const *pszNativeSrc;
+ int rc = rtPathToNative(&pszNativeSrc, pszSrc, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ char const *pszNativeDst;
+ rc = rtPathToNative(&pszNativeDst, pszDst, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check that the source exists and that any types that's specified matches.
+ * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
+ * errors from the next step.
+ *
+ * There are race conditions here (perhaps unlikely ones, but still), but I'm
+ * afraid there is little with can do to fix that.
+ */
+ struct stat SrcStat;
+ if (lstat(pszNativeSrc, &SrcStat))
+ rc = RTErrConvertFromErrno(errno);
+ else if (!fFileType)
+ rc = VINF_SUCCESS;
+ else if (RTFS_IS_DIRECTORY(fFileType))
+ rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
+ else
+ rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ {
+ bool fSameFile = false;
+
+ /*
+ * Check if the target exists, rename is rather destructive.
+ * We'll have to make sure we don't overwrite the source!
+ * Another race condition btw.
+ */
+ struct stat DstStat;
+ if (lstat(pszNativeDst, &DstStat))
+ rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
+ else
+ {
+ Assert(SrcStat.st_dev && DstStat.st_dev);
+ Assert(SrcStat.st_ino && DstStat.st_ino);
+ if ( SrcStat.st_dev == DstStat.st_dev
+ && SrcStat.st_ino == DstStat.st_ino
+ && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
+ {
+ /*
+ * It's likely that we're talking about the same file here.
+ * We should probably check paths or whatever, but for now this'll have to be enough.
+ */
+ fSameFile = true;
+ }
+ if (fSameFile)
+ rc = VINF_SUCCESS;
+ else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
+ rc = VERR_ALREADY_EXISTS;
+ else
+ rc = VINF_SUCCESS;
+
+ }
+ if (RT_SUCCESS(rc))
+ {
+ if (!rename(pszNativeSrc, pszNativeDst))
+ rc = VINF_SUCCESS;
+ else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
+ && (errno == ENOTDIR || errno == EEXIST))
+ {
+ /*
+ * Check that the destination isn't a directory.
+ * Yet another race condition.
+ */
+ if (rtPathSame(pszNativeSrc, pszNativeDst))
+ {
+ rc = VINF_SUCCESS;
+ Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
+ pszSrc, pszDst, fRename, fFileType, errno));
+ }
+ else
+ {
+ if (lstat(pszNativeDst, &DstStat))
+ rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
+ else if (S_ISDIR(DstStat.st_mode))
+ rc = VERR_ALREADY_EXISTS;
+ else
+ rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ {
+ if (!unlink(pszNativeDst))
+ {
+ if (!rename(pszNativeSrc, pszNativeDst))
+ rc = VINF_SUCCESS;
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
+ pszSrc, pszDst, fRename, fFileType, rc, errno));
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
+ pszSrc, pszDst, fRename, fFileType, rc, errno));
+ }
+ }
+ else
+ Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
+ pszSrc, pszDst, fRename, fFileType, rc));
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ if (errno == ENOTDIR)
+ rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
+ Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
+ pszSrc, pszDst, fRename, fFileType, rc, errno));
+ }
+ }
+ else
+ Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
+ pszSrc, pszDst, fRename, fFileType, rc, errno));
+ }
+ else
+ Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
+ pszSrc, pszDst, fRename, fFileType, rc, errno));
+
+ rtPathFreeNative(pszNativeDst, pszDst);
+ }
+ rtPathFreeNative(pszNativeSrc, pszSrc);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Hand it to the worker.
+ */
+ int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
+
+ Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathUnlink(const char *pszPath, uint32_t fUnlink)
+{
+ RT_NOREF_PV(pszPath); RT_NOREF_PV(fUnlink);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTDECL(bool) RTPathExists(const char *pszPath)
+{
+ return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
+}
+
+
+RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, false);
+ AssertReturn(*pszPath, false);
+ Assert(RTPATH_F_IS_VALID(fFlags, 0));
+
+ /*
+ * Convert the path and check if it exists using stat().
+ */
+ char const *pszNativePath;
+ int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat Stat;
+ if (fFlags & RTPATH_F_FOLLOW_LINK)
+ rc = stat(pszNativePath, &Stat);
+ else
+ rc = lstat(pszNativePath, &Stat);
+ if (!rc)
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_GENERAL_FAILURE;
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+ return RT_SUCCESS(rc);
+}
+
+
+RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
+{
+ /*
+ * Try with a reasonably sized buffer first.
+ */
+ char szNativeCurDir[RTPATH_MAX];
+ if (getcwd(szNativeCurDir, sizeof(szNativeCurDir)) != NULL)
+ return rtPathFromNativeCopy(pszPath, cchPath, szNativeCurDir, NULL);
+
+ /*
+ * Retry a few times with really big buffers if we failed because CWD is unreasonably long.
+ */
+ int iErr = errno;
+ if (iErr != ERANGE)
+ return RTErrConvertFromErrno(iErr);
+
+ size_t cbNativeTmp = RTPATH_BIG_MAX;
+ for (;;)
+ {
+ char *pszNativeTmp = (char *)RTMemTmpAlloc(cbNativeTmp);
+ if (!pszNativeTmp)
+ return VERR_NO_TMP_MEMORY;
+ if (getcwd(pszNativeTmp, cbNativeTmp) != NULL)
+ {
+ int rc = rtPathFromNativeCopy(pszPath, cchPath, pszNativeTmp, NULL);
+ RTMemTmpFree(pszNativeTmp);
+ return rc;
+ }
+ iErr = errno;
+ RTMemTmpFree(pszNativeTmp);
+ if (iErr != ERANGE)
+ return RTErrConvertFromErrno(iErr);
+
+ cbNativeTmp += RTPATH_BIG_MAX;
+ if (cbNativeTmp > RTPATH_BIG_MAX * 4)
+ return VERR_FILENAME_TOO_LONG;
+ }
+}
+
+
+RTDECL(int) RTPathSetCurrent(const char *pszPath)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+
+ /*
+ * Change the directory.
+ */
+ char const *pszNativePath;
+ int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (chdir(pszNativePath))
+ rc = RTErrConvertFromErrno(errno);
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/path2-posix.cpp b/src/VBox/Runtime/r3/posix/path2-posix.cpp
new file mode 100644
index 00000000..9af9512f
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/path2-posix.cpp
@@ -0,0 +1,316 @@
+/* $Id: path2-posix.cpp $ */
+/** @file
+ * IPRT - Path Manipulation, POSIX, Part 2 - RTPathQueryInfo.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PATH
+#include <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <iprt/path.h>
+#include <iprt/env.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/path.h"
+#include "internal/process.h"
+#include "internal/fs.h"
+
+
+RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
+}
+
+
+RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
+ AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
+ && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+ ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the filename.
+ */
+ char const *pszNativePath;
+ int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat Stat;
+ if (fFlags & RTPATH_F_FOLLOW_LINK)
+ rc = stat(pszNativePath, &Stat);
+ else
+ rc = lstat(pszNativePath, &Stat); /** @todo how doesn't have lstat again? */
+ if (!rc)
+ {
+ rtFsConvertStatToObjInfo(pObjInfo, &Stat, pszPath, 0);
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ case RTFSOBJATTRADD_UNIX:
+ Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ rtFsObjInfoAttrSetUnixOwner(pObjInfo, Stat.st_uid);
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ rtFsObjInfoAttrSetUnixGroup(pObjInfo, Stat.st_gid);
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ /** @todo Use SGI extended attribute interface to query EA info. */
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+
+ LogFlow(("RTPathQueryInfoEx(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
+ pszPath, pszPath, pObjInfo, enmAdditionalAttribs, rc));
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ return RTPathSetTimesEx(pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, RTPATH_F_ON_LINK);
+}
+
+
+RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the paths.
+ */
+ char const *pszNativePath;
+ int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ RTFSOBJINFO ObjInfo;
+
+ /*
+ * If it's a no-op, we'll only verify the existance of the file.
+ */
+ if (!pAccessTime && !pModificationTime)
+ rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, fFlags);
+ else
+ {
+ /*
+ * Convert the input to timeval, getting the missing one if necessary,
+ * and call the API which does the change.
+ */
+ struct timeval aTimevals[2];
+ if (pAccessTime && pModificationTime)
+ {
+ RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]);
+ RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
+ }
+ else
+ {
+ rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
+ RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
+ }
+ else
+ Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
+ pszPath, pAccessTime, pModificationTime, rc));
+ }
+ if (RT_SUCCESS(rc))
+ {
+ if (fFlags & RTPATH_F_FOLLOW_LINK)
+ {
+ if (utimes(pszNativePath, aTimevals))
+ rc = RTErrConvertFromErrno(errno);
+ }
+#if (defined(RT_OS_DARWIN) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) \
+ || defined(RT_OS_FREEBSD) \
+ || defined(RT_OS_LINUX) \
+ || defined(RT_OS_OS2) /** @todo who really has lutimes? */
+ else
+ {
+ if (lutimes(pszNativePath, aTimevals))
+ {
+ /* If lutimes is not supported (e.g. linux < 2.6.22), try fall back on utimes: */
+ if (errno != ENOSYS)
+ rc = RTErrConvertFromErrno(errno);
+ else
+ {
+ if (pAccessTime && pModificationTime)
+ rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
+ if (RT_SUCCESS(rc) && !RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
+ {
+ if (utimes(pszNativePath, aTimevals))
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ }
+ }
+#else
+ else
+ {
+ if (pAccessTime && pModificationTime)
+ rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
+ if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
+ rc = VERR_NS_SYMLINK_SET_TIME;
+ else if (RT_SUCCESS(rc))
+ {
+ if (utimes(pszNativePath, aTimevals))
+ rc = RTErrConvertFromErrno(errno);
+ }
+ }
+#endif
+ if (RT_FAILURE(rc))
+ Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
+ pszPath, pAccessTime, pModificationTime, rc, errno));
+ }
+ }
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+
+ LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
+ pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
+ pChangeTime, pChangeTime, pBirthTime, pBirthTime, rc));
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathSetOwner(const char *pszPath, uint32_t uid, uint32_t gid)
+{
+ return RTPathSetOwnerEx(pszPath, uid, gid, RTPATH_F_ON_LINK);
+}
+
+
+RTR3DECL(int) RTPathSetOwnerEx(const char *pszPath, uint32_t uid, uint32_t gid, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ uid_t uidNative = uid != NIL_RTUID ? (uid_t)uid : (uid_t)-1;
+ AssertReturn(uid == uidNative, VERR_INVALID_PARAMETER);
+ gid_t gidNative = gid != NIL_RTGID ? (gid_t)gid : (uid_t)-1;
+ AssertReturn(gid == gidNative, VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the path.
+ */
+ char const *pszNativePath;
+ int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (fFlags & RTPATH_F_FOLLOW_LINK)
+ {
+ if (chown(pszNativePath, uidNative, gidNative))
+ rc = RTErrConvertFromErrno(errno);
+ }
+#if 1
+ else
+ {
+ if (lchown(pszNativePath, uidNative, gidNative))
+ rc = RTErrConvertFromErrno(errno);
+ }
+#else
+ else
+ {
+ RTFSOBJINFO ObjInfo;
+ rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
+ if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
+ rc = VERR_NS_SYMLINK_CHANGE_OWNER;
+ else if (RT_SUCCESS(rc))
+ {
+ if (lchown(pszNativePath, uidNative, gidNative))
+ rc = RTErrConvertFromErrno(errno);
+ }
+ }
+#endif
+ if (RT_FAILURE(rc))
+ Log(("RTPathSetOwnerEx('%s',%d,%d): failed with %Rrc and errno=%d\n",
+ pszPath, uid, gid, rc, errno));
+
+ rtPathFreeNative(pszNativePath, pszPath);
+ }
+
+ LogFlow(("RTPathSetOwnerEx(%p:{%s}, uid=%d, gid=%d): return %Rrc\n",
+ pszPath, pszPath, uid, gid, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/pathhost-posix.cpp b/src/VBox/Runtime/r3/posix/pathhost-posix.cpp
new file mode 100644
index 00000000..b0c4fe46
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/pathhost-posix.cpp
@@ -0,0 +1,294 @@
+/* $Id: pathhost-posix.cpp $ */
+/** @file
+ * IPRT - Path Conversions, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PATH
+#include "internal/iprt.h"
+#include "internal/path.h"
+#include "internal/string.h"
+#include "internal/thread.h"
+
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/once.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Initialize once object. */
+static RTONCE g_OnceInitPathConv = RTONCE_INITIALIZER;
+/** If set, then we can pass UTF-8 thru directly. */
+static bool g_fPassthruUtf8 = false;
+/** The UTF-8 to FS iconv cache entry. */
+static RTSTRICONV g_enmUtf8ToFsIdx = RTSTRICONV_UTF8_TO_LOCALE;
+/** The FS to UTF-8 iconv cache entry. */
+static RTSTRICONV g_enmFsToUtf8Idx = RTSTRICONV_LOCALE_TO_UTF8;
+/** The codeset we're using. */
+static char g_szFsCodeset[32];
+
+
+/**
+ * Do a case insensitive compare where the 2nd string is known and can be case
+ * folded when writing the code.
+ *
+ * @returns see strcmp.
+ * @param pszStr1 The string to compare against pszLower and
+ * pszUpper.
+ * @param pszUpper The upper case edition of the 2nd string.
+ * @param pszLower The lower case edition of the 2nd string.
+ */
+static int rtPathStrICmp(const char *pszStr1, const char *pszUpper, const char *pszLower)
+{
+ Assert(strlen(pszLower) == strlen(pszUpper));
+ for (;;)
+ {
+ char ch1 = *pszStr1++;
+ char ch2Upper = *pszUpper++;
+ char ch2Lower = *pszLower++;
+ if ( ch1 != ch2Upper
+ && ch1 != ch2Lower)
+ return ch1 < ch2Upper ? -1 : 1;
+ if (!ch1)
+ return 0;
+ }
+}
+
+/**
+ * Is the specified codeset something we can treat as UTF-8.
+ *
+ * @returns true if we can do UTF-8 passthru, false if not.
+ * @param pszCodeset The codeset in question.
+ */
+static bool rtPathConvInitIsUtf8(const char *pszCodeset)
+{
+ /* Paranoia. */
+ if (!pszCodeset)
+ return false;
+
+ /*
+ * Avoid RTStrICmp at this point.
+ */
+ static struct
+ {
+ const char *pszUpper;
+ const char *pszLower;
+ } const s_aUtf8Compatible[] =
+ {
+ /* The default locale. */
+ { "C" , "c" },
+ { "POSIX" , "posix" },
+ /* 7-bit ASCII. */
+ { "ANSI_X3.4-1968" , "ansi_x3.4-1968" },
+ { "ANSI_X3.4-1986" , "ansi_x3.4-1986" },
+ { "US-ASCII" , "us-ascii" },
+ { "ISO646-US" , "iso646-us" },
+ { "ISO_646.IRV:1991" , "iso_646.irv:1991" },
+ { "ISO-IR-6" , "iso-ir-6" },
+ { "IBM367" , "ibm367" },
+ /* UTF-8 */
+ { "UTF-8" , "utf-8" },
+ { "UTF8" , "utf8" },
+ { "ISO-10646/UTF-8" , "iso-10646/utf-8" },
+ { "ISO-10646/UTF8" , "iso-10646/utf8" }
+ };
+
+ for (size_t i = 0; i < RT_ELEMENTS(s_aUtf8Compatible); i++)
+ if (!rtPathStrICmp(pszCodeset, s_aUtf8Compatible[i].pszUpper, s_aUtf8Compatible[i].pszLower))
+ return true;
+
+ return false;
+}
+
+
+/**
+ * Init once for the path conversion code.
+ *
+ * @returns IPRT status code.
+ * @param pvUser1 Unused.
+ * @param pvUser2 Unused.
+ */
+static DECLCALLBACK(int32_t) rtPathConvInitOnce(void *pvUser)
+{
+ /*
+ * Read the environment variable, no mercy on misconfigs here except that
+ * empty values are quietly ignored. (We use a temp buffer for stripping.)
+ */
+ char *pszEnvValue = NULL;
+ char szEnvValue[sizeof(g_szFsCodeset)];
+ int rc = RTEnvGetEx(RTENV_DEFAULT, RTPATH_CODESET_ENV_VAR, szEnvValue, sizeof(szEnvValue), NULL);
+ if (rc != VERR_ENV_VAR_NOT_FOUND && RT_FAILURE(rc))
+ return rc;
+ if (RT_SUCCESS(rc))
+ pszEnvValue = RTStrStrip(szEnvValue);
+
+ if (pszEnvValue && *pszEnvValue)
+ {
+ g_fPassthruUtf8 = rtPathConvInitIsUtf8(pszEnvValue);
+ g_enmFsToUtf8Idx = RTSTRICONV_FS_TO_UTF8;
+ g_enmUtf8ToFsIdx = RTSTRICONV_UTF8_TO_FS;
+ strcpy(g_szFsCodeset, pszEnvValue);
+ }
+ else
+ {
+ const char *pszCodeset = rtStrGetLocaleCodeset();
+ size_t cchCodeset = pszCodeset ? strlen(pszCodeset) : sizeof(g_szFsCodeset);
+ if (cchCodeset >= sizeof(g_szFsCodeset))
+ /* This shouldn't happen, but we'll manage. */
+ g_szFsCodeset[0] = '\0';
+ else
+ {
+ memcpy(g_szFsCodeset, pszCodeset, cchCodeset + 1);
+ pszCodeset = g_szFsCodeset;
+ }
+ g_fPassthruUtf8 = rtPathConvInitIsUtf8(pszCodeset);
+ g_enmFsToUtf8Idx = RTSTRICONV_LOCALE_TO_UTF8;
+ g_enmUtf8ToFsIdx = RTSTRICONV_UTF8_TO_LOCALE;
+ }
+
+ NOREF(pvUser);
+ return VINF_SUCCESS;
+}
+
+
+int rtPathToNative(char const **ppszNativePath, const char *pszPath, const char *pszBasePath)
+{
+ *ppszNativePath = NULL;
+
+ int rc = RTOnce(&g_OnceInitPathConv, rtPathConvInitOnce, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_fPassthruUtf8 || !*pszPath)
+ *ppszNativePath = pszPath;
+ else
+ rc = rtStrConvert(pszPath, strlen(pszPath), "UTF-8",
+ (char **)ppszNativePath, 0, g_szFsCodeset,
+ 2, g_enmUtf8ToFsIdx);
+ }
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return rc;
+}
+
+
+void rtPathFreeNative(char const *pszNativePath, const char *pszPath)
+{
+ if ( pszNativePath != pszPath
+ && pszNativePath)
+ RTStrFree((char *)pszNativePath);
+}
+
+
+int rtPathFromNative(const char **ppszPath, const char *pszNativePath, const char *pszBasePath)
+{
+ *ppszPath = NULL;
+
+ int rc = RTOnce(&g_OnceInitPathConv, rtPathConvInitOnce, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_fPassthruUtf8 || !*pszNativePath)
+ {
+ size_t cCpsIgnored;
+ size_t cchNativePath;
+ rc = rtUtf8Length(pszNativePath, RTSTR_MAX, &cCpsIgnored, &cchNativePath);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszPath;
+ *ppszPath = pszPath = RTStrAlloc(cchNativePath + 1);
+ if (pszPath)
+ memcpy(pszPath, pszNativePath, cchNativePath + 1);
+ else
+ rc = VERR_NO_STR_MEMORY;
+ }
+ }
+ else
+ rc = rtStrConvert(pszNativePath, strlen(pszNativePath), g_szFsCodeset,
+ (char **)ppszPath, 0, "UTF-8",
+ 2, g_enmFsToUtf8Idx);
+ }
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return rc;
+}
+
+
+void rtPathFreeIprt(const char *pszPath, const char *pszNativePath)
+{
+ if ( pszPath != pszNativePath
+ && pszPath)
+ RTStrFree((char *)pszPath);
+}
+
+
+int rtPathFromNativeCopy(char *pszPath, size_t cbPath, const char *pszNativePath, const char *pszBasePath)
+{
+ int rc = RTOnce(&g_OnceInitPathConv, rtPathConvInitOnce, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_fPassthruUtf8 || !*pszNativePath)
+ rc = RTStrCopy(pszPath, cbPath, pszNativePath);
+ else if (cbPath)
+ rc = rtStrConvert(pszNativePath, strlen(pszNativePath), g_szFsCodeset,
+ &pszPath, cbPath, "UTF-8",
+ 2, g_enmFsToUtf8Idx);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return rc;
+}
+
+
+int rtPathFromNativeDup(char **ppszPath, const char *pszNativePath, const char *pszBasePath)
+{
+ int rc = RTOnce(&g_OnceInitPathConv, rtPathConvInitOnce, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (g_fPassthruUtf8 || !*pszNativePath)
+ rc = RTStrDupEx(ppszPath, pszNativePath);
+ else
+ rc = rtStrConvert(pszNativePath, strlen(pszNativePath), g_szFsCodeset,
+ ppszPath, 0, "UTF-8",
+ 2, g_enmFsToUtf8Idx);
+ }
+
+ NOREF(pszBasePath); /* We don't query the FS for codeset preferences. */
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/pipe-posix.cpp b/src/VBox/Runtime/r3/posix/pipe-posix.cpp
new file mode 100644
index 00000000..dc673bd3
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/pipe-posix.cpp
@@ -0,0 +1,754 @@
+/* $Id: pipe-posix.cpp $ */
+/** @file
+ * IPRT - Anonymous Pipes, POSIX Implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/pipe.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/poll.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
+#include <signal.h>
+#ifdef RT_OS_LINUX
+# include <sys/syscall.h>
+#endif
+#ifdef RT_OS_SOLARIS
+# include <sys/filio.h>
+#endif
+
+#include "internal/pipe.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTPIPEINTERNAL
+{
+ /** Magic value (RTPIPE_MAGIC). */
+ uint32_t u32Magic;
+ /** The file descriptor. */
+ int fd;
+ /** Set if this is the read end, clear if it's the write end. */
+ bool fRead;
+ /** RTPipeFromNative: Leave it open on RTPipeClose. */
+ bool fLeaveOpen;
+ /** Atomically operated state variable.
+ *
+ * - Bits 0 thru 29 - Users of the new mode.
+ * - Bit 30 - The pipe mode, set indicates blocking.
+ * - Bit 31 - Set when we're switching the mode.
+ */
+ uint32_t volatile u32State;
+} RTPIPEINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @name RTPIPEINTERNAL::u32State defines
+ * @{ */
+#define RTPIPE_POSIX_BLOCKING UINT32_C(0x40000000)
+#define RTPIPE_POSIX_SWITCHING UINT32_C(0x80000000)
+#define RTPIPE_POSIX_SWITCHING_BIT 31
+#define RTPIPE_POSIX_USERS_MASK UINT32_C(0x3fffffff)
+/** @} */
+
+
+
+/**
+ * Wrapper for calling pipe2() or pipe().
+ *
+ * When using pipe2() the returned handles are marked close-on-exec and does
+ * not risk racing process creation calls on other threads.
+ *
+ * @returns See pipe().
+ * @param paFds See pipe().
+ * @param piNewPipeSyscall Where to cache which call we should used. -1 if
+ * pipe(), 1 if pipe2(), 0 if not yet decided.
+ */
+static int my_pipe_wrapper(int *paFds, int *piNewPipeSyscall)
+{
+ if (*piNewPipeSyscall >= 0)
+ {
+#if defined(RT_OS_LINUX) && defined(__NR_pipe2) && defined(O_CLOEXEC)
+ long rc = syscall(__NR_pipe2, paFds, O_CLOEXEC);
+ if (rc >= 0)
+ {
+ if (*piNewPipeSyscall == 0)
+ *piNewPipeSyscall = 1;
+ return (int)rc;
+ }
+#endif
+ *piNewPipeSyscall = -1;
+ }
+
+ return pipe(paFds);
+}
+
+
+RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
+ AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the pipe and clear/set the close-on-exec flag as required.
+ */
+ int aFds[2] = {-1, -1};
+ static int s_iNewPipeSyscall = 0;
+ if (my_pipe_wrapper(aFds, &s_iNewPipeSyscall))
+ return RTErrConvertFromErrno(errno);
+
+ int rc = VINF_SUCCESS;
+ if (s_iNewPipeSyscall > 0)
+ {
+ /* created with close-on-exec set. */
+ if (fFlags & RTPIPE_C_INHERIT_READ)
+ {
+ if (fcntl(aFds[0], F_SETFD, 0))
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ if (fFlags & RTPIPE_C_INHERIT_WRITE)
+ {
+ if (fcntl(aFds[1], F_SETFD, 0))
+ rc = RTErrConvertFromErrno(errno);
+ }
+ }
+ else
+ {
+ /* created with close-on-exec cleared. */
+ if (!(fFlags & RTPIPE_C_INHERIT_READ))
+ {
+ if (fcntl(aFds[0], F_SETFD, FD_CLOEXEC))
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ if (!(fFlags & RTPIPE_C_INHERIT_WRITE))
+ {
+ if (fcntl(aFds[1], F_SETFD, FD_CLOEXEC))
+ rc = RTErrConvertFromErrno(errno);
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the two handles.
+ */
+ RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAlloc(sizeof(RTPIPEINTERNAL));
+ if (pThisR)
+ {
+ RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAlloc(sizeof(RTPIPEINTERNAL));
+ if (pThisW)
+ {
+ pThisR->u32Magic = RTPIPE_MAGIC;
+ pThisW->u32Magic = RTPIPE_MAGIC;
+ pThisR->fd = aFds[0];
+ pThisW->fd = aFds[1];
+ pThisR->fRead = true;
+ pThisW->fRead = false;
+ pThisR->fLeaveOpen = false;
+ pThisW->fLeaveOpen = false;
+ pThisR->u32State = RTPIPE_POSIX_BLOCKING;
+ pThisW->u32State = RTPIPE_POSIX_BLOCKING;
+
+ *phPipeRead = pThisR;
+ *phPipeWrite = pThisW;
+
+ /*
+ * Before we leave, make sure to shut up SIGPIPE.
+ */
+ signal(SIGPIPE, SIG_IGN);
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThisR);
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ close(aFds[0]);
+ close(aFds[1]);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ if (pThis == NIL_RTPIPE)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
+
+ int fd = pThis->fd;
+ pThis->fd = -1;
+ if (!fLeaveOpen && !pThis->fLeaveOpen)
+ close(fd);
+
+ if (ASMAtomicReadU32(&pThis->u32State) & RTPIPE_POSIX_USERS_MASK)
+ {
+ AssertFailed();
+ RTThreadSleep(1);
+ }
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeClose(RTPIPE hPipe)
+{
+ return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/);
+}
+
+
+RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER);
+ AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
+
+ /*
+ * Get and validate the pipe handle info.
+ */
+ int hNative = (int)hNativePipe;
+ struct stat st;
+ AssertReturn(fstat(hNative, &st) == 0, RTErrConvertFromErrno(errno));
+ AssertMsgReturn(S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode), ("%#x (%o)\n", st.st_mode, st.st_mode), VERR_INVALID_HANDLE);
+
+ int fFd = fcntl(hNative, F_GETFL, 0);
+ AssertReturn(fFd != -1, VERR_INVALID_HANDLE);
+ AssertMsgReturn( (fFd & O_ACCMODE) == (fFlags & RTPIPE_N_READ ? O_RDONLY : O_WRONLY)
+ || (fFd & O_ACCMODE) == O_RDWR /* Solaris creates bi-directional pipes. */
+ , ("%#x\n", fFd), VERR_INVALID_HANDLE);
+
+ /*
+ * Create the handle.
+ */
+ RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAlloc(sizeof(RTPIPEINTERNAL));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTPIPE_MAGIC;
+ pThis->fd = hNative;
+ pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ);
+ pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN);
+ pThis->u32State = fFd & O_NONBLOCK ? 0 : RTPIPE_POSIX_BLOCKING;
+
+ /*
+ * Fix up inheritability and shut up SIGPIPE and we're done.
+ */
+ if (fcntl(hNative, F_SETFD, fFlags & RTPIPE_N_INHERIT ? 0 : FD_CLOEXEC) == 0)
+ {
+ signal(SIGPIPE, SIG_IGN);
+ *phPipe = pThis;
+ return VINF_SUCCESS;
+ }
+
+ int rc = RTErrConvertFromErrno(errno);
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
+
+ return pThis->fd;
+}
+
+
+/**
+ * Prepare blocking mode.
+ *
+ * @returns VINF_SUCCESS
+ * @retval VERR_WRONG_ORDER
+ * @retval VERR_INTERNAL_ERROR_4
+ *
+ * @param pThis The pipe handle.
+ */
+static int rtPipeTryBlocking(RTPIPEINTERNAL *pThis)
+{
+ /*
+ * Update the state.
+ */
+ for (;;)
+ {
+ uint32_t u32State = ASMAtomicReadU32(&pThis->u32State);
+ uint32_t const u32StateOld = u32State;
+ uint32_t const cUsers = (u32State & RTPIPE_POSIX_USERS_MASK);
+
+ if (u32State & RTPIPE_POSIX_BLOCKING)
+ {
+ AssertReturn(cUsers < RTPIPE_POSIX_USERS_MASK / 2, VERR_INTERNAL_ERROR_4);
+ u32State &= ~RTPIPE_POSIX_USERS_MASK;
+ u32State |= cUsers + 1;
+ if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
+ {
+ if (u32State & RTPIPE_POSIX_SWITCHING)
+ break;
+ return VINF_SUCCESS;
+ }
+ }
+ else if (cUsers == 0)
+ {
+ u32State = 1 | RTPIPE_POSIX_SWITCHING | RTPIPE_POSIX_BLOCKING;
+ if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
+ break;
+ }
+ else
+ return VERR_WRONG_ORDER;
+ ASMNopPause();
+ }
+
+ /*
+ * Do the switching.
+ */
+ int fFlags = fcntl(pThis->fd, F_GETFL, 0);
+ if (fFlags != -1)
+ {
+ if ( !(fFlags & O_NONBLOCK)
+ || fcntl(pThis->fd, F_SETFL, fFlags & ~O_NONBLOCK) != -1)
+ {
+ ASMAtomicBitClear(&pThis->u32State, RTPIPE_POSIX_SWITCHING_BIT);
+ return VINF_SUCCESS;
+ }
+ }
+
+ ASMAtomicDecU32(&pThis->u32State);
+ return RTErrConvertFromErrno(errno);
+}
+
+
+/**
+ * Prepare non-blocking mode.
+ *
+ * @returns VINF_SUCCESS
+ * @retval VERR_WRONG_ORDER
+ * @retval VERR_INTERNAL_ERROR_4
+ *
+ * @param pThis The pipe handle.
+ */
+static int rtPipeTryNonBlocking(RTPIPEINTERNAL *pThis)
+{
+ /*
+ * Update the state.
+ */
+ for (;;)
+ {
+ uint32_t u32State = ASMAtomicReadU32(&pThis->u32State);
+ uint32_t const u32StateOld = u32State;
+ uint32_t const cUsers = (u32State & RTPIPE_POSIX_USERS_MASK);
+
+ if (!(u32State & RTPIPE_POSIX_BLOCKING))
+ {
+ AssertReturn(cUsers < RTPIPE_POSIX_USERS_MASK / 2, VERR_INTERNAL_ERROR_4);
+ u32State &= ~RTPIPE_POSIX_USERS_MASK;
+ u32State |= cUsers + 1;
+ if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
+ {
+ if (u32State & RTPIPE_POSIX_SWITCHING)
+ break;
+ return VINF_SUCCESS;
+ }
+ }
+ else if (cUsers == 0)
+ {
+ u32State = 1 | RTPIPE_POSIX_SWITCHING;
+ if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
+ break;
+ }
+ else
+ return VERR_WRONG_ORDER;
+ ASMNopPause();
+ }
+
+ /*
+ * Do the switching.
+ */
+ int fFlags = fcntl(pThis->fd, F_GETFL, 0);
+ if (fFlags != -1)
+ {
+ if ( (fFlags & O_NONBLOCK)
+ || fcntl(pThis->fd, F_SETFL, fFlags | O_NONBLOCK) != -1)
+ {
+ ASMAtomicBitClear(&pThis->u32State, RTPIPE_POSIX_SWITCHING_BIT);
+ return VINF_SUCCESS;
+ }
+ }
+
+ ASMAtomicDecU32(&pThis->u32State);
+ return RTErrConvertFromErrno(errno);
+}
+
+
+/**
+ * Checks if the read pipe has a HUP condition.
+ *
+ * @returns true if HUP, false if no.
+ * @param pThis The pipe handle (read).
+ */
+static bool rtPipePosixHasHup(RTPIPEINTERNAL *pThis)
+{
+ Assert(pThis->fRead);
+
+ struct pollfd PollFd;
+ RT_ZERO(PollFd);
+ PollFd.fd = pThis->fd;
+ PollFd.events = POLLHUP;
+ return poll(&PollFd, 1, 0) >= 1
+ && (PollFd.revents & POLLHUP);
+}
+
+
+RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbRead);
+ AssertPtr(pvBuf);
+
+ int rc = rtPipeTryNonBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ ssize_t cbRead = read(pThis->fd, pvBuf, RT_MIN(cbToRead, SSIZE_MAX));
+ if (cbRead >= 0)
+ {
+ if (cbRead || !cbToRead || !rtPipePosixHasHup(pThis))
+ *pcbRead = cbRead;
+ else
+ rc = VERR_BROKEN_PIPE;
+ }
+ else if (errno == EAGAIN)
+ {
+ *pcbRead = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ ASMAtomicDecU32(&pThis->u32State);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+
+ int rc = rtPipeTryBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbTotalRead = 0;
+ while (cbToRead > 0)
+ {
+ ssize_t cbRead = read(pThis->fd, pvBuf, RT_MIN(cbToRead, SSIZE_MAX));
+ if (cbRead < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+ if (!cbRead && rtPipePosixHasHup(pThis))
+ {
+ rc = VERR_BROKEN_PIPE;
+ break;
+ }
+
+ /* advance */
+ pvBuf = (char *)pvBuf + cbRead;
+ cbTotalRead += cbRead;
+ cbToRead -= cbRead;
+ }
+
+ if (pcbRead)
+ {
+ *pcbRead = cbTotalRead;
+ if ( RT_FAILURE(rc)
+ && cbTotalRead
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+
+ ASMAtomicDecU32(&pThis->u32State);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbWritten);
+ AssertPtr(pvBuf);
+
+ int rc = rtPipeTryNonBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ if (cbToWrite)
+ {
+ ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
+ if (cbWritten >= 0)
+ *pcbWritten = cbWritten;
+ else if (errno == EAGAIN)
+ {
+ *pcbWritten = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ *pcbWritten = 0;
+
+ ASMAtomicDecU32(&pThis->u32State);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+ AssertPtrNull(pcbWritten);
+
+ int rc = rtPipeTryBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
+ if (cbWritten < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+
+ /* advance */
+ pvBuf = (char const *)pvBuf + cbWritten;
+ cbTotalWritten += cbWritten;
+ cbToWrite -= cbWritten;
+ }
+
+ if (pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ if ( RT_FAILURE(rc)
+ && cbTotalWritten
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+
+ ASMAtomicDecU32(&pThis->u32State);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeFlush(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+
+ if (fsync(pThis->fd))
+ {
+ if (errno == EINVAL || errno == ENOTSUP)
+ return VERR_NOT_SUPPORTED;
+ return RTErrConvertFromErrno(errno);
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ struct pollfd PollFd;
+ RT_ZERO(PollFd);
+ PollFd.fd = pThis->fd;
+ PollFd.events = POLLHUP | POLLERR;
+ if (pThis->fRead)
+ PollFd.events |= POLLIN | POLLPRI;
+ else
+ PollFd.events |= POLLOUT;
+
+ int timeout;
+ if ( cMillies == RT_INDEFINITE_WAIT
+ || cMillies >= INT_MAX /* lazy bird */)
+ timeout = -1;
+ else
+ timeout = cMillies;
+
+ int rc = poll(&PollFd, 1, timeout);
+ if (rc == -1)
+ return RTErrConvertFromErrno(errno);
+ return rc > 0 ? VINF_SUCCESS : VERR_TIMEOUT;
+}
+
+
+RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
+ AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
+
+ int cb = 0;
+ int rc = ioctl(pThis->fd, FIONREAD, &cb);
+ if (rc != -1)
+ {
+ AssertStmt(cb >= 0, cb = 0);
+ *pcbReadable = cb;
+ return VINF_SUCCESS;
+ }
+
+ rc = errno;
+ if (rc == ENOTTY)
+ rc = VERR_NOT_SUPPORTED;
+ else
+ rc = RTErrConvertFromErrno(rc);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+
+ rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
+
+ if (pThis->fRead)
+ {
+ int cb = 0;
+ int rc = ioctl(pThis->fd, FIONREAD, &cb);
+ if (rc >= 0)
+ pObjInfo->cbObject = cb;
+ }
+#ifdef FIONSPACE
+ else
+ {
+ int cb = 0;
+ int rc = ioctl(pThis->fd, FIONSPACE, &cb);
+ if (rc >= 0)
+ pObjInfo->cbObject = cb;
+ }
+#endif
+
+ /** @todo Check this out on linux, solaris and darwin... (Currently going by a
+ * FreeBSD manpage.) */
+ struct stat St;
+ if (fstat(pThis->fd, &St))
+ {
+ pObjInfo->cbAllocated = St.st_blksize;
+ if ( enmAddAttr == RTFSOBJATTRADD_NOTHING
+ || enmAddAttr == RTFSOBJATTRADD_UNIX)
+ {
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pObjInfo->Attr.u.Unix.INodeId = St.st_ino;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = St.st_dev;
+ }
+ }
+ /** @todo error handling? */
+
+ return VINF_SUCCESS;
+}
+
+
+int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
+
+ *phNative = pThis->fd;
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/process-creation-posix.cpp b/src/VBox/Runtime/r3/posix/process-creation-posix.cpp
new file mode 100644
index 00000000..6cf7be73
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/process-creation-posix.cpp
@@ -0,0 +1,2408 @@
+/* $Id: process-creation-posix.cpp $ */
+/** @file
+ * IPRT - Process Creation, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <iprt/cdefs.h>
+#ifdef RT_OS_LINUX
+# define IPRT_WITH_DYNAMIC_CRYPT_R
+#endif
+#if (defined(RT_OS_LINUX) || defined(RT_OS_OS2)) && !defined(_GNU_SOURCE)
+# define _GNU_SOURCE
+#endif
+#if defined(RT_OS_LINUX) && !defined(_XOPEN_SOURCE)
+# define _XOPEN_SOURCE 700 /* for newlocale */
+#endif
+
+#ifdef RT_OS_OS2
+# define crypt unistd_crypt
+# define setkey unistd_setkey
+# define encrypt unistd_encrypt
+# include <unistd.h>
+# undef crypt
+# undef setkey
+# undef encrypt
+#else
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <grp.h>
+#include <pwd.h>
+#if defined(RT_OS_LINUX) || defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
+# include <crypt.h>
+#endif
+#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
+# include <shadow.h>
+#endif
+#if defined(RT_OS_DARWIN)
+# include <xlocale.h> /* for newlocale() */
+#endif
+
+#if defined(RT_OS_LINUX) || defined(RT_OS_OS2)
+/* While Solaris has posix_spawn() of course we don't want to use it as
+ * we need to have the child in a different process contract, no matter
+ * whether it is started detached or not. */
+# define HAVE_POSIX_SPAWN 1
+#endif
+#if defined(RT_OS_DARWIN) && defined(MAC_OS_X_VERSION_MIN_REQUIRED)
+# if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+# define HAVE_POSIX_SPAWN 1
+# endif
+#endif
+#ifdef HAVE_POSIX_SPAWN
+# include <spawn.h>
+#endif
+
+#if !defined(IPRT_USE_PAM) \
+ && !defined(IPRT_WITHOUT_PAM) \
+ && ( defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX) || defined(RT_OS_NETBSD) || defined(RT_OS_OPENBSD) || defined(RT_OS_SOLARIS) )
+# define IPRT_USE_PAM
+#endif
+#ifdef IPRT_USE_PAM
+# include <security/pam_appl.h>
+# include <stdlib.h>
+# include <dlfcn.h>
+# include <iprt/asm.h>
+#endif
+
+#ifdef RT_OS_SOLARIS
+# include <limits.h>
+# include <sys/ctfs.h>
+# include <sys/contract/process.h>
+# include <libcontract.h>
+#endif
+
+#ifndef RT_OS_SOLARIS
+# include <paths.h>
+#else
+# define _PATH_MAILDIR "/var/mail"
+# define _PATH_DEFPATH "/usr/bin:/bin"
+# define _PATH_STDPATH "/sbin:/usr/sbin:/bin:/usr/bin"
+#endif
+#ifndef _PATH_BSHELL
+# define _PATH_BSHELL "/bin/sh"
+#endif
+
+
+#include <iprt/process.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#if defined(IPRT_WITH_DYNAMIC_CRYPT_R) || defined(IPRT_USE_PAM)
+# include <iprt/ldr.h>
+#endif
+#include <iprt/log.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/socket.h>
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include "internal/process.h"
+#include "internal/path.h"
+#include "internal/string.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifdef IPRT_USE_PAM
+/*
+ * The PAM library names and version ranges to try.
+ */
+# ifdef RT_OS_DARWIN
+# include <mach-o/dyld.h>
+/** @node libpam.2.dylib was introduced with 10.6.x (OpenPAM); we use
+ * libpam.dylib as that's a symlink to the latest and greatest. */
+# define IPRT_LIBPAM_FILE_1 "libpam.dylib"
+# define IPRT_LIBPAM_FILE_1_FIRST_VER 0
+# define IPRT_LIBPAM_FILE_1_END_VER 0
+# define IPRT_LIBPAM_FILE_2 "libpam.2.dylib"
+# define IPRT_LIBPAM_FILE_2_FIRST_VER 0
+# define IPRT_LIBPAM_FILE_2_END_VER 0
+# define IPRT_LIBPAM_FILE_3 "libpam.1.dylib"
+# define IPRT_LIBPAM_FILE_3_FIRST_VER 0
+# define IPRT_LIBPAM_FILE_3_END_VER 0
+# elif RT_OS_LINUX
+# define IPRT_LIBPAM_FILE_1 "libpam.so.0"
+# define IPRT_LIBPAM_FILE_1_FIRST_VER 0
+# define IPRT_LIBPAM_FILE_1_END_VER 0
+# define IPRT_LIBPAM_FILE_2 "libpam.so"
+# define IPRT_LIBPAM_FILE_2_FIRST_VER 16
+# define IPRT_LIBPAM_FILE_2_END_VER 1
+# else
+# define IPRT_LIBPAM_FILE_1 "libpam.so"
+# define IPRT_LIBPAM_FILE_1_FIRST_VER 16
+# define IPRT_LIBPAM_FILE_1_END_VER 0
+# endif
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#ifdef IPRT_USE_PAM
+/** For passing info between rtCheckCredentials and rtPamConv. */
+typedef struct RTPROCPAMARGS
+{
+ const char *pszUser;
+ const char *pszPassword;
+} RTPROCPAMARGS;
+/** Pointer to rtPamConv argument package. */
+typedef RTPROCPAMARGS *PRTPROCPAMARGS;
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Environment dump marker used with CSH. */
+static const char g_szEnvMarkerBegin[] = "IPRT_EnvEnvEnv_Begin_EnvEnvEnv";
+/** Environment dump marker used with CSH. */
+static const char g_szEnvMarkerEnd[] = "IPRT_EnvEnvEnv_End_EnvEnvEnv";
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int rtProcPosixCreateInner(const char *pszExec, const char * const *papszArgs, RTENV hEnv, RTENV hEnvToUse,
+ uint32_t fFlags, const char *pszAsUser, uid_t uid, gid_t gid,
+ unsigned cRedirFds, int *paRedirFds, PRTPROCESS phProcess);
+
+
+#ifdef IPRT_USE_PAM
+/**
+ * Worker for rtCheckCredentials that feeds password and maybe username to PAM.
+ *
+ * @returns PAM status.
+ * @param cMessages Number of messages.
+ * @param papMessages Message vector.
+ * @param ppaResponses Where to put our responses.
+ * @param pvAppData Pointer to RTPROCPAMARGS.
+ */
+#if defined(RT_OS_SOLARIS)
+static int rtPamConv(int cMessages, struct pam_message **papMessages, struct pam_response **ppaResponses, void *pvAppData)
+#else
+static int rtPamConv(int cMessages, const struct pam_message **papMessages, struct pam_response **ppaResponses, void *pvAppData)
+#endif
+{
+ LogFlow(("rtPamConv: cMessages=%d\n", cMessages));
+ PRTPROCPAMARGS pArgs = (PRTPROCPAMARGS)pvAppData;
+ AssertPtrReturn(pArgs, PAM_CONV_ERR);
+
+ struct pam_response *paResponses = (struct pam_response *)calloc(cMessages, sizeof(paResponses[0]));
+ AssertReturn(paResponses, PAM_CONV_ERR);
+ for (int i = 0; i < cMessages; i++)
+ {
+ LogFlow(("rtPamConv: #%d: msg_style=%d msg=%s\n", i, papMessages[i]->msg_style, papMessages[i]->msg));
+
+ paResponses[i].resp_retcode = 0;
+ if (papMessages[i]->msg_style == PAM_PROMPT_ECHO_OFF)
+ paResponses[i].resp = strdup(pArgs->pszPassword);
+ else if (papMessages[i]->msg_style == PAM_PROMPT_ECHO_ON)
+ paResponses[i].resp = strdup(pArgs->pszUser);
+ else
+ {
+ paResponses[i].resp = NULL;
+ continue;
+ }
+ if (paResponses[i].resp == NULL)
+ {
+ while (i-- > 0)
+ free(paResponses[i].resp);
+ free(paResponses);
+ LogFlow(("rtPamConv: out of memory\n"));
+ return PAM_CONV_ERR;
+ }
+ }
+
+ *ppaResponses = paResponses;
+ return PAM_SUCCESS;
+}
+
+
+/**
+ * Common PAM driver for rtCheckCredentials and the case where pszAsUser is NULL
+ * but RTPROC_FLAGS_PROFILE is set.
+ *
+ * @returns IPRT status code.
+ * @param pszPamService The PAM service to use for the run.
+ * @param pszUser The user.
+ * @param pszPassword The password.
+ * @param ppapszEnv Where to return PAM environment variables, NULL is
+ * fine if no variables to return. Call
+ * rtProcPosixFreePamEnv to free. Optional, so NULL
+ * can be passed in.
+ * @param pfMayFallBack Where to return whether a fallback to crypt is
+ * acceptable or if the failure result is due to
+ * authentication failing. Optional.
+ */
+static int rtProcPosixAuthenticateUsingPam(const char *pszPamService, const char *pszUser, const char *pszPassword,
+ char ***ppapszEnv, bool *pfMayFallBack)
+{
+ if (pfMayFallBack)
+ *pfMayFallBack = true;
+
+ /*
+ * Dynamically load pam the first time we go thru here.
+ */
+ static int (*s_pfnPamStart)(const char *, const char *, struct pam_conv *, pam_handle_t **);
+ static int (*s_pfnPamAuthenticate)(pam_handle_t *, int);
+ static int (*s_pfnPamAcctMgmt)(pam_handle_t *, int);
+ static int (*s_pfnPamSetItem)(pam_handle_t *, int, const void *);
+ static int (*s_pfnPamSetCred)(pam_handle_t *, int);
+ static char ** (*s_pfnPamGetEnvList)(pam_handle_t *);
+ static int (*s_pfnPamOpenSession)(pam_handle_t *, int);
+ static int (*s_pfnPamCloseSession)(pam_handle_t *, int);
+ static int (*s_pfnPamEnd)(pam_handle_t *, int);
+ if ( s_pfnPamStart == NULL
+ || s_pfnPamAuthenticate == NULL
+ || s_pfnPamAcctMgmt == NULL
+ || s_pfnPamSetItem == NULL
+ || s_pfnPamEnd == NULL)
+ {
+ RTLDRMOD hModPam = NIL_RTLDRMOD;
+ const char *pszLast;
+ int rc = RTLdrLoadSystemEx(pszLast = IPRT_LIBPAM_FILE_1, RTLDRLOAD_FLAGS_GLOBAL | RTLDRLOAD_FLAGS_NO_UNLOAD
+ | RTLDRLOAD_FLAGS_SO_VER_RANGE(IPRT_LIBPAM_FILE_1_FIRST_VER, IPRT_LIBPAM_FILE_1_END_VER),
+ &hModPam);
+# ifdef IPRT_LIBPAM_FILE_2
+ if (RT_FAILURE(rc))
+ rc = RTLdrLoadSystemEx(pszLast = IPRT_LIBPAM_FILE_2, RTLDRLOAD_FLAGS_GLOBAL | RTLDRLOAD_FLAGS_NO_UNLOAD
+ | RTLDRLOAD_FLAGS_SO_VER_RANGE(IPRT_LIBPAM_FILE_2_FIRST_VER, IPRT_LIBPAM_FILE_2_END_VER),
+ &hModPam);
+# endif
+# ifdef IPRT_LIBPAM_FILE_3
+ if (RT_FAILURE(rc))
+ rc = RTLdrLoadSystemEx(pszLast = IPRT_LIBPAM_FILE_3, RTLDRLOAD_FLAGS_GLOBAL | RTLDRLOAD_FLAGS_NO_UNLOAD
+ | RTLDRLOAD_FLAGS_SO_VER_RANGE(IPRT_LIBPAM_FILE_3_FIRST_VER, IPRT_LIBPAM_FILE_3_END_VER),
+ &hModPam);
+# endif
+ if (RT_FAILURE(rc))
+ {
+ LogRelMax(10, ("failed to load %s: %Rrc\n", pszLast, rc));
+ return VERR_AUTHENTICATION_FAILURE;
+ }
+
+ *(uintptr_t *)&s_pfnPamStart = (uintptr_t)RTLdrGetFunction(hModPam, "pam_start");
+ *(uintptr_t *)&s_pfnPamAuthenticate = (uintptr_t)RTLdrGetFunction(hModPam, "pam_authenticate");
+ *(uintptr_t *)&s_pfnPamAcctMgmt = (uintptr_t)RTLdrGetFunction(hModPam, "pam_acct_mgmt");
+ *(uintptr_t *)&s_pfnPamSetItem = (uintptr_t)RTLdrGetFunction(hModPam, "pam_set_item");
+ *(uintptr_t *)&s_pfnPamSetCred = (uintptr_t)RTLdrGetFunction(hModPam, "pam_setcred");
+ *(uintptr_t *)&s_pfnPamGetEnvList = (uintptr_t)RTLdrGetFunction(hModPam, "pam_getenvlist");
+ *(uintptr_t *)&s_pfnPamOpenSession = (uintptr_t)RTLdrGetFunction(hModPam, "pam_open_session");
+ *(uintptr_t *)&s_pfnPamCloseSession = (uintptr_t)RTLdrGetFunction(hModPam, "pam_close_session");
+ *(uintptr_t *)&s_pfnPamEnd = (uintptr_t)RTLdrGetFunction(hModPam, "pam_end");
+ ASMCompilerBarrier();
+
+ RTLdrClose(hModPam);
+
+ if ( s_pfnPamStart == NULL
+ || s_pfnPamAuthenticate == NULL
+ || s_pfnPamAcctMgmt == NULL
+ || s_pfnPamSetItem == NULL
+ || s_pfnPamEnd == NULL)
+ {
+ LogRelMax(10, ("failed to resolve symbols: %p %p %p %p %p\n",
+ s_pfnPamStart, s_pfnPamAuthenticate, s_pfnPamAcctMgmt, s_pfnPamSetItem, s_pfnPamEnd));
+ return VERR_AUTHENTICATION_FAILURE;
+ }
+ }
+
+# define pam_start s_pfnPamStart
+# define pam_authenticate s_pfnPamAuthenticate
+# define pam_acct_mgmt s_pfnPamAcctMgmt
+# define pam_set_item s_pfnPamSetItem
+# define pam_setcred s_pfnPamSetCred
+# define pam_getenvlist s_pfnPamGetEnvList
+# define pam_open_session s_pfnPamOpenSession
+# define pam_close_session s_pfnPamCloseSession
+# define pam_end s_pfnPamEnd
+
+ /*
+ * Do the PAM stuff.
+ */
+ pam_handle_t *hPam = NULL;
+ RTPROCPAMARGS PamConvArgs = { pszUser, pszPassword };
+ struct pam_conv PamConversation;
+ RT_ZERO(PamConversation);
+ PamConversation.appdata_ptr = &PamConvArgs;
+ PamConversation.conv = rtPamConv;
+ int rc = pam_start(pszPamService, pszUser, &PamConversation, &hPam);
+ if (rc == PAM_SUCCESS)
+ {
+ rc = pam_set_item(hPam, PAM_RUSER, pszUser);
+ LogRel2(("rtProcPosixAuthenticateUsingPam(%s): pam_setitem/PAM_RUSER: %s\n", pszPamService, pszUser));
+ if (rc == PAM_SUCCESS)
+ {
+ /*
+ * Secure TTY fun ahead (for pam_securetty).
+ *
+ * We need to set PAM_TTY (if available) to make PAM stacks work which
+ * require a secure TTY via pam_securetty (Debian 10 + 11, for example). This
+ * is typically an issue when launching as 'root'. See @bugref{10225}.
+ *
+ * Note! We only can try (or better: guess) to a certain amount, as it really
+ * depends on the distribution or Administrator which has set up the
+ * system which (and how) things are allowed (see /etc/securetty).
+ *
+ * Note! We don't acctually try or guess anything about the distro like
+ * suggested by the above note, we just try determine the TTY of
+ * the _parent_ process and hope for the best. (bird)
+ */
+ char szTTY[64];
+ int rc2 = RTEnvGetEx(RTENV_DEFAULT, "DISPLAY", szTTY, sizeof(szTTY), NULL);
+ if (RT_FAILURE(rc2))
+ {
+ /* Virtual terminal hint given? */
+ static char const s_szPrefix[] = "tty";
+ memcpy(szTTY, s_szPrefix, sizeof(s_szPrefix));
+ rc2 = RTEnvGetEx(RTENV_DEFAULT, "XDG_VTNR", &szTTY[sizeof(s_szPrefix) - 1], sizeof(s_szPrefix) - 1, NULL);
+ }
+
+ /** @todo Should we - distinguished from the login service - also set the hostname as PAM_TTY?
+ * The pam_access and pam_systemd talk about this. Similarly, SSH and cron use "ssh" and "cron" for PAM_TTY
+ * (see PAM_TTY_KLUDGE). */
+#ifdef IPRT_WITH_PAM_TTY_KLUDGE
+ if (RT_FAILURE(rc2))
+ if (!RTStrICmp(pszPamService, "access")) /* Access management needed? */
+ {
+ int err = gethostname(szTTY, sizeof(szTTY));
+ if (err == 0)
+ rc2 = VINF_SUCCESS;
+ }
+#endif
+ /* As a last resort, try stdin's TTY name instead (if any). */
+ if (RT_FAILURE(rc2))
+ {
+ rc2 = ttyname_r(0 /*stdin*/, szTTY, sizeof(szTTY));
+ if (rc2 != 0)
+ rc2 = RTErrConvertFromErrno(rc2);
+ }
+
+ LogRel2(("rtProcPosixAuthenticateUsingPam(%s): pam_setitem/PAM_TTY: %s, rc2=%Rrc\n", pszPamService, szTTY, rc2));
+ if (szTTY[0] == '\0')
+ LogRel2(("rtProcPosixAuthenticateUsingPam(%s): Hint: Looks like running as a non-interactive user (no TTY/PTY).\n"
+ "Authentication requiring a secure terminal might fail.\n", pszPamService));
+
+ if ( RT_SUCCESS(rc2)
+ && szTTY[0] != '\0') /* Only try using PAM_TTY if we have something to set. */
+ rc = pam_set_item(hPam, PAM_TTY, szTTY);
+
+ if (rc == PAM_SUCCESS)
+ {
+ /* From this point on we don't allow falling back to other auth methods. */
+ if (pfMayFallBack)
+ *pfMayFallBack = false;
+
+ rc = pam_authenticate(hPam, 0);
+ if (rc == PAM_SUCCESS)
+ {
+ rc = pam_acct_mgmt(hPam, 0);
+ if ( rc == PAM_SUCCESS
+ || rc == PAM_AUTHINFO_UNAVAIL /*??*/)
+ {
+ if ( ppapszEnv
+ && s_pfnPamGetEnvList
+ && s_pfnPamSetCred)
+ {
+ /* pam_env.so creates the environment when pam_setcred is called,. */
+ int rcSetCred = pam_setcred(hPam, PAM_ESTABLISH_CRED | PAM_SILENT);
+ /** @todo check pam_setcred status code? */
+
+ /* Unless it does it during session opening (Ubuntu 21.10). This
+ unfortunately means we might mount user dir and other crap: */
+ /** @todo do session handling properly */
+ int rcOpenSession = PAM_ABORT;
+ if ( s_pfnPamOpenSession
+ && s_pfnPamCloseSession)
+ rcOpenSession = pam_open_session(hPam, PAM_SILENT);
+
+ *ppapszEnv = pam_getenvlist(hPam);
+ LogFlowFunc(("pam_getenvlist -> %p ([0]=%p); rcSetCred=%d rcOpenSession=%d\n",
+ *ppapszEnv, *ppapszEnv ? **ppapszEnv : NULL, rcSetCred, rcOpenSession)); RT_NOREF(rcSetCred);
+
+ if (rcOpenSession == PAM_SUCCESS)
+ pam_close_session(hPam, PAM_SILENT);
+ pam_setcred(hPam, PAM_DELETE_CRED);
+ }
+
+ pam_end(hPam, PAM_SUCCESS);
+ LogFlowFunc(("pam auth (for %s) successful\n", pszPamService));
+ return VINF_SUCCESS;
+ }
+ LogFunc(("pam_acct_mgmt -> %d\n", rc));
+ }
+ else
+ LogFunc(("pam_authenticate -> %d\n", rc));
+ }
+ else
+ LogFunc(("pam_setitem/PAM_TTY -> %d\n", rc));
+ }
+ else
+ LogFunc(("pam_set_item/PAM_RUSER -> %d\n", rc));
+ pam_end(hPam, rc);
+ }
+ else
+ LogFunc(("pam_start(%s) -> %d\n", pszPamService, rc));
+
+ LogRel2(("rtProcPosixAuthenticateUsingPam(%s): Failed authenticating user '%s' with %d\n", pszPamService, pszUser, rc));
+ return VERR_AUTHENTICATION_FAILURE;
+}
+
+
+/**
+ * Checks if the given service file is present in any of the pam.d directories.
+ */
+static bool rtProcPosixPamServiceExists(const char *pszService)
+{
+ char szPath[256];
+
+ /* PAM_CONFIG_D: */
+ int rc = RTPathJoin(szPath, sizeof(szPath), "/etc/pam.d/", pszService); AssertRC(rc);
+ if (RTFileExists(szPath))
+ return true;
+
+ /* PAM_CONFIG_DIST_D: */
+ rc = RTPathJoin(szPath, sizeof(szPath), "/usr/lib/pam.d/", pszService); AssertRC(rc);
+ if (RTFileExists(szPath))
+ return true;
+
+ /* No support for PAM_CONFIG_DIST2_D. */
+ return false;
+}
+
+#endif /* IPRT_USE_PAM */
+
+
+#if defined(IPRT_WITH_DYNAMIC_CRYPT_R)
+/** Pointer to crypt_r(). */
+typedef char *(*PFNCRYPTR)(const char *, const char *, struct crypt_data *);
+
+/**
+ * Wrapper for resolving and calling crypt_r dynamically.
+ *
+ * The reason for this is that fedora 30+ wants to use libxcrypt rather than the
+ * glibc libcrypt. The two libraries has different crypt_data sizes and layout,
+ * so we allocate a 256KB data block to be on the safe size (caller does this).
+ */
+static char *rtProcDynamicCryptR(const char *pszKey, const char *pszSalt, struct crypt_data *pData)
+{
+ static PFNCRYPTR volatile s_pfnCryptR = NULL;
+ PFNCRYPTR pfnCryptR = s_pfnCryptR;
+ if (pfnCryptR)
+ return pfnCryptR(pszKey, pszSalt, pData);
+
+ pfnCryptR = (PFNCRYPTR)(uintptr_t)RTLdrGetSystemSymbolEx("libcrypt.so", "crypt_r", RTLDRLOAD_FLAGS_SO_VER_RANGE(1, 6));
+ if (!pfnCryptR)
+ pfnCryptR = (PFNCRYPTR)(uintptr_t)RTLdrGetSystemSymbolEx("libxcrypt.so", "crypt_r", RTLDRLOAD_FLAGS_SO_VER_RANGE(1, 32));
+ if (pfnCryptR)
+ {
+ s_pfnCryptR = pfnCryptR;
+ return pfnCryptR(pszKey, pszSalt, pData);
+ }
+
+ LogRel(("IPRT/RTProc: Unable to locate crypt_r!\n"));
+ return NULL;
+}
+#endif /* IPRT_WITH_DYNAMIC_CRYPT_R */
+
+
+/** Free the environment list returned by rtCheckCredentials. */
+static void rtProcPosixFreePamEnv(char **papszEnv)
+{
+ if (papszEnv)
+ {
+ for (size_t i = 0; papszEnv[i] != NULL; i++)
+ free(papszEnv[i]);
+ free(papszEnv);
+ }
+}
+
+
+/**
+ * Check the credentials and return the gid/uid of user.
+ *
+ * @param pszUser The username.
+ * @param pszPasswd The password to authenticate with.
+ * @param gid Where to store the GID of the user.
+ * @param uid Where to store the UID of the user.
+ * @param ppapszEnv Where to return PAM environment variables, NULL is fine
+ * if no variables to return. Call rtProcPosixFreePamEnv to
+ * free. Optional, so NULL can be passed in.
+ * @returns IPRT status code
+ */
+static int rtCheckCredentials(const char *pszUser, const char *pszPasswd, gid_t *pGid, uid_t *pUid, char ***ppapszEnv)
+{
+ Log(("rtCheckCredentials: pszUser=%s\n", pszUser));
+ int rc;
+
+ if (ppapszEnv)
+ *ppapszEnv = NULL;
+
+ /*
+ * Resolve user to UID and GID.
+ */
+ char achBuf[_4K];
+ struct passwd Pw;
+ struct passwd *pPw;
+ if (getpwnam_r(pszUser, &Pw, achBuf, sizeof(achBuf), &pPw) != 0)
+ return VERR_AUTHENTICATION_FAILURE;
+ if (!pPw)
+ return VERR_AUTHENTICATION_FAILURE;
+
+ *pUid = pPw->pw_uid;
+ *pGid = pPw->pw_gid;
+
+#ifdef IPRT_USE_PAM
+ /*
+ * Try authenticate using PAM, and falling back on crypto if allowed.
+ */
+ const char *pszService = "iprt-as-user";
+ if (!rtProcPosixPamServiceExists("iprt-as-user"))
+# ifdef IPRT_PAM_NATIVE_SERVICE_NAME_AS_USER
+ pszService = IPRT_PAM_NATIVE_SERVICE_NAME_AS_USER;
+# else
+ pszService = "login";
+# endif
+ bool fMayFallBack = false;
+ rc = rtProcPosixAuthenticateUsingPam(pszService, pszUser, pszPasswd, ppapszEnv, &fMayFallBack);
+ if (RT_SUCCESS(rc) || !fMayFallBack)
+ {
+ RTMemWipeThoroughly(achBuf, sizeof(achBuf), 3);
+ return rc;
+ }
+#endif
+
+#if !defined(IPRT_USE_PAM) || defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) || defined(RT_OS_OS2)
+# if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
+ /*
+ * Ditto for /etc/shadow and replace pw_passwd from above if we can access it:
+ *
+ * Note! On FreeBSD and OS/2 the root user will open /etc/shadow above, so
+ * this getspnam_r step is not necessary.
+ */
+ struct spwd ShwPwd;
+ char achBuf2[_4K];
+# if defined(RT_OS_LINUX)
+ struct spwd *pShwPwd = NULL;
+ if (getspnam_r(pszUser, &ShwPwd, achBuf2, sizeof(achBuf2), &pShwPwd) != 0)
+ pShwPwd = NULL;
+# else
+ struct spwd *pShwPwd = getspnam_r(pszUser, &ShwPwd, achBuf2, sizeof(achBuf2));
+# endif
+ if (pShwPwd != NULL)
+ pPw->pw_passwd = pShwPwd->sp_pwdp;
+# endif
+
+ /*
+ * Encrypt the passed in password and see if it matches.
+ */
+# if defined(RT_OS_LINUX)
+ /* Default fCorrect=true if no password specified. In that case, pPw->pw_passwd
+ must be NULL (no password set for this user). Fail if a password is specified
+ but the user does not have one assigned. */
+ rc = !pszPasswd || !*pszPasswd ? VINF_SUCCESS : VERR_AUTHENTICATION_FAILURE;
+ if (pPw->pw_passwd && *pPw->pw_passwd)
+# endif
+ {
+# if defined(RT_OS_LINUX) || defined(RT_OS_OS2)
+# ifdef IPRT_WITH_DYNAMIC_CRYPT_R
+ size_t const cbCryptData = RT_MAX(sizeof(struct crypt_data) * 2, _256K);
+# else
+ size_t const cbCryptData = sizeof(struct crypt_data);
+# endif
+ struct crypt_data *pCryptData = (struct crypt_data *)RTMemTmpAllocZ(cbCryptData);
+ if (pCryptData)
+ {
+# ifdef IPRT_WITH_DYNAMIC_CRYPT_R
+ char *pszEncPasswd = rtProcDynamicCryptR(pszPasswd, pPw->pw_passwd, pCryptData);
+# else
+ char *pszEncPasswd = crypt_r(pszPasswd, pPw->pw_passwd, pCryptData);
+# endif
+ rc = pszEncPasswd && !strcmp(pszEncPasswd, pPw->pw_passwd) ? VINF_SUCCESS : VERR_AUTHENTICATION_FAILURE;
+ RTMemWipeThoroughly(pCryptData, cbCryptData, 3);
+ RTMemTmpFree(pCryptData);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+# else
+ char *pszEncPasswd = crypt(pszPasswd, pPw->pw_passwd);
+ rc = strcmp(pszEncPasswd, pPw->pw_passwd) == 0 ? VINF_SUCCESS : VERR_AUTHENTICATION_FAILURE;
+# endif
+ }
+
+ /*
+ * Return GID and UID on success. Always wipe stack buffers.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ *pGid = pPw->pw_gid;
+ *pUid = pPw->pw_uid;
+ }
+# if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
+ RTMemWipeThoroughly(achBuf2, sizeof(achBuf2), 3);
+# endif
+#endif
+ RTMemWipeThoroughly(achBuf, sizeof(achBuf), 3);
+ return rc;
+}
+
+#ifdef RT_OS_SOLARIS
+
+/** @todo the error reporting of the Solaris process contract code could be
+ * a lot better, but essentially it is not meant to run into errors after
+ * the debugging phase. */
+static int rtSolarisContractPreFork(void)
+{
+ int templateFd = open64(CTFS_ROOT "/process/template", O_RDWR);
+ if (templateFd < 0)
+ return -1;
+
+ /* Set template parameters and event sets. */
+ if (ct_pr_tmpl_set_param(templateFd, CT_PR_PGRPONLY))
+ {
+ close(templateFd);
+ return -1;
+ }
+ if (ct_pr_tmpl_set_fatal(templateFd, CT_PR_EV_HWERR))
+ {
+ close(templateFd);
+ return -1;
+ }
+ if (ct_tmpl_set_critical(templateFd, 0))
+ {
+ close(templateFd);
+ return -1;
+ }
+ if (ct_tmpl_set_informative(templateFd, CT_PR_EV_HWERR))
+ {
+ close(templateFd);
+ return -1;
+ }
+
+ /* Make this the active template for the process. */
+ if (ct_tmpl_activate(templateFd))
+ {
+ close(templateFd);
+ return -1;
+ }
+
+ return templateFd;
+}
+
+static void rtSolarisContractPostForkChild(int templateFd)
+{
+ if (templateFd == -1)
+ return;
+
+ /* Clear the active template. */
+ ct_tmpl_clear(templateFd);
+ close(templateFd);
+}
+
+static void rtSolarisContractPostForkParent(int templateFd, pid_t pid)
+{
+ if (templateFd == -1)
+ return;
+
+ /* Clear the active template. */
+ int cleared = ct_tmpl_clear(templateFd);
+ close(templateFd);
+
+ /* If the clearing failed or the fork failed there's nothing more to do. */
+ if (cleared || pid <= 0)
+ return;
+
+ /* Look up the contract which was created by this thread. */
+ int statFd = open64(CTFS_ROOT "/process/latest", O_RDONLY);
+ if (statFd == -1)
+ return;
+ ct_stathdl_t statHdl;
+ if (ct_status_read(statFd, CTD_COMMON, &statHdl))
+ {
+ close(statFd);
+ return;
+ }
+ ctid_t ctId = ct_status_get_id(statHdl);
+ ct_status_free(statHdl);
+ close(statFd);
+ if (ctId < 0)
+ return;
+
+ /* Abandon this contract we just created. */
+ char ctlPath[PATH_MAX];
+ size_t len = snprintf(ctlPath, sizeof(ctlPath),
+ CTFS_ROOT "/process/%ld/ctl", (long)ctId);
+ if (len >= sizeof(ctlPath))
+ return;
+ int ctlFd = open64(ctlPath, O_WRONLY);
+ if (statFd == -1)
+ return;
+ if (ct_ctl_abandon(ctlFd) < 0)
+ {
+ close(ctlFd);
+ return;
+ }
+ close(ctlFd);
+}
+
+#endif /* RT_OS_SOLARIS */
+
+
+RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
+{
+ return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
+ NULL, NULL, NULL, /* standard handles */
+ NULL /*pszAsUser*/, NULL /* pszPassword*/, NULL /*pvExtraData*/,
+ pProcess);
+}
+
+
+/**
+ * Adjust the profile environment after forking the child process and changing
+ * the UID.
+ *
+ * @returns IRPT status code.
+ * @param hEnvToUse The environment we're going to use with execve.
+ * @param fFlags The process creation flags.
+ * @param hEnv The environment passed in by the user.
+ */
+static int rtProcPosixAdjustProfileEnvFromChild(RTENV hEnvToUse, uint32_t fFlags, RTENV hEnv)
+{
+ int rc = VINF_SUCCESS;
+#ifdef RT_OS_DARWIN
+ if ( RT_SUCCESS(rc)
+ && (!(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || RTEnvExistEx(hEnv, "TMPDIR")) )
+ {
+ char szValue[RTPATH_MAX];
+ size_t cbNeeded = confstr(_CS_DARWIN_USER_TEMP_DIR, szValue, sizeof(szValue));
+ if (cbNeeded > 0 && cbNeeded < sizeof(szValue))
+ {
+ char *pszTmp;
+ rc = RTStrCurrentCPToUtf8(&pszTmp, szValue);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTEnvSetEx(hEnvToUse, "TMPDIR", pszTmp);
+ RTStrFree(pszTmp);
+ }
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+#else
+ RT_NOREF_PV(hEnvToUse); RT_NOREF_PV(fFlags); RT_NOREF_PV(hEnv);
+#endif
+ return rc;
+}
+
+
+/**
+ * Undos quoting and escape sequences and looks for stop characters.
+ *
+ * @returns Where to continue scanning in @a pszString. This points to the
+ * next character after the stop character, but for the zero terminator
+ * it points to the terminator character.
+ * @param pszString The string to undo quoting and escaping for.
+ * This is both input and output as the work is
+ * done in place.
+ * @param pfStoppedOnEqual Where to return whether we stopped work on a
+ * plain equal characater or not. If this is NULL,
+ * then the equal character is not a stop
+ * character, then only newline and the zero
+ * terminator are.
+ */
+static char *rtProcPosixProfileEnvUnquoteAndUnescapeString(char *pszString, bool *pfStoppedOnEqual)
+{
+ if (pfStoppedOnEqual)
+ *pfStoppedOnEqual = false;
+
+ enum { kPlain, kSingleQ, kDoubleQ } enmState = kPlain;
+ char *pszDst = pszString;
+ for (;;)
+ {
+ char ch = *pszString++;
+ switch (ch)
+ {
+ default:
+ *pszDst++ = ch;
+ break;
+
+ case '\\':
+ {
+ char ch2;
+ if ( enmState == kSingleQ
+ || (ch2 = *pszString) == '\0'
+ || (enmState == kDoubleQ && strchr("\\$`\"\n", ch2) == NULL) )
+ *pszDst++ = ch;
+ else
+ {
+ *pszDst++ = ch2;
+ pszString++;
+ }
+ break;
+ }
+
+ case '"':
+ if (enmState == kSingleQ)
+ *pszDst++ = ch;
+ else
+ enmState = enmState == kPlain ? kDoubleQ : kPlain;
+ break;
+
+ case '\'':
+ if (enmState == kDoubleQ)
+ *pszDst++ = ch;
+ else
+ enmState = enmState == kPlain ? kSingleQ : kPlain;
+ break;
+
+ case '\n':
+ if (enmState == kPlain)
+ {
+ *pszDst = '\0';
+ return pszString;
+ }
+ *pszDst++ = ch;
+ break;
+
+ case '=':
+ if (enmState == kPlain && pfStoppedOnEqual)
+ {
+ *pszDst = '\0';
+ *pfStoppedOnEqual = true;
+ return pszString;
+ }
+ *pszDst++ = ch;
+ break;
+
+ case '\0':
+ Assert(enmState == kPlain);
+ *pszDst = '\0';
+ return pszString - 1;
+ }
+ }
+}
+
+
+/**
+ * Worker for rtProcPosixProfileEnvRunAndHarvest that parses the environment
+ * dump and loads it into hEnvToUse.
+ *
+ * @note This isn't entirely correct should any of the profile setup scripts
+ * unset any of the environment variables in the basic initial
+ * enviornment, but since that's unlikely and it's very convenient to
+ * have something half sensible as a basis if don't don't grok the dump
+ * entirely and would skip central stuff like PATH or HOME.
+ *
+ * @returns IPRT status code.
+ * @retval -VERR_PARSE_ERROR (positive, e.g. warning) if we run into trouble.
+ * @retval -VERR_INVALID_UTF8_ENCODING (positive, e.g. warning) if there are
+ * invalid UTF-8 in the environment. This isn't unlikely if the
+ * profile doesn't use UTF-8. This is unfortunately not something we
+ * can guess to accurately up front, so we don't do any guessing and
+ * hope everyone is sensible and use UTF-8.
+ *
+ * @param hEnvToUse The basic environment to extend with what we manage
+ * to parse here.
+ * @param pszEnvDump The environment dump to parse. Nominally in Bourne
+ * shell 'export -p' format.
+ * @param fWithMarkers Whether there are markers around the dump (C shell,
+ * tmux) or not.
+ */
+static int rtProcPosixProfileEnvHarvest(RTENV hEnvToUse, char *pszEnvDump, bool fWithMarkers)
+{
+ LogRel3(("**** pszEnvDump start ****\n%s**** pszEnvDump end ****\n", pszEnvDump));
+ if (!LogIs3Enabled())
+ LogFunc(("**** pszEnvDump start ****\n%s**** pszEnvDump end ****\n", pszEnvDump));
+
+ /*
+ * Clip dump at markers if we're using them (C shell).
+ */
+ if (fWithMarkers)
+ {
+ char *pszStart = strstr(pszEnvDump, g_szEnvMarkerBegin);
+ AssertReturn(pszStart, -VERR_PARSE_ERROR);
+ pszStart += sizeof(g_szEnvMarkerBegin) - 1;
+ if (*pszStart == '\n')
+ pszStart++;
+ pszEnvDump = pszStart;
+
+ char *pszEnd = strstr(pszStart, g_szEnvMarkerEnd);
+ AssertReturn(pszEnd, -VERR_PARSE_ERROR);
+ *pszEnd = '\0';
+ }
+
+ /*
+ * Since we're using /bin/sh -c "export -p" for all the dumping, we should
+ * always get lines on the format:
+ * export VAR1="Value 1"
+ * export VAR2=Value2
+ *
+ * However, just in case something goes wrong, like bash doesn't think it
+ * needs to be posixly correct, try deal with the alternative where
+ * "declare -x " replaces the "export".
+ */
+ const char *pszPrefix;
+ if ( strncmp(pszEnvDump, RT_STR_TUPLE("export")) == 0
+ && RT_C_IS_BLANK(pszEnvDump[6]))
+ pszPrefix = "export ";
+ else if ( strncmp(pszEnvDump, RT_STR_TUPLE("declare")) == 0
+ && RT_C_IS_BLANK(pszEnvDump[7])
+ && pszEnvDump[8] == '-')
+ pszPrefix = "declare -x "; /* We only need to care about the non-array, non-function lines. */
+ else
+ AssertFailedReturn(-VERR_PARSE_ERROR);
+ size_t const cchPrefix = strlen(pszPrefix);
+
+ /*
+ * Process the lines, ignoring stuff which we don't grok.
+ * The shell should quote problematic characters. Bash double quotes stuff
+ * by default, whereas almquist's shell does it as needed and only the value
+ * side.
+ */
+ int rc = VINF_SUCCESS;
+ while (pszEnvDump && *pszEnvDump != '\0')
+ {
+ /*
+ * Skip the prefixing command.
+ */
+ if ( cchPrefix == 0
+ || strncmp(pszEnvDump, pszPrefix, cchPrefix) == 0)
+ {
+ pszEnvDump += cchPrefix;
+ while (RT_C_IS_BLANK(*pszEnvDump))
+ pszEnvDump++;
+ }
+ else
+ {
+ /* Oops, must find our bearings for some reason... */
+ pszEnvDump = strchr(pszEnvDump, '\n');
+ rc = -VERR_PARSE_ERROR;
+ continue;
+ }
+
+ /*
+ * Parse out the variable name using typical bourne shell escaping
+ * and quoting rules.
+ */
+ /** @todo We should throw away lines that aren't propertly quoted, now we
+ * just continue and use what we found. */
+ const char *pszVar = pszEnvDump;
+ bool fStoppedOnPlainEqual = false;
+ pszEnvDump = rtProcPosixProfileEnvUnquoteAndUnescapeString(pszEnvDump, &fStoppedOnPlainEqual);
+ const char *pszValue = pszEnvDump;
+ if (fStoppedOnPlainEqual)
+ pszEnvDump = rtProcPosixProfileEnvUnquoteAndUnescapeString(pszEnvDump, NULL /*pfStoppedOnPlainEqual*/);
+ else
+ pszValue = "";
+
+ /*
+ * Add them if valid UTF-8, otherwise we simply drop them for now.
+ * The whole codeset stuff goes seriously wonky here as the environment
+ * we're harvesting probably contains it's own LC_CTYPE or LANG variables,
+ * so ignore the problem for now.
+ */
+ if ( RTStrIsValidEncoding(pszVar)
+ && RTStrIsValidEncoding(pszValue))
+ {
+ int rc2 = RTEnvSetEx(hEnvToUse, pszVar, pszValue);
+ AssertRCReturn(rc2, rc2);
+ }
+ else if (rc == VINF_SUCCESS)
+ rc = -VERR_INVALID_UTF8_ENCODING;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Runs the user's shell in login mode with some environment dumping logic and
+ * harvests the dump, putting it into hEnvToUse.
+ *
+ * This is a bit hairy, esp. with regards to codesets.
+ *
+ * @returns IPRT status code. Not all error statuses will be returned and the
+ * caller should just continue with whatever is in hEnvToUse.
+ *
+ * @param hEnvToUse On input this is the basic user environment, on success
+ * in is fleshed out with stuff from the login shell dump.
+ * @param pszAsUser The user name for the profile.
+ * @param uid The UID corrsponding to @a pszAsUser, ~0 if current user.
+ * @param gid The GID corrsponding to @a pszAsUser, ~0 if current user.
+ * @param pszShell The login shell. This is a writable string to avoid
+ * needing to make a copy of it when examining the path
+ * part, instead we make a temporary change to it which is
+ * always reverted before returning.
+ */
+static int rtProcPosixProfileEnvRunAndHarvest(RTENV hEnvToUse, const char *pszAsUser, uid_t uid, gid_t gid, char *pszShell)
+{
+ LogFlowFunc(("pszAsUser=%s uid=%u gid=%u pszShell=%s; hEnvToUse contains %u variables on entry\n",
+ pszAsUser, uid, gid, pszShell, RTEnvCountEx(hEnvToUse) ));
+
+ /*
+ * The three standard handles should be pointed to /dev/null, the 3rd handle
+ * is used to dump the environment.
+ */
+ RTPIPE hPipeR, hPipeW;
+ int rc = RTPipeCreate(&hPipeR, &hPipeW, 0);
+ if (RT_SUCCESS(rc))
+ {
+ RTFILE hFileNull;
+ rc = RTFileOpenBitBucket(&hFileNull, RTFILE_O_READWRITE);
+ if (RT_SUCCESS(rc))
+ {
+ int aRedirFds[4];
+ aRedirFds[0] = aRedirFds[1] = aRedirFds[2] = RTFileToNative(hFileNull);
+ aRedirFds[3] = RTPipeToNative(hPipeW);
+
+ /*
+ * Allocate a buffer for receiving the environment dump.
+ *
+ * This is fixed sized for simplicity and safety (creative user script
+ * shouldn't be allowed to exhaust our memory or such, after all we're
+ * most likely running with root privileges in this code path).
+ */
+ size_t const cbEnvDump = _64K;
+ char * const pszEnvDump = (char *)RTMemTmpAllocZ(cbEnvDump);
+ if (pszEnvDump)
+ {
+ /*
+ * Our default approach is using /bin/sh:
+ */
+ const char *pszExec = _PATH_BSHELL;
+ const char *apszArgs[8];
+ apszArgs[0] = "-sh"; /* First arg must start with a dash for login shells. */
+ apszArgs[1] = "-c";
+ apszArgs[2] = "POSIXLY_CORRECT=1;export -p >&3";
+ apszArgs[3] = NULL;
+
+ /*
+ * But see if we can trust the shell to be a real usable shell.
+ * This would be great as different shell typically has different profile setup
+ * files and we'll endup with the wrong enviornment if we use a different shell.
+ */
+ char szDashShell[32];
+ char szExportArg[128];
+ bool fWithMarkers = false;
+ const char *pszShellNm = RTPathFilename(pszShell);
+ if ( pszShellNm
+ && access(pszShellNm, X_OK))
+ {
+ /*
+ * First the check that it's a known bin directory:
+ */
+ size_t const cchShellPath = pszShellNm - pszShell;
+ char const chSaved = pszShell[cchShellPath - 1];
+ pszShell[cchShellPath - 1] = '\0';
+ if ( RTPathCompare(pszShell, "/bin") == 0
+ || RTPathCompare(pszShell, "/usr/bin") == 0
+ || RTPathCompare(pszShell, "/usr/local/bin") == 0)
+ {
+ /*
+ * Then see if we recognize the shell name.
+ */
+ RTStrCopy(&szDashShell[1], sizeof(szDashShell) - 1, pszShellNm);
+ szDashShell[0] = '-';
+ if ( strcmp(pszShellNm, "bash") == 0
+ || strcmp(pszShellNm, "ksh") == 0
+ || strcmp(pszShellNm, "ksh93") == 0
+ || strcmp(pszShellNm, "zsh") == 0
+ || strcmp(pszShellNm, "fish") == 0)
+ {
+ pszExec = pszShell;
+ apszArgs[0] = szDashShell;
+
+ /* Use /bin/sh for doing the environment dumping so we get the same kind
+ of output from everyone and can limit our parsing + testing efforts. */
+ RTStrPrintf(szExportArg, sizeof(szExportArg),
+ "%s -c 'POSIXLY_CORRECT=1;export -p >&3'", _PATH_BSHELL);
+ apszArgs[2] = szExportArg;
+ }
+ /* C shell is very annoying in that it closes fd 3 without regard to what
+ we might have put there, so we must use stdout here but with markers so
+ we can find the dump.
+ Seems tmux have similar issues as it doesn't work above, but works fine here. */
+ else if ( strcmp(pszShellNm, "csh") == 0
+ || strcmp(pszShellNm, "tcsh") == 0
+ || strcmp(pszShellNm, "tmux") == 0)
+ {
+ pszExec = pszShell;
+ apszArgs[0] = szDashShell;
+
+ fWithMarkers = true;
+ size_t cch = RTStrPrintf(szExportArg, sizeof(szExportArg),
+ "%s -c 'set -e;POSIXLY_CORRECT=1;echo %s;export -p;echo %s'",
+ _PATH_BSHELL, g_szEnvMarkerBegin, g_szEnvMarkerEnd);
+ Assert(cch < sizeof(szExportArg) - 1); RT_NOREF(cch);
+ apszArgs[2] = szExportArg;
+
+ aRedirFds[1] = aRedirFds[3];
+ aRedirFds[3] = -1;
+ }
+ }
+ pszShell[cchShellPath - 1] = chSaved;
+ }
+
+ /*
+ * Create the process and wait for the output.
+ */
+ LogFunc(("Executing '%s': '%s', '%s', '%s'\n", pszExec, apszArgs[0], apszArgs[1], apszArgs[2]));
+ RTPROCESS hProcess = NIL_RTPROCESS;
+ rc = rtProcPosixCreateInner(pszExec, apszArgs, hEnvToUse, hEnvToUse, 0 /*fFlags*/,
+ pszAsUser, uid, gid, RT_ELEMENTS(aRedirFds), aRedirFds, &hProcess);
+ if (RT_SUCCESS(rc))
+ {
+ RTPipeClose(hPipeW);
+ hPipeW = NIL_RTPIPE;
+
+ size_t offEnvDump = 0;
+ uint64_t const msStart = RTTimeMilliTS();
+ for (;;)
+ {
+ size_t cbRead = 0;
+ if (offEnvDump < cbEnvDump - 1)
+ {
+ rc = RTPipeRead(hPipeR, &pszEnvDump[offEnvDump], cbEnvDump - 1 - offEnvDump, &cbRead);
+ if (RT_SUCCESS(rc))
+ offEnvDump += cbRead;
+ else
+ {
+ LogFlowFunc(("Breaking out of read loop: %Rrc\n", rc));
+ if (rc == VERR_BROKEN_PIPE)
+ rc = VINF_SUCCESS;
+ break;
+ }
+ pszEnvDump[offEnvDump] = '\0';
+ }
+ else
+ {
+ LogFunc(("Too much data in environment dump, dropping it\n"));
+ rc = VERR_TOO_MUCH_DATA;
+ break;
+ }
+
+ /* Do the timout check. */
+ uint64_t const cMsElapsed = RTTimeMilliTS() - msStart;
+ if (cMsElapsed >= RT_MS_15SEC)
+ {
+ LogFunc(("Timed out after %RU64 ms\n", cMsElapsed));
+ rc = VERR_TIMEOUT;
+ break;
+ }
+
+ /* If we got no data in above wait for more to become ready. */
+ if (!cbRead)
+ RTPipeSelectOne(hPipeR, RT_MS_15SEC - cMsElapsed);
+ }
+
+ /*
+ * Kill the process and wait for it to avoid leaving zombies behind.
+ */
+ /** @todo do we check the exit code? */
+ int rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, NULL);
+ if (RT_SUCCESS(rc2))
+ LogFlowFunc(("First RTProcWait succeeded\n"));
+ else
+ {
+ LogFunc(("First RTProcWait failed (%Rrc), terminating and doing a blocking wait\n", rc2));
+ RTProcTerminate(hProcess);
+ RTProcWait(hProcess, RTPROCWAIT_FLAGS_BLOCK, NULL);
+ }
+
+ /*
+ * Parse the result.
+ */
+ if (RT_SUCCESS(rc))
+ rc = rtProcPosixProfileEnvHarvest(hEnvToUse, pszEnvDump, fWithMarkers);
+ else
+ {
+ LogFunc(("Ignoring rc=%Rrc from the pipe read loop and continues with basic environment\n", rc));
+ rc = -rc;
+ }
+ }
+ else
+ LogFunc(("Failed to create process '%s': %Rrc\n", pszExec, rc));
+ RTMemTmpFree(pszEnvDump);
+ }
+ else
+ {
+ LogFunc(("Failed to allocate %#zx bytes for the dump\n", cbEnvDump));
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ RTFileClose(hFileNull);
+ }
+ else
+ LogFunc(("Failed to open /dev/null: %Rrc\n", rc));
+ RTPipeClose(hPipeR);
+ RTPipeClose(hPipeW);
+ }
+ else
+ LogFunc(("Failed to create pipe: %Rrc\n", rc));
+ LogFlowFunc(("returns %Rrc (hEnvToUse contains %u variables now)\n", rc, RTEnvCountEx(hEnvToUse)));
+ return rc;
+}
+
+
+/**
+ * Create an environment for the given user.
+ *
+ * This starts by creating a very basic environment and then tries to do it
+ * properly by running the user's shell in login mode with some environment
+ * dumping attached. The latter may fail and we'll ignore that for now and move
+ * ahead with the very basic environment.
+ *
+ * @returns IPRT status code.
+ * @param phEnvToUse Where to return the created environment.
+ * @param pszAsUser The user name for the profile. NULL if the current
+ * user.
+ * @param uid The UID corrsponding to @a pszAsUser, ~0 if NULL.
+ * @param gid The GID corrsponding to @a pszAsUser, ~0 if NULL.
+ * @param fFlags RTPROC_FLAGS_XXX
+ * @param papszPamEnv Array of environment variables returned by PAM, if
+ * it was used for authentication and produced anything.
+ * Otherwise NULL.
+ */
+static int rtProcPosixCreateProfileEnv(PRTENV phEnvToUse, const char *pszAsUser, uid_t uid, gid_t gid,
+ uint32_t fFlags, char **papszPamEnv)
+{
+ /*
+ * Get the passwd entry for the user.
+ */
+ struct passwd Pwd;
+ struct passwd *pPwd = NULL;
+ char achBuf[_4K];
+ int rc;
+ errno = 0;
+ if (pszAsUser)
+ rc = getpwnam_r(pszAsUser, &Pwd, achBuf, sizeof(achBuf), &pPwd);
+ else
+ rc = getpwuid_r(getuid(), &Pwd, achBuf, sizeof(achBuf), &pPwd);
+ if (rc == 0 && pPwd)
+ {
+ /*
+ * Convert stuff to UTF-8 since the environment is UTF-8.
+ */
+ char *pszDir;
+ rc = RTStrCurrentCPToUtf8(&pszDir, pPwd->pw_dir);
+ if (RT_SUCCESS(rc))
+ {
+#if 0 /* Enable and modify this to test shells other that your login shell. */
+ pPwd->pw_shell = (char *)"/bin/tmux";
+#endif
+ char *pszShell;
+ rc = RTStrCurrentCPToUtf8(&pszShell, pPwd->pw_shell);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszAsUserFree = NULL;
+ if (!pszAsUser)
+ {
+ rc = RTStrCurrentCPToUtf8(&pszAsUserFree, pPwd->pw_name);
+ if (RT_SUCCESS(rc))
+ pszAsUser = pszAsUserFree;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create and populate the environment.
+ */
+ rc = RTEnvCreate(phEnvToUse);
+ if (RT_SUCCESS(rc))
+ {
+ RTENV hEnvToUse = *phEnvToUse;
+ rc = RTEnvSetEx(hEnvToUse, "HOME", pszDir);
+ if (RT_SUCCESS(rc))
+ rc = RTEnvSetEx(hEnvToUse, "SHELL", pszShell);
+ if (RT_SUCCESS(rc))
+ rc = RTEnvSetEx(hEnvToUse, "USER", pszAsUser);
+ if (RT_SUCCESS(rc))
+ rc = RTEnvSetEx(hEnvToUse, "LOGNAME", pszAsUser);
+ if (RT_SUCCESS(rc))
+ rc = RTEnvSetEx(hEnvToUse, "PATH", pPwd->pw_uid == 0 ? _PATH_STDPATH : _PATH_DEFPATH);
+ char szTmpPath[RTPATH_MAX];
+ if (RT_SUCCESS(rc))
+ {
+ RTStrPrintf(szTmpPath, sizeof(szTmpPath), "%s/%s", _PATH_MAILDIR, pszAsUser);
+ rc = RTEnvSetEx(hEnvToUse, "MAIL", szTmpPath);
+ }
+#ifdef RT_OS_DARWIN
+ if (RT_SUCCESS(rc))
+ {
+ /* TMPDIR is some unique per user directory under /var/folders on darwin,
+ so get the one for the current user. If we're launching the process as
+ a different user, rtProcPosixAdjustProfileEnvFromChild will update it
+ again for the actual child process user (provided we set it here). See
+ https://opensource.apple.com/source/Libc/Libc-997.1.1/darwin/_dirhelper.c
+ for the implementation of this query. */
+ size_t cbNeeded = confstr(_CS_DARWIN_USER_TEMP_DIR, szTmpPath, sizeof(szTmpPath));
+ if (cbNeeded > 0 && cbNeeded < sizeof(szTmpPath))
+ {
+ char *pszTmp;
+ rc = RTStrCurrentCPToUtf8(&pszTmp, szTmpPath);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTEnvSetEx(hEnvToUse, "TMPDIR", pszTmp);
+ RTStrFree(pszTmp);
+ }
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+#endif
+ /*
+ * Add everything from the PAM environment.
+ */
+ if (RT_SUCCESS(rc) && papszPamEnv != NULL)
+ for (size_t i = 0; papszPamEnv[i] != NULL && RT_SUCCESS(rc); i++)
+ {
+ char *pszEnvVar;
+ rc = RTStrCurrentCPToUtf8(&pszEnvVar, papszPamEnv[i]);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszValue = strchr(pszEnvVar, '=');
+ if (pszValue)
+ *pszValue++ = '\0';
+ rc = RTEnvSetEx(hEnvToUse, pszEnvVar, pszValue ? pszValue : "");
+ RTStrFree(pszEnvVar);
+ }
+ /* Ignore conversion issue, though LogRel them. */
+ else if (rc != VERR_NO_STR_MEMORY && rc != VERR_NO_MEMORY)
+ {
+ LogRelMax(256, ("RTStrCurrentCPToUtf8(,%.*Rhxs) -> %Rrc\n", strlen(pszEnvVar), pszEnvVar, rc));
+ rc = -rc;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Now comes the fun part where we need to try run a shell in login mode
+ * and harvest its final environment to get the proper environment for
+ * the user. We ignore some failures here so buggy login scrips and
+ * other weird stuff won't trip us up too badly.
+ */
+ if (!(fFlags & RTPROC_FLAGS_ONLY_BASIC_PROFILE))
+ rc = rtProcPosixProfileEnvRunAndHarvest(hEnvToUse, pszAsUser, uid, gid, pszShell);
+ }
+
+ if (RT_FAILURE(rc))
+ RTEnvDestroy(hEnvToUse);
+ }
+ RTStrFree(pszAsUserFree);
+ }
+ RTStrFree(pszShell);
+ }
+ RTStrFree(pszDir);
+ }
+ }
+ else
+ rc = errno ? RTErrConvertFromErrno(errno) : VERR_ACCESS_DENIED;
+ return rc;
+}
+
+
+/**
+ * Converts the arguments to the child's LC_CTYPE charset if necessary.
+ *
+ * @returns IPRT status code.
+ * @param papszArgs The arguments (UTF-8).
+ * @param hEnvToUse The child process environment.
+ * @param ppapszArgs Where to return the converted arguments. The array
+ * entries must be freed by RTStrFree and the array itself
+ * by RTMemFree.
+ */
+static int rtProcPosixConvertArgv(const char * const *papszArgs, RTENV hEnvToUse, char ***ppapszArgs)
+{
+ *ppapszArgs = (char **)papszArgs;
+
+ /*
+ * The first thing we need to do here is to try guess the codeset of the
+ * child process and check if it's UTF-8 or not.
+ */
+ const char *pszEncoding;
+ char szEncoding[512];
+ if (hEnvToUse == RTENV_DEFAULT)
+ {
+ /* Same environment as us, assume setlocale is up to date: */
+ pszEncoding = rtStrGetLocaleCodeset();
+ }
+ else
+ {
+ /*
+ * LC_ALL overrides everything else. The LC_* environment variables are often set
+ * to the empty string so move on the next variable if that is the case (that's
+ * what setlocale in glibc does).
+ */
+ const char *pszVar;
+ int rc = RTEnvGetEx(hEnvToUse, pszVar = "LC_ALL", szEncoding, sizeof(szEncoding), NULL);
+ if (rc == VERR_ENV_VAR_NOT_FOUND || (RT_SUCCESS(rc) && szEncoding[0] == '\0'))
+ rc = RTEnvGetEx(hEnvToUse, pszVar = "LC_CTYPE", szEncoding, sizeof(szEncoding), NULL);
+ if (rc == VERR_ENV_VAR_NOT_FOUND || (RT_SUCCESS(rc) && szEncoding[0] == '\0'))
+ rc = RTEnvGetEx(hEnvToUse, pszVar = "LANG", szEncoding, sizeof(szEncoding), NULL);
+ if (RT_SUCCESS(rc) && szEncoding[0] != '\0')
+ {
+ /*
+ * LC_ALL can contain a composite locale consisting of the locales of each of the
+ * categories in two different formats depending on the OS. On Solaris, macOS, and
+ * *BSD composite locale names use slash ('/') as the separator and the following
+ * order for the categories:
+ * LC_CTYPE/LC_NUMERIC/LC_TIME/LC_COLLATE/LC_MONETARY/LC_MESSAGES
+ * e.g.:
+ * en_US.UTF-8/POSIX/el_GR.UTF-8/el_CY.UTF-8/en_GB.UTF-8/es_ES.UTF-8
+ *
+ * On Solaris there is also a leading slash.
+ *
+ * On Linux and OS/2 the composite locale format is made up of key-value pairs
+ * of category names and locales of the form 'name=value' with each element
+ * separated by a semicolon in the same order as above with following additional
+ * categories included as well:
+ * LC_PAPER/LC_NAME/LC_ADDRESS/LC_TELEPHONE/LC_MEASUREMENT/LC_IDENTIFICATION
+ * e.g.
+ * LC_CTYPE=fr_BE;LC_NUMERIC=fr_BE@euro;LC_TIME=fr_BE.utf8;LC_COLLATE=fr_CA;\
+ * LC_MONETARY=fr_CA.utf8;LC_MESSAGES=fr_CH;LC_PAPER=fr_CH.utf8;LC_NAME=fr_FR;\
+ * LC_ADDRESS=fr_FR.utf8;LC_TELEPHONE=fr_LU;LC_MEASUREMENT=fr_LU@euro;\
+ * LC_IDENTIFICATION=fr_LU.utf8
+ */
+ char *pszEncodingStart = szEncoding;
+#if !defined(RT_OS_LINUX) && !defined(RT_OS_OS2)
+ if (*pszEncodingStart == '/')
+ pszEncodingStart++;
+ char *pszSlash = strchr(pszEncodingStart, '/');
+ if (pszSlash)
+ *pszSlash = '\0'; /* This ASSUMES the first one is LC_CTYPE! */
+#else
+ char *pszCType = strstr(pszEncodingStart, "LC_CTYPE=");
+ if (pszCType)
+ {
+ pszEncodingStart = pszCType + sizeof("LC_CTYPE=") - 1;
+
+ char *pszSemiColon = strchr(pszEncodingStart, ';');
+ if (pszSemiColon)
+ *pszSemiColon = '\0';
+ }
+#endif
+
+ /*
+ * Use newlocale and nl_langinfo_l to determine the default codeset for the locale
+ * specified in the child's environment. These routines have been around since
+ * ancient days on Linux and for quite a long time on macOS, Solaris, and *BSD but
+ * to ensure their availability check that LC_CTYPE_MASK is defined.
+ *
+ * Note! The macOS nl_langinfo(3)/nl_langinfo_l(3) routines return a pointer to an
+ * empty string for "short" locale names like en_NZ, it_IT, el_GR, etc. so use
+ * UTF-8 in those cases as it is the default for short name locales on macOS
+ * (see also rtStrGetLocaleCodeset).
+ */
+#ifdef LC_CTYPE_MASK
+ locale_t hLocale = newlocale(LC_CTYPE_MASK, pszEncodingStart, (locale_t)0);
+ if (hLocale != (locale_t)0)
+ {
+ const char *pszCodeset = nl_langinfo_l(CODESET, hLocale);
+ Log2Func(("nl_langinfo_l(CODESET, %s=%s) -> %s\n", pszVar, pszEncodingStart, pszCodeset));
+ if (!pszCodeset || *pszCodeset == '\0')
+# ifdef RT_OS_DARWIN
+ pszEncoding = "UTF-8";
+# else
+ pszEncoding = "ASCII";
+# endif
+ else
+ {
+ rc = RTStrCopy(szEncoding, sizeof(szEncoding), pszCodeset);
+ AssertRC(rc); /* cannot possibly overflow */
+ }
+
+ freelocale(hLocale);
+ pszEncoding = szEncoding;
+ }
+ else
+#endif
+ {
+ /* If there is something that ought to be a character set encoding, try use it: */
+ const char *pszDot = strchr(pszEncodingStart, '.');
+ if (pszDot)
+ pszDot = RTStrStripL(pszDot + 1);
+ if (pszDot && *pszDot != '\0')
+ {
+ pszEncoding = pszDot;
+ Log2Func(("%s=%s -> %s (simple)\n", pszVar, szEncoding, pszEncoding));
+ }
+ else
+ {
+ /* This is mostly wrong, but I cannot think of anything better now: */
+ pszEncoding = rtStrGetLocaleCodeset();
+ LogFunc(("No newlocale or it failed (on '%s=%s', errno=%d), falling back on %s that we're using...\n",
+ pszVar, pszEncodingStart, errno, pszEncoding));
+ }
+ }
+ RT_NOREF_PV(pszVar);
+ }
+ else
+#ifdef RT_OS_DARWIN /* @bugref{10153}: Darwin defaults to UTF-8. */
+ pszEncoding = "UTF-8";
+#else
+ pszEncoding = "ASCII";
+#endif
+ }
+
+ /*
+ * Do nothing if it's UTF-8.
+ */
+ if (rtStrIsCodesetUtf8(pszEncoding))
+ {
+ LogFlowFunc(("No conversion needed (%s)\n", pszEncoding));
+ return VINF_SUCCESS;
+ }
+
+
+ /*
+ * Do the conversion.
+ */
+ size_t cArgs = 0;
+ while (papszArgs[cArgs] != NULL)
+ cArgs++;
+ LogFunc(("Converting #%u arguments to %s...\n", cArgs, pszEncoding));
+
+ char **papszArgsConverted = (char **)RTMemAllocZ(sizeof(papszArgsConverted[0]) * (cArgs + 2));
+ AssertReturn(papszArgsConverted, VERR_NO_MEMORY);
+
+ void *pvConversionCache = NULL;
+ rtStrLocalCacheInit(&pvConversionCache);
+ for (size_t i = 0; i < cArgs; i++)
+ {
+ int rc = rtStrLocalCacheConvert(papszArgs[i], strlen(papszArgs[i]), "UTF-8",
+ &papszArgsConverted[i], 0, pszEncoding, &pvConversionCache);
+ if (RT_SUCCESS(rc) && rc != VWRN_NO_TRANSLATION)
+ { /* likely */ }
+ else
+ {
+ LogRelMax(100, ("Failed to convert argument #%u '%s' to '%s': %Rrc\n", i, papszArgs[i], pszEncoding, rc));
+ while (i-- > 0)
+ RTStrFree(papszArgsConverted[i]);
+ RTMemFree(papszArgsConverted);
+ rtStrLocalCacheDelete(&pvConversionCache);
+ return rc == VWRN_NO_TRANSLATION || rc == VERR_NO_TRANSLATION ? VERR_PROC_NO_ARG_TRANSLATION : rc;
+ }
+ }
+
+ rtStrLocalCacheDelete(&pvConversionCache);
+ *ppapszArgs = papszArgsConverted;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * The result structure for rtPathFindExec/RTPathTraverseList.
+ * @todo move to common path code?
+ */
+typedef struct RTPATHINTSEARCH
+{
+ /** For EACCES or EPERM errors that we continued on.
+ * @note Must be initialized to VINF_SUCCESS. */
+ int rcSticky;
+ /** Buffer containing the filename. */
+ char szFound[RTPATH_MAX];
+} RTPATHINTSEARCH;
+/** Pointer to a rtPathFindExec/RTPathTraverseList result. */
+typedef RTPATHINTSEARCH *PRTPATHINTSEARCH;
+
+
+/**
+ * RTPathTraverseList callback used by RTProcCreateEx to locate the executable.
+ */
+static DECLCALLBACK(int) rtPathFindExec(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
+{
+ const char *pszExec = (const char *)pvUser1;
+ PRTPATHINTSEARCH pResult = (PRTPATHINTSEARCH)pvUser2;
+ int rc = RTPathJoinEx(pResult->szFound, sizeof(pResult->szFound), pchPath, cchPath, pszExec, RTSTR_MAX,
+ RTPATH_STR_F_STYLE_HOST);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszNativeExec = NULL;
+ rc = rtPathToNative(&pszNativeExec, pResult->szFound, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (!access(pszNativeExec, X_OK))
+ rc = VINF_SUCCESS;
+ else
+ {
+ if ( errno == EACCES
+ || errno == EPERM)
+ pResult->rcSticky = RTErrConvertFromErrno(errno);
+ rc = VERR_TRY_AGAIN;
+ }
+ rtPathFreeNative(pszNativeExec, pResult->szFound);
+ }
+ else
+ AssertRCStmt(rc, rc = VERR_TRY_AGAIN /* don't stop on this, whatever it is */);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
+ PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
+ const char *pszPassword, void *pvExtraData, PRTPROCESS phProcess)
+{
+ int rc;
+ LogFlow(("RTProcCreateEx: pszExec=%s pszAsUser=%s fFlags=%#x phStdIn=%p phStdOut=%p phStdErr=%p\n",
+ pszExec, pszAsUser, fFlags, phStdIn, phStdOut, phStdErr));
+
+ /*
+ * Input validation
+ */
+ AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
+ AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
+ AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
+ AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
+ AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
+#if defined(RT_OS_OS2)
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ return VERR_PROC_DETACH_NOT_SUPPORTED;
+#endif
+ AssertReturn(pvExtraData == NULL || (fFlags & RTPROC_FLAGS_DESIRED_SESSION_ID), VERR_INVALID_PARAMETER);
+
+ /*
+ * Get the file descriptors for the handles we've been passed.
+ */
+ PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
+ int aStdFds[3] = { -1, -1, -1 };
+ for (int i = 0; i < 3; i++)
+ {
+ if (paHandles[i])
+ {
+ AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
+ switch (paHandles[i]->enmType)
+ {
+ case RTHANDLETYPE_FILE:
+ aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE
+ ? (int)RTFileToNative(paHandles[i]->u.hFile)
+ : -2 /* close it */;
+ break;
+
+ case RTHANDLETYPE_PIPE:
+ aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
+ ? (int)RTPipeToNative(paHandles[i]->u.hPipe)
+ : -2 /* close it */;
+ break;
+
+ case RTHANDLETYPE_SOCKET:
+ aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
+ ? (int)RTSocketToNative(paHandles[i]->u.hSocket)
+ : -2 /* close it */;
+ break;
+
+ default:
+ AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
+ }
+ /** @todo check the close-on-execness of these handles? */
+ }
+ }
+
+ for (int i = 0; i < 3; i++)
+ if (aStdFds[i] == i)
+ aStdFds[i] = -1;
+ LogFlowFunc(("aStdFds={%d, %d, %d}\n", aStdFds[0], aStdFds[1], aStdFds[2]));
+
+ for (int i = 0; i < 3; i++)
+ AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,
+ ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),
+ VERR_NOT_SUPPORTED);
+
+ /*
+ * Validate the credentials if a user is specified.
+ */
+ bool const fNeedLoginEnv = (fFlags & RTPROC_FLAGS_PROFILE)
+ && ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT);
+ uid_t uid = ~(uid_t)0;
+ gid_t gid = ~(gid_t)0;
+ char **papszPamEnv = NULL;
+ if (pszAsUser)
+ {
+ rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid, fNeedLoginEnv ? &papszPamEnv : NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+#ifdef IPRT_USE_PAM
+ /*
+ * User unchanged, but if PROFILE is request we must try get the PAM
+ * environmnet variables.
+ *
+ * For this to work, we'll need a special PAM service profile which doesn't
+ * actually do any authentication, only concerns itself with the enviornment
+ * setup. gdm-launch-environment is such one, and we use it if we haven't
+ * got an IPRT specific one there.
+ */
+ else if (fNeedLoginEnv)
+ {
+ const char *pszService;
+ if (rtProcPosixPamServiceExists("iprt-environment"))
+ pszService = "iprt-environment";
+# ifdef IPRT_PAM_NATIVE_SERVICE_NAME_ENVIRONMENT
+ else if (rtProcPosixPamServiceExists(IPRT_PAM_NATIVE_SERVICE_NAME_ENVIRONMENT))
+ pszService = IPRT_PAM_NATIVE_SERVICE_NAME_ENVIRONMENT;
+# endif
+ else if (rtProcPosixPamServiceExists("gdm-launch-environment"))
+ pszService = "gdm-launch-environment";
+ else
+ pszService = NULL;
+ if (pszService)
+ {
+ char szLoginName[512];
+ rc = getlogin_r(szLoginName, sizeof(szLoginName));
+ if (rc == 0)
+ rc = rtProcPosixAuthenticateUsingPam(pszService, szLoginName, "xxx", &papszPamEnv, NULL);
+ }
+ }
+#endif
+
+ /*
+ * Create the child environment if either RTPROC_FLAGS_PROFILE or
+ * RTPROC_FLAGS_ENV_CHANGE_RECORD are in effect.
+ */
+ RTENV hEnvToUse = hEnv;
+ if ( (fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_PROFILE))
+ && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
+ || hEnv == RTENV_DEFAULT) )
+ {
+ if (fFlags & RTPROC_FLAGS_PROFILE)
+ rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser, uid, gid, fFlags, papszPamEnv);
+ else
+ rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT);
+ rtProcPosixFreePamEnv(papszPamEnv);
+ papszPamEnv = NULL;
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT)
+ {
+ rc = RTEnvApplyChanges(hEnvToUse, hEnv);
+ if (RT_FAILURE(rc))
+ {
+ RTEnvDestroy(hEnvToUse);
+ return rc;
+ }
+ }
+ }
+ Assert(papszPamEnv == NULL);
+
+ /*
+ * Check for execute access to the file, searching the PATH if needed.
+ */
+ const char *pszNativeExec = NULL;
+ rc = rtPathToNative(&pszNativeExec, pszExec, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (access(pszNativeExec, X_OK) == 0)
+ rc = VINF_SUCCESS;
+ else
+ {
+ rc = errno;
+ rtPathFreeNative(pszNativeExec, pszExec);
+
+ if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH)
+ || rc != ENOENT
+ || RTPathHavePath(pszExec) )
+ rc = RTErrConvertFromErrno(rc);
+ else
+ {
+ /* Search the PATH for it: */
+ char *pszPath = RTEnvDupEx(hEnvToUse, "PATH");
+ if (pszPath)
+ {
+ PRTPATHINTSEARCH pResult = (PRTPATHINTSEARCH)alloca(sizeof(*pResult));
+ pResult->rcSticky = VINF_SUCCESS;
+ rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, pResult);
+ RTStrFree(pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ /* Found it. Now, convert to native path: */
+ pszExec = pResult->szFound;
+ rc = rtPathToNative(&pszNativeExec, pszExec, NULL);
+ }
+ else
+ rc = rc != VERR_END_OF_STRING ? rc
+ : pResult->rcSticky == VINF_SUCCESS ? VERR_FILE_NOT_FOUND : pResult->rcSticky;
+ }
+ else
+ rc = VERR_NO_STR_MEMORY;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Convert arguments to child codeset if necessary.
+ */
+ char **papszArgsConverted = (char **)papszArgs;
+ if (!(fFlags & RTPROC_FLAGS_UTF8_ARGV))
+ rc = rtProcPosixConvertArgv(papszArgs, hEnvToUse, &papszArgsConverted);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * The rest of the process creation is reused internally by rtProcPosixCreateProfileEnv.
+ */
+ rc = rtProcPosixCreateInner(pszNativeExec, papszArgsConverted, hEnv, hEnvToUse, fFlags, pszAsUser, uid, gid,
+ RT_ELEMENTS(aStdFds), aStdFds, phProcess);
+
+ }
+
+ /* Free the translated argv copy, if needed. */
+ if (papszArgsConverted != (char **)papszArgs)
+ {
+ for (size_t i = 0; papszArgsConverted[i] != NULL; i++)
+ RTStrFree(papszArgsConverted[i]);
+ RTMemFree(papszArgsConverted);
+ }
+ rtPathFreeNative(pszNativeExec, pszExec);
+ }
+ }
+ if (hEnvToUse != hEnv)
+ RTEnvDestroy(hEnvToUse);
+ return rc;
+}
+
+
+/**
+ * The inner 2nd half of RTProcCreateEx.
+ *
+ * This is also used by rtProcPosixCreateProfileEnv().
+ *
+ * @returns IPRT status code.
+ * @param pszNativeExec The executable to run (absolute path, X_OK).
+ * Native path.
+ * @param papszArgs The arguments. Caller has done codeset conversions.
+ * @param hEnv The original enviornment request, needed for
+ * adjustments if starting as different user.
+ * @param hEnvToUse The environment we should use.
+ * @param fFlags The process creation flags, RTPROC_FLAGS_XXX.
+ * @param pszAsUser The user to start the process as, if requested.
+ * @param uid The UID corrsponding to @a pszAsUser, ~0 if NULL.
+ * @param gid The GID corrsponding to @a pszAsUser, ~0 if NULL.
+ * @param cRedirFds Number of redirection file descriptors.
+ * @param paRedirFds Pointer to redirection file descriptors. Entries
+ * containing -1 are not modified (inherit from parent),
+ * -2 indicates that the descriptor should be closed in the
+ * child.
+ * @param phProcess Where to return the process ID on success.
+ */
+static int rtProcPosixCreateInner(const char *pszNativeExec, const char * const *papszArgs, RTENV hEnv, RTENV hEnvToUse,
+ uint32_t fFlags, const char *pszAsUser, uid_t uid, gid_t gid,
+ unsigned cRedirFds, int *paRedirFds, PRTPROCESS phProcess)
+{
+ /*
+ * Get the environment block.
+ */
+ const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse);
+ AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);
+
+ /*
+ * Optimize the redirections.
+ */
+ while (cRedirFds > 0 && paRedirFds[cRedirFds - 1] == -1)
+ cRedirFds--;
+
+ /*
+ * Child PID.
+ */
+ pid_t pid = -1;
+
+ /*
+ * Take care of detaching the process.
+ *
+ * HACK ALERT! Put the process into a new process group with pgid = pid
+ * to make sure it differs from that of the parent process to ensure that
+ * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide
+ * waits. setsid() includes the setpgid() functionality.
+ * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt.
+ */
+#ifndef RT_OS_OS2
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ {
+# ifdef RT_OS_SOLARIS
+ int templateFd = -1;
+ if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
+ {
+ templateFd = rtSolarisContractPreFork();
+ if (templateFd == -1)
+ return VERR_OPEN_FAILED;
+ }
+# endif /* RT_OS_SOLARIS */
+ pid = fork();
+ if (!pid)
+ {
+# ifdef RT_OS_SOLARIS
+ if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
+ rtSolarisContractPostForkChild(templateFd);
+# endif
+ setsid(); /* see comment above */
+
+ pid = -1;
+ /* Child falls through to the actual spawn code below. */
+ }
+ else
+ {
+# ifdef RT_OS_SOLARIS
+ if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
+ rtSolarisContractPostForkParent(templateFd, pid);
+# endif
+ if (pid > 0)
+ {
+ /* Must wait for the temporary process to avoid a zombie. */
+ int status = 0;
+ pid_t pidChild = 0;
+
+ /* Restart if we get interrupted. */
+ do
+ {
+ pidChild = waitpid(pid, &status, 0);
+ } while ( pidChild == -1
+ && errno == EINTR);
+
+ /* Assume that something wasn't found. No detailed info. */
+ if (status)
+ return VERR_PROCESS_NOT_FOUND;
+ if (phProcess)
+ *phProcess = 0;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+ }
+ }
+#endif
+
+ /*
+ * Spawn the child.
+ *
+ * Any spawn code MUST not execute any atexit functions if it is for a
+ * detached process. It would lead to running the atexit functions which
+ * make only sense for the parent. libORBit e.g. gets confused by multiple
+ * execution. Remember, there was only a fork() so far, and until exec()
+ * is successfully run there is nothing which would prevent doing anything
+ * silly with the (duplicated) file descriptors.
+ */
+ int rc;
+#ifdef HAVE_POSIX_SPAWN
+ /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */
+ if ( uid == ~(uid_t)0
+ && gid == ~(gid_t)0)
+ {
+ /* Spawn attributes. */
+ posix_spawnattr_t Attr;
+ rc = posix_spawnattr_init(&Attr);
+ if (!rc)
+ {
+ /* Indicate that process group and signal mask are to be changed,
+ and that the child should use default signal actions. */
+ rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF);
+ Assert(rc == 0);
+
+ /* The child starts in its own process group. */
+ if (!rc)
+ {
+ rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
+ Assert(rc == 0);
+ }
+
+ /* Unmask all signals. */
+ if (!rc)
+ {
+ sigset_t SigMask;
+ sigemptyset(&SigMask);
+ rc = posix_spawnattr_setsigmask(&Attr, &SigMask); Assert(rc == 0);
+ }
+
+ /* File changes. */
+ posix_spawn_file_actions_t FileActions;
+ posix_spawn_file_actions_t *pFileActions = NULL;
+ if (!rc && cRedirFds > 0)
+ {
+ rc = posix_spawn_file_actions_init(&FileActions);
+ if (!rc)
+ {
+ pFileActions = &FileActions;
+ for (unsigned i = 0; i < cRedirFds; i++)
+ {
+ int fd = paRedirFds[i];
+ if (fd == -2)
+ rc = posix_spawn_file_actions_addclose(&FileActions, i);
+ else if (fd >= 0 && fd != (int)i)
+ {
+ rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
+ if (!rc)
+ {
+ for (unsigned j = i + 1; j < cRedirFds; j++)
+ if (paRedirFds[j] == fd)
+ {
+ fd = -1;
+ break;
+ }
+ if (fd >= 0)
+ rc = posix_spawn_file_actions_addclose(&FileActions, fd);
+ }
+ }
+ if (rc)
+ break;
+ }
+ }
+ }
+
+ if (!rc)
+ rc = posix_spawn(&pid, pszNativeExec, pFileActions, &Attr, (char * const *)papszArgs,
+ (char * const *)papszEnv);
+
+ /* cleanup */
+ int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
+ if (pFileActions)
+ {
+ rc2 = posix_spawn_file_actions_destroy(pFileActions);
+ Assert(rc2 == 0);
+ }
+
+ /* return on success.*/
+ if (!rc)
+ {
+ /* For a detached process this happens in the temp process, so
+ * it's not worth doing anything as this process must exit. */
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ _Exit(0);
+ if (phProcess)
+ *phProcess = pid;
+ return VINF_SUCCESS;
+ }
+ }
+ /* For a detached process this happens in the temp process, so
+ * it's not worth doing anything as this process must exit. */
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ _Exit(124);
+ }
+ else
+#endif
+ {
+#ifdef RT_OS_SOLARIS
+ int templateFd = -1;
+ if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
+ {
+ templateFd = rtSolarisContractPreFork();
+ if (templateFd == -1)
+ return VERR_OPEN_FAILED;
+ }
+#endif /* RT_OS_SOLARIS */
+ pid = fork();
+ if (!pid)
+ {
+#ifdef RT_OS_SOLARIS
+ if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
+ rtSolarisContractPostForkChild(templateFd);
+#endif /* RT_OS_SOLARIS */
+ if (!(fFlags & RTPROC_FLAGS_DETACHED))
+ setpgid(0, 0); /* see comment above */
+
+ /*
+ * Change group and user if requested.
+ */
+#if 1 /** @todo This needs more work, see suplib/hardening. */
+ if (pszAsUser)
+ {
+ int ret = initgroups(pszAsUser, gid);
+ if (ret)
+ {
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ _Exit(126);
+ else
+ exit(126);
+ }
+ }
+ if (gid != ~(gid_t)0)
+ {
+ if (setgid(gid))
+ {
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ _Exit(126);
+ else
+ exit(126);
+ }
+ }
+
+ if (uid != ~(uid_t)0)
+ {
+ if (setuid(uid))
+ {
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ _Exit(126);
+ else
+ exit(126);
+ }
+ }
+#endif
+
+ /*
+ * Some final profile environment tweaks, if running as user.
+ */
+ if ( (fFlags & RTPROC_FLAGS_PROFILE)
+ && pszAsUser
+ && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
+ || hEnv == RTENV_DEFAULT) )
+ {
+ rc = rtProcPosixAdjustProfileEnvFromChild(hEnvToUse, fFlags, hEnv);
+ papszEnv = RTEnvGetExecEnvP(hEnvToUse);
+ if (RT_FAILURE(rc) || !papszEnv)
+ {
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ _Exit(126);
+ else
+ exit(126);
+ }
+ }
+
+ /*
+ * Unset the signal mask.
+ */
+ sigset_t SigMask;
+ sigemptyset(&SigMask);
+ rc = sigprocmask(SIG_SETMASK, &SigMask, NULL);
+ Assert(rc == 0);
+
+ /*
+ * Apply changes to the standard file descriptor and stuff.
+ */
+ for (unsigned i = 0; i < cRedirFds; i++)
+ {
+ int fd = paRedirFds[i];
+ if (fd == -2)
+ close(i);
+ else if (fd >= 0)
+ {
+ int rc2 = dup2(fd, i);
+ if (rc2 != (int)i)
+ {
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ _Exit(125);
+ else
+ exit(125);
+ }
+ for (unsigned j = i + 1; j < cRedirFds; j++)
+ if (paRedirFds[j] == fd)
+ {
+ fd = -1;
+ break;
+ }
+ if (fd >= 0)
+ close(fd);
+ }
+ }
+
+ /*
+ * Finally, execute the requested program.
+ */
+ rc = execve(pszNativeExec, (char * const *)papszArgs, (char * const *)papszEnv);
+ if (errno == ENOEXEC)
+ {
+ /* This can happen when trying to start a shell script without the magic #!/bin/sh */
+ RTAssertMsg2Weak("Cannot execute this binary format!\n");
+ }
+ else
+ RTAssertMsg2Weak("execve returns %d errno=%d (%s)\n", rc, errno, pszNativeExec);
+ RTAssertReleasePanic();
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ _Exit(127);
+ else
+ exit(127);
+ }
+#ifdef RT_OS_SOLARIS
+ if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
+ rtSolarisContractPostForkParent(templateFd, pid);
+#endif /* RT_OS_SOLARIS */
+ if (pid > 0)
+ {
+ /* For a detached process this happens in the temp process, so
+ * it's not worth doing anything as this process must exit. */
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ _Exit(0);
+ if (phProcess)
+ *phProcess = pid;
+ return VINF_SUCCESS;
+ }
+ /* For a detached process this happens in the temp process, so
+ * it's not worth doing anything as this process must exit. */
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ _Exit(124);
+ return RTErrConvertFromErrno(errno);
+ }
+
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)
+{
+ /*
+ * Fork the child process in a new session and quit the parent.
+ *
+ * - fork once and create a new session (setsid). This will detach us
+ * from the controlling tty meaning that we won't receive the SIGHUP
+ * (or any other signal) sent to that session.
+ * - The SIGHUP signal is ignored because the session/parent may throw
+ * us one before we get to the setsid.
+ * - When the parent exit(0) we will become an orphan and re-parented to
+ * the init process.
+ * - Because of the sometimes unexpected semantics of assigning the
+ * controlling tty automagically when a session leader first opens a tty,
+ * we will fork() once more to get rid of the session leadership role.
+ */
+
+ /* We start off by opening the pidfile, so that we can fail straight away
+ * if it already exists. */
+ int fdPidfile = -1;
+ if (pszPidfile != NULL)
+ {
+ /* @note the exclusive create is not guaranteed on all file
+ * systems (e.g. NFSv2) */
+ if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
+ return RTErrConvertFromErrno(errno);
+ }
+
+ /* Ignore SIGHUP straight away. */
+ struct sigaction OldSigAct;
+ struct sigaction SigAct;
+ memset(&SigAct, 0, sizeof(SigAct));
+ SigAct.sa_handler = SIG_IGN;
+ int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
+
+ /* First fork, to become independent process. */
+ pid_t pid = fork();
+ if (pid == -1)
+ {
+ if (fdPidfile != -1)
+ close(fdPidfile);
+ return RTErrConvertFromErrno(errno);
+ }
+ if (pid != 0)
+ {
+ /* Parent exits, no longer necessary. The child gets reparented
+ * to the init process. */
+ exit(0);
+ }
+
+ /* Create new session, fix up the standard file descriptors and the
+ * current working directory. */
+ /** @todo r=klaus the webservice uses this function and assumes that the
+ * contract id of the daemon is the same as that of the original process.
+ * Whenever this code is changed this must still remain possible. */
+ pid_t newpgid = setsid();
+ int SavedErrno = errno;
+ if (rcSigAct != -1)
+ sigaction(SIGHUP, &OldSigAct, NULL);
+ if (newpgid == -1)
+ {
+ if (fdPidfile != -1)
+ close(fdPidfile);
+ return RTErrConvertFromErrno(SavedErrno);
+ }
+
+ if (!fNoClose)
+ {
+ /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
+ int fd = open("/dev/null", O_RDWR);
+ if (fd == -1) /* paranoia */
+ {
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ fd = open("/dev/null", O_RDWR);
+ }
+ if (fd != -1)
+ {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ close(fd);
+ }
+ }
+
+ if (!fNoChDir)
+ {
+ int rcIgnored = chdir("/");
+ NOREF(rcIgnored);
+ }
+
+ /* Second fork to lose session leader status. */
+ pid = fork();
+ if (pid == -1)
+ {
+ if (fdPidfile != -1)
+ close(fdPidfile);
+ return RTErrConvertFromErrno(errno);
+ }
+
+ if (pid != 0)
+ {
+ /* Write the pid file, this is done in the parent, before exiting. */
+ if (fdPidfile != -1)
+ {
+ char szBuf[256];
+ size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
+ ssize_t cbIgnored = write(fdPidfile, szBuf, cbPid); NOREF(cbIgnored);
+ close(fdPidfile);
+ }
+ exit(0);
+ }
+
+ if (fdPidfile != -1)
+ close(fdPidfile);
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/process-posix.cpp b/src/VBox/Runtime/r3/posix/process-posix.cpp
new file mode 100644
index 00000000..b0074f1c
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/process-posix.cpp
@@ -0,0 +1,279 @@
+/* $Id: process-posix.cpp $ */
+/** @file
+ * IPRT - Process, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <pwd.h>
+
+#include <iprt/process.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/pipe.h>
+#include <iprt/socket.h>
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include "internal/process.h"
+
+
+RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
+{
+ int rc;
+ do rc = RTProcWaitNoResume(Process, fFlags, pProcStatus);
+ while (rc == VERR_INTERRUPTED);
+ return rc;
+}
+
+
+RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
+{
+ /*
+ * Validate input.
+ */
+ if (Process <= 0)
+ {
+ AssertMsgFailed(("Invalid Process=%d\n", Process));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (fFlags & ~(RTPROCWAIT_FLAGS_NOBLOCK | RTPROCWAIT_FLAGS_BLOCK))
+ {
+ AssertMsgFailed(("Invalid flags %#x\n", fFlags));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Perform the wait.
+ */
+ int iStatus = 0;
+ int rc = waitpid(Process, &iStatus, fFlags & RTPROCWAIT_FLAGS_NOBLOCK ? WNOHANG : 0);
+ if (rc > 0)
+ {
+ /*
+ * Fill in the status structure.
+ */
+ if (pProcStatus)
+ {
+ if (WIFEXITED(iStatus))
+ {
+ pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
+ pProcStatus->iStatus = WEXITSTATUS(iStatus);
+ }
+ else if (WIFSIGNALED(iStatus))
+ {
+ pProcStatus->enmReason = RTPROCEXITREASON_SIGNAL;
+ pProcStatus->iStatus = WTERMSIG(iStatus);
+ }
+ else
+ {
+ Assert(!WIFSTOPPED(iStatus));
+ pProcStatus->enmReason = RTPROCEXITREASON_ABEND;
+ pProcStatus->iStatus = iStatus;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Child running?
+ */
+ if (!rc)
+ {
+ Assert(fFlags & RTPROCWAIT_FLAGS_NOBLOCK);
+ return VERR_PROCESS_RUNNING;
+ }
+
+ /*
+ * Figure out which error to return.
+ */
+ int iErr = errno;
+ if (iErr == ECHILD)
+ return VERR_PROCESS_NOT_FOUND;
+ return RTErrConvertFromErrno(iErr);
+}
+
+
+RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
+{
+ if (Process == NIL_RTPROCESS)
+ return VINF_SUCCESS;
+
+ if (!kill(Process, SIGKILL))
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
+{
+ /// @todo
+ return 1;
+}
+
+
+RTR3DECL(int) RTProcQueryParent(RTPROCESS hProcess, PRTPROCESS phParent)
+{
+ if (hProcess == RTProcSelf())
+ {
+ *phParent = getppid();
+ return VINF_SUCCESS;
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTR3DECL(int) RTProcQueryUsername(RTPROCESS hProcess, char *pszUser, size_t cbUser, size_t *pcbUser)
+{
+ AssertReturn( (pszUser && cbUser > 0)
+ || (!pszUser && !cbUser), VERR_INVALID_PARAMETER);
+ AssertReturn(pcbUser || pszUser, VERR_INVALID_PARAMETER);
+
+ int rc;
+ if ( hProcess == NIL_RTPROCESS
+ || hProcess == RTProcSelf())
+ {
+ /*
+ * Figure a good buffer estimate.
+ */
+ int32_t cbPwdMax = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (cbPwdMax <= _1K)
+ cbPwdMax = _1K;
+ else
+ AssertStmt(cbPwdMax <= 32*_1M, cbPwdMax = 32*_1M);
+ char *pchBuf = (char *)RTMemTmpAllocZ(cbPwdMax);
+ if (pchBuf)
+ {
+ /*
+ * Get the password file entry.
+ */
+ struct passwd Pwd;
+ struct passwd *pPwd = NULL;
+ rc = getpwuid_r(geteuid(), &Pwd, pchBuf, cbPwdMax, &pPwd);
+ if (!rc)
+ {
+ /*
+ * Convert the name to UTF-8, assuming that we're getting it in the local codeset.
+ */
+ /** @todo This isn't exactly optimal... the current codeset/page conversion
+ * stuff never was. Should optimize that for UTF-8 and ASCII one day.
+ * And also optimize for avoiding heap. */
+ char *pszTmp = NULL;
+ rc = RTStrCurrentCPToUtf8(&pszTmp, pPwd->pw_name);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbTmp = strlen(pszTmp) + 1;
+ if (pcbUser)
+ *pcbUser = cbTmp;
+ if (cbTmp <= cbUser)
+ {
+ memcpy(pszUser, pszTmp, cbTmp);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ RTStrFree(pszTmp);
+ }
+ }
+ else
+ rc = RTErrConvertFromErrno(rc);
+ RTMemFree(pchBuf);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
+
+RTR3DECL(int) RTProcQueryUsernameA(RTPROCESS hProcess, char **ppszUser)
+{
+ AssertPtrReturn(ppszUser, VERR_INVALID_POINTER);
+
+ int rc;
+ if ( hProcess == NIL_RTPROCESS
+ || hProcess == RTProcSelf())
+ {
+ /*
+ * Figure a good buffer estimate.
+ */
+ int32_t cbPwdMax = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (cbPwdMax <= _1K)
+ cbPwdMax = _1K;
+ else
+ AssertStmt(cbPwdMax <= 32*_1M, cbPwdMax = 32*_1M);
+ char *pchBuf = (char *)RTMemTmpAllocZ(cbPwdMax);
+ if (pchBuf)
+ {
+ /*
+ * Get the password file entry.
+ */
+ struct passwd Pwd;
+ struct passwd *pPwd = NULL;
+ rc = getpwuid_r(geteuid(), &Pwd, pchBuf, cbPwdMax, &pPwd);
+ if (!rc)
+ {
+ /*
+ * Convert the name to UTF-8, assuming that we're getting it in the local codeset.
+ */
+ rc = RTStrCurrentCPToUtf8(ppszUser, pPwd->pw_name);
+ }
+ else
+ rc = RTErrConvertFromErrno(rc);
+ RTMemFree(pchBuf);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/rand-posix.cpp b/src/VBox/Runtime/r3/posix/rand-posix.cpp
new file mode 100644
index 00000000..191e3db8
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/rand-posix.cpp
@@ -0,0 +1,148 @@
+/* $Id: rand-posix.cpp $ */
+/** @file
+ * IPRT - Random Numbers and Byte Streams, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#ifdef _MSC_VER
+# include <io.h>
+# include <stdio.h>
+#else
+# include <unistd.h>
+# include <sys/time.h>
+#endif
+
+#include <iprt/rand.h>
+#include <iprt/mem.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include "internal/rand.h"
+#include "internal/magics.h"
+
+
+
+/** @copydoc RTRANDINT::pfnGetBytes */
+static DECLCALLBACK(void) rtRandAdvPosixGetBytes(PRTRANDINT pThis, uint8_t *pb, size_t cb)
+{
+ ssize_t cbRead = read(pThis->u.File.hFile, pb, cb);
+ if ((size_t)cbRead != cb)
+ {
+ /* S10 has been observed returning 1040 bytes at the time from /dev/urandom.
+ Which means we need to do than 256 rounds to reach 668171 bytes if
+ that's what demanded by the caller (like tstRTMemWipe.cpp). */
+ ssize_t cTries = RT_MAX(256, cb / 64);
+ do
+ {
+ if (cbRead > 0)
+ {
+ cb -= cbRead;
+ pb += cbRead;
+ }
+ cbRead = read(pThis->u.File.hFile, pb, cb);
+ } while ( (size_t)cbRead != cb
+ && cTries-- > 0);
+ AssertReleaseMsg((size_t)cbRead == cb, ("%zu != %zu, cTries=%zd errno=%d\n", cbRead, cb, cTries, errno));
+ }
+}
+
+
+/** @copydoc RTRANDINT::pfnDestroy */
+static DECLCALLBACK(int) rtRandAdvPosixDestroy(PRTRANDINT pThis)
+{
+ pThis->u32Magic = ~RTRANDINT_MAGIC;
+ int fd = pThis->u.File.hFile;
+ pThis->u.File.hFile = -1;
+ RTMemFree(pThis);
+ close(fd);
+ return VINF_SUCCESS;
+}
+
+
+static int rtRandAdvPosixCreateSystem(PRTRAND phRand, const char *pszDev) RT_NO_THROW_DEF
+{
+ /*
+ * Try open it first and then setup the handle structure.
+ */
+ int fd = open(pszDev, O_RDONLY);
+ if (fd < 0)
+ return RTErrConvertFromErrno(errno);
+ int rc;
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) != -1)
+ {
+ PRTRANDINT pThis = (PRTRANDINT)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTRANDINT_MAGIC;
+ pThis->pfnGetBytes = rtRandAdvPosixGetBytes;
+ pThis->pfnGetU32 = rtRandAdvSynthesizeU32FromBytes;
+ pThis->pfnGetU64 = rtRandAdvSynthesizeU64FromBytes;
+ pThis->pfnSeed = rtRandAdvStubSeed;
+ pThis->pfnSaveState = rtRandAdvStubSaveState;
+ pThis->pfnRestoreState = rtRandAdvStubRestoreState;
+ pThis->pfnDestroy = rtRandAdvPosixDestroy;
+ pThis->u.File.hFile = fd;
+
+ *phRand = pThis;
+ return VINF_SUCCESS;
+ }
+
+ /* bail out */
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ close(fd);
+ return rc;
+}
+
+
+RTDECL(int) RTRandAdvCreateSystemFaster(PRTRAND phRand) RT_NO_THROW_DEF
+{
+ return rtRandAdvPosixCreateSystem(phRand, "/dev/urandom");
+}
+
+
+RTDECL(int) RTRandAdvCreateSystemTruer(PRTRAND phRand) RT_NO_THROW_DEF
+{
+ return rtRandAdvPosixCreateSystem(phRand, "/dev/random");
+}
+
diff --git a/src/VBox/Runtime/r3/posix/rtmempage-exec-mmap-heap-posix.cpp b/src/VBox/Runtime/r3/posix/rtmempage-exec-mmap-heap-posix.cpp
new file mode 100644
index 00000000..2bb60178
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/rtmempage-exec-mmap-heap-posix.cpp
@@ -0,0 +1,797 @@
+/* $Id: rtmempage-exec-mmap-heap-posix.cpp $ */
+/** @file
+ * IPRT - RTMemPage*, POSIX with heap.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/avl.h>
+#include <iprt/critsect.h>
+#include <iprt/errcore.h>
+#include <iprt/once.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include "internal/mem.h"
+#include "../alloc-ef.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Threshold at which to we switch to simply calling mmap. */
+#define RTMEMPAGEPOSIX_MMAP_THRESHOLD _128K
+/** The size of a heap block (power of two) - in bytes. */
+#define RTMEMPAGEPOSIX_BLOCK_SIZE _2M
+AssertCompile(RTMEMPAGEPOSIX_BLOCK_SIZE == (RTMEMPAGEPOSIX_BLOCK_SIZE / PAGE_SIZE) * PAGE_SIZE);
+/** The number of pages per heap block. */
+#define RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT (RTMEMPAGEPOSIX_BLOCK_SIZE / PAGE_SIZE)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to a page heap block. */
+typedef struct RTHEAPPAGEBLOCK *PRTHEAPPAGEBLOCK;
+
+/**
+ * A simple page heap.
+ */
+typedef struct RTHEAPPAGE
+{
+ /** Magic number (RTHEAPPAGE_MAGIC). */
+ uint32_t u32Magic;
+ /** The number of pages in the heap (in BlockTree). */
+ uint32_t cHeapPages;
+ /** The number of currently free pages. */
+ uint32_t cFreePages;
+ /** Number of successful calls. */
+ uint32_t cAllocCalls;
+ /** Number of successful free calls. */
+ uint32_t cFreeCalls;
+ /** The free call number at which we last tried to minimize the heap. */
+ uint32_t uLastMinimizeCall;
+ /** Tree of heap blocks. */
+ AVLRPVTREE BlockTree;
+ /** Allocation hint no 1 (last freed). */
+ PRTHEAPPAGEBLOCK pHint1;
+ /** Allocation hint no 2 (last alloc). */
+ PRTHEAPPAGEBLOCK pHint2;
+ /** Critical section protecting the heap. */
+ RTCRITSECT CritSect;
+ /** Set if the memory must allocated with execute access. */
+ bool fExec;
+} RTHEAPPAGE;
+#define RTHEAPPAGE_MAGIC UINT32_C(0xfeedface)
+/** Pointer to a page heap. */
+typedef RTHEAPPAGE *PRTHEAPPAGE;
+
+
+/**
+ * Describes a page heap block.
+ */
+typedef struct RTHEAPPAGEBLOCK
+{
+ /** The AVL tree node core (void pointer range). */
+ AVLRPVNODECORE Core;
+ /** Allocation bitmap. Set bits marks allocated pages. */
+ uint32_t bmAlloc[RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT / 32];
+ /** Allocation boundrary bitmap. Set bits marks the start of
+ * allocations. */
+ uint32_t bmFirst[RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT / 32];
+ /** The number of free pages. */
+ uint32_t cFreePages;
+ /** Pointer back to the heap. */
+ PRTHEAPPAGE pHeap;
+} RTHEAPPAGEBLOCK;
+
+
+/**
+ * Argument package for rtHeapPageAllocCallback.
+ */
+typedef struct RTHEAPPAGEALLOCARGS
+{
+ /** The number of pages to allocate. */
+ size_t cPages;
+ /** Non-null on success. */
+ void *pvAlloc;
+ /** RTMEMPAGEALLOC_F_XXX. */
+ uint32_t fFlags;
+} RTHEAPPAGEALLOCARGS;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Initialize once structure. */
+static RTONCE g_MemPagePosixInitOnce = RTONCE_INITIALIZER;
+/** The page heap. */
+static RTHEAPPAGE g_MemPagePosixHeap;
+/** The exec page heap. */
+static RTHEAPPAGE g_MemExecPosixHeap;
+
+
+#ifdef RT_OS_OS2
+/*
+ * A quick mmap/munmap mockup for avoid duplicating lots of good code.
+ */
+# define INCL_BASE
+# include <os2.h>
+# undef MAP_PRIVATE
+# define MAP_PRIVATE 0
+# undef MAP_ANONYMOUS
+# define MAP_ANONYMOUS 0
+# undef MAP_FAILED
+# define MAP_FAILED (void *)-1
+# undef mmap
+# define mmap iprt_mmap
+# undef munmap
+# define munmap iprt_munmap
+
+static void *mmap(void *pvWhere, size_t cb, int fProt, int fFlags, int fd, off_t off)
+{
+ NOREF(pvWhere); NOREF(fd); NOREF(off);
+ void *pv = NULL;
+ ULONG fAlloc = OBJ_ANY | PAG_COMMIT;
+ if (fProt & PROT_EXEC)
+ fAlloc |= PAG_EXECUTE;
+ if (fProt & PROT_READ)
+ fAlloc |= PAG_READ;
+ if (fProt & PROT_WRITE)
+ fAlloc |= PAG_WRITE;
+ APIRET rc = DosAllocMem(&pv, cb, fAlloc);
+ if (rc == NO_ERROR)
+ return pv;
+ errno = ENOMEM;
+ return MAP_FAILED;
+}
+
+static int munmap(void *pv, size_t cb)
+{
+ APIRET rc = DosFreeMem(pv);
+ if (rc == NO_ERROR)
+ return 0;
+ errno = EINVAL;
+ return -1;
+}
+
+#endif
+
+/**
+ * Initializes the heap.
+ *
+ * @returns IPRT status code.
+ * @param pHeap The page heap to initialize.
+ * @param fExec Whether the heap memory should be marked as
+ * executable or not.
+ */
+int RTHeapPageInit(PRTHEAPPAGE pHeap, bool fExec)
+{
+ int rc = RTCritSectInitEx(&pHeap->CritSect,
+ RTCRITSECT_FLAGS_NO_LOCK_VAL | RTCRITSECT_FLAGS_NO_NESTING | RTCRITSECT_FLAGS_BOOTSTRAP_HACK,
+ NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ pHeap->cHeapPages = 0;
+ pHeap->cFreePages = 0;
+ pHeap->cAllocCalls = 0;
+ pHeap->cFreeCalls = 0;
+ pHeap->uLastMinimizeCall = 0;
+ pHeap->BlockTree = NULL;
+ pHeap->fExec = fExec;
+ pHeap->u32Magic = RTHEAPPAGE_MAGIC;
+ }
+ return rc;
+}
+
+
+/**
+ * Deletes the heap and all the memory it tracks.
+ *
+ * @returns IPRT status code.
+ * @param pHeap The page heap to delete.
+ */
+int RTHeapPageDelete(PRTHEAPPAGE pHeap)
+{
+ NOREF(pHeap);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Applies flags to an allocation.
+ *
+ * @param pv The allocation.
+ * @param cb The size of the allocation (page aligned).
+ * @param fFlags RTMEMPAGEALLOC_F_XXX.
+ */
+DECLINLINE(void) rtMemPagePosixApplyFlags(void *pv, size_t cb, uint32_t fFlags)
+{
+#ifndef RT_OS_OS2
+ if (fFlags & RTMEMPAGEALLOC_F_ADVISE_LOCKED)
+ {
+ int rc = mlock(pv, cb);
+# ifndef RT_OS_SOLARIS /* mlock(3C) on Solaris requires the priv_lock_memory privilege */
+ AssertMsg(rc == 0, ("mlock %p LB %#zx -> %d errno=%d\n", pv, cb, rc, errno));
+# endif
+ NOREF(rc);
+ }
+
+# ifdef MADV_DONTDUMP
+ if (fFlags & RTMEMPAGEALLOC_F_ADVISE_NO_DUMP)
+ {
+ int rc = madvise(pv, cb, MADV_DONTDUMP);
+ AssertMsg(rc == 0, ("madvice %p LB %#zx MADV_DONTDUMP -> %d errno=%d\n", pv, cb, rc, errno));
+ NOREF(rc);
+ }
+# endif
+#endif
+
+ if (fFlags & RTMEMPAGEALLOC_F_ZERO)
+ RT_BZERO(pv, cb);
+}
+
+
+/**
+ * Avoids some gotos in rtHeapPageAllocFromBlock.
+ *
+ * @returns VINF_SUCCESS.
+ * @param pBlock The block.
+ * @param iPage The page to start allocating at.
+ * @param cPages The number of pages.
+ * @param fFlags RTMEMPAGEALLOC_F_XXX.
+ * @param ppv Where to return the allocation address.
+ */
+DECLINLINE(int) rtHeapPageAllocFromBlockSuccess(PRTHEAPPAGEBLOCK pBlock, uint32_t iPage, size_t cPages, uint32_t fFlags, void **ppv)
+{
+ PRTHEAPPAGE pHeap = pBlock->pHeap;
+
+ ASMBitSet(&pBlock->bmFirst[0], iPage);
+ pBlock->cFreePages -= cPages;
+ pHeap->cFreePages -= cPages;
+ if (!pHeap->pHint2 || pHeap->pHint2->cFreePages < pBlock->cFreePages)
+ pHeap->pHint2 = pBlock;
+ pHeap->cAllocCalls++;
+
+ void *pv = (uint8_t *)pBlock->Core.Key + (iPage << PAGE_SHIFT);
+ *ppv = pv;
+
+ if (fFlags)
+ rtMemPagePosixApplyFlags(pv, cPages << PAGE_SHIFT, fFlags);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks if a page range is free in the specified block.
+ *
+ * @returns @c true if the range is free, @c false if not.
+ * @param pBlock The block.
+ * @param iFirst The first page to check.
+ * @param cPages The number of pages to check.
+ */
+DECLINLINE(bool) rtHeapPageIsPageRangeFree(PRTHEAPPAGEBLOCK pBlock, uint32_t iFirst, uint32_t cPages)
+{
+ uint32_t i = iFirst + cPages;
+ while (i-- > iFirst)
+ {
+ if (ASMBitTest(&pBlock->bmAlloc[0], i))
+ return false;
+ Assert(!ASMBitTest(&pBlock->bmFirst[0], i));
+ }
+ return true;
+}
+
+
+/**
+ * Tries to allocate a chunk of pages from a heap block.
+ *
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_NO_MEMORY if the allocation failed.
+ * @param pBlock The block to allocate from.
+ * @param cPages The size of the allocation.
+ * @param fFlags RTMEMPAGEALLOC_F_XXX.
+ * @param ppv Where to return the allocation address on success.
+ */
+DECLINLINE(int) rtHeapPageAllocFromBlock(PRTHEAPPAGEBLOCK pBlock, size_t cPages, uint32_t fFlags, void **ppv)
+{
+ if (pBlock->cFreePages >= cPages)
+ {
+ int iPage = ASMBitFirstClear(&pBlock->bmAlloc[0], RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT);
+ Assert(iPage >= 0);
+
+ /* special case: single page. */
+ if (cPages == 1)
+ {
+ ASMBitSet(&pBlock->bmAlloc[0], iPage);
+ return rtHeapPageAllocFromBlockSuccess(pBlock, iPage, cPages, fFlags, ppv);
+ }
+
+ while ( iPage >= 0
+ && (unsigned)iPage <= RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT - cPages)
+ {
+ if (rtHeapPageIsPageRangeFree(pBlock, iPage + 1, cPages - 1))
+ {
+ ASMBitSetRange(&pBlock->bmAlloc[0], iPage, iPage + cPages);
+ return rtHeapPageAllocFromBlockSuccess(pBlock, iPage, cPages, fFlags, ppv);
+ }
+
+ /* next */
+ iPage = ASMBitNextSet(&pBlock->bmAlloc[0], RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT, iPage);
+ if (iPage < 0 || iPage >= RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT - 1)
+ break;
+ iPage = ASMBitNextClear(&pBlock->bmAlloc[0], RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT, iPage);
+ }
+ }
+
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * RTAvlrPVDoWithAll callback.
+ *
+ * @returns 0 to continue the enum, non-zero to quit it.
+ * @param pNode The node.
+ * @param pvUser The user argument.
+ */
+static DECLCALLBACK(int) rtHeapPageAllocCallback(PAVLRPVNODECORE pNode, void *pvUser)
+{
+ PRTHEAPPAGEBLOCK pBlock = RT_FROM_MEMBER(pNode, RTHEAPPAGEBLOCK, Core);
+ RTHEAPPAGEALLOCARGS *pArgs = (RTHEAPPAGEALLOCARGS *)pvUser;
+ int rc = rtHeapPageAllocFromBlock(pBlock, pArgs->cPages, pArgs->fFlags, &pArgs->pvAlloc);
+ return RT_SUCCESS(rc) ? 1 : 0;
+}
+
+
+/**
+ * Worker for RTHeapPageAlloc.
+ *
+ * @returns IPRT status code
+ * @param pHeap The heap - locked.
+ * @param cPages The page count.
+ * @param pszTag The tag.
+ * @param fFlags RTMEMPAGEALLOC_F_XXX.
+ * @param ppv Where to return the address of the allocation
+ * on success.
+ */
+static int rtHeapPageAllocLocked(PRTHEAPPAGE pHeap, size_t cPages, const char *pszTag, uint32_t fFlags, void **ppv)
+{
+ int rc;
+ NOREF(pszTag);
+
+ /*
+ * Use the hints first.
+ */
+ if (pHeap->pHint1)
+ {
+ rc = rtHeapPageAllocFromBlock(pHeap->pHint1, cPages, fFlags, ppv);
+ if (rc != VERR_NO_MEMORY)
+ return rc;
+ }
+ if (pHeap->pHint2)
+ {
+ rc = rtHeapPageAllocFromBlock(pHeap->pHint2, cPages, fFlags, ppv);
+ if (rc != VERR_NO_MEMORY)
+ return rc;
+ }
+
+ /*
+ * Search the heap for a block with enough free space.
+ *
+ * N.B. This search algorithm is not optimal at all. What (hopefully) saves
+ * it are the two hints above.
+ */
+ if (pHeap->cFreePages >= cPages)
+ {
+ RTHEAPPAGEALLOCARGS Args;
+ Args.cPages = cPages;
+ Args.pvAlloc = NULL;
+ Args.fFlags = fFlags;
+ RTAvlrPVDoWithAll(&pHeap->BlockTree, true /*fFromLeft*/, rtHeapPageAllocCallback, &Args);
+ if (Args.pvAlloc)
+ {
+ *ppv = Args.pvAlloc;
+ return VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * Didn't find anytyhing, so expand the heap with a new block.
+ */
+ RTCritSectLeave(&pHeap->CritSect);
+ void *pvPages;
+ pvPages = mmap(NULL, RTMEMPAGEPOSIX_BLOCK_SIZE,
+ PROT_READ | PROT_WRITE | (pHeap->fExec ? PROT_EXEC : 0),
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (pvPages == MAP_FAILED)
+ {
+ RTCritSectEnter(&pHeap->CritSect);
+ return RTErrConvertFromErrno(errno);
+
+ }
+ /** @todo Eliminate this rtMemBaseAlloc dependency! */
+ PRTHEAPPAGEBLOCK pBlock;
+#ifdef RTALLOC_REPLACE_MALLOC
+ if (g_pfnOrgMalloc)
+ pBlock = (PRTHEAPPAGEBLOCK)g_pfnOrgMalloc(sizeof(*pBlock));
+ else
+#endif
+ pBlock = (PRTHEAPPAGEBLOCK)rtMemBaseAlloc(sizeof(*pBlock));
+ if (!pBlock)
+ {
+ munmap(pvPages, RTMEMPAGEPOSIX_BLOCK_SIZE);
+ RTCritSectEnter(&pHeap->CritSect);
+ return VERR_NO_MEMORY;
+ }
+
+ RT_ZERO(*pBlock);
+ pBlock->Core.Key = pvPages;
+ pBlock->Core.KeyLast = (uint8_t *)pvPages + RTMEMPAGEPOSIX_BLOCK_SIZE - 1;
+ pBlock->cFreePages = RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT;
+ pBlock->pHeap = pHeap;
+
+ RTCritSectEnter(&pHeap->CritSect);
+
+ bool fRc = RTAvlrPVInsert(&pHeap->BlockTree, &pBlock->Core); Assert(fRc); NOREF(fRc);
+ pHeap->cFreePages += RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT;
+ pHeap->cHeapPages += RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT;
+
+ /*
+ * Grab memory from the new block (cannot fail).
+ */
+ rc = rtHeapPageAllocFromBlock(pBlock, cPages, fFlags, ppv);
+ Assert(rc == VINF_SUCCESS);
+
+ return rc;
+}
+
+
+/**
+ * Allocates one or more pages off the heap.
+ *
+ * @returns IPRT status code.
+ * @param pHeap The page heap.
+ * @param cPages The number of pages to allocate.
+ * @param pszTag The allocation tag.
+ * @param fFlags RTMEMPAGEALLOC_F_XXX.
+ * @param ppv Where to return the pointer to the pages.
+ */
+int RTHeapPageAlloc(PRTHEAPPAGE pHeap, size_t cPages, const char *pszTag, uint32_t fFlags, void **ppv)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtr(ppv);
+ *ppv = NULL;
+ AssertPtrReturn(pHeap, VERR_INVALID_HANDLE);
+ AssertReturn(pHeap->u32Magic == RTHEAPPAGE_MAGIC, VERR_INVALID_HANDLE);
+ AssertMsgReturn(cPages < RTMEMPAGEPOSIX_BLOCK_SIZE, ("%#zx\n", cPages), VERR_OUT_OF_RANGE);
+
+ /*
+ * Grab the lock and call a worker with many returns.
+ */
+ int rc = RTCritSectEnter(&pHeap->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtHeapPageAllocLocked(pHeap, cPages, pszTag, fFlags, ppv);
+ RTCritSectLeave(&pHeap->CritSect);
+ }
+
+ return rc;
+}
+
+
+/**
+ * RTAvlrPVDoWithAll callback.
+ *
+ * @returns 0 to continue the enum, non-zero to quit it.
+ * @param pNode The node.
+ * @param pvUser Pointer to a block pointer variable. For returning
+ * the address of the block to be freed.
+ */
+static DECLCALLBACK(int) rtHeapPageFindUnusedBlockCallback(PAVLRPVNODECORE pNode, void *pvUser)
+{
+ PRTHEAPPAGEBLOCK pBlock = RT_FROM_MEMBER(pNode, RTHEAPPAGEBLOCK, Core);
+ if (pBlock->cFreePages == RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT)
+ {
+ *(PRTHEAPPAGEBLOCK *)pvUser = pBlock;
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ * Allocates one or more pages off the heap.
+ *
+ * @returns IPRT status code.
+ * @param pHeap The page heap.
+ * @param pv Pointer to what RTHeapPageAlloc returned.
+ * @param cPages The number of pages that was allocated.
+ */
+int RTHeapPageFree(PRTHEAPPAGE pHeap, void *pv, size_t cPages)
+{
+ /*
+ * Validate input.
+ */
+ if (!pv)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pHeap, VERR_INVALID_HANDLE);
+ AssertReturn(pHeap->u32Magic == RTHEAPPAGE_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Grab the lock and look up the page.
+ */
+ int rc = RTCritSectEnter(&pHeap->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ PRTHEAPPAGEBLOCK pBlock = (PRTHEAPPAGEBLOCK)RTAvlrPVRangeGet(&pHeap->BlockTree, pv);
+ if (pBlock)
+ {
+ /*
+ * Validate the specified address range.
+ */
+ uint32_t const iPage = (uint32_t)(((uintptr_t)pv - (uintptr_t)pBlock->Core.Key) >> PAGE_SHIFT);
+ /* Check the range is within the block. */
+ bool fOk = iPage + cPages <= RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT;
+ /* Check that it's the start of an allocation. */
+ fOk = fOk && ASMBitTest(&pBlock->bmFirst[0], iPage);
+ /* Check that the range ends at an allocation boundrary. */
+ fOk = fOk && ( iPage + cPages == RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT
+ || ASMBitTest(&pBlock->bmFirst[0], iPage + cPages)
+ || !ASMBitTest(&pBlock->bmAlloc[0], iPage + cPages));
+ /* Check the other pages. */
+ uint32_t const iLastPage = iPage + cPages - 1;
+ for (uint32_t i = iPage + 1; i < iLastPage && fOk; i++)
+ fOk = ASMBitTest(&pBlock->bmAlloc[0], i)
+ && !ASMBitTest(&pBlock->bmFirst[0], i);
+ if (fOk)
+ {
+ /*
+ * Free the memory.
+ */
+ ASMBitClearRange(&pBlock->bmAlloc[0], iPage, iPage + cPages);
+ ASMBitClear(&pBlock->bmFirst[0], iPage);
+ pBlock->cFreePages += cPages;
+ pHeap->cFreePages += cPages;
+ pHeap->cFreeCalls++;
+ if (!pHeap->pHint1 || pHeap->pHint1->cFreePages < pBlock->cFreePages)
+ pHeap->pHint1 = pBlock;
+
+ /** @todo Add bitmaps for tracking madvice and mlock so we can undo those. */
+
+ /*
+ * Shrink the heap. Not very efficient because of the AVL tree.
+ */
+ if ( pHeap->cFreePages >= RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT * 3
+ && pHeap->cFreePages >= pHeap->cHeapPages / 2 /* 50% free */
+ && pHeap->cFreeCalls - pHeap->uLastMinimizeCall > RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT
+ )
+ {
+ uint32_t cFreePageTarget = pHeap->cHeapPages / 4; /* 25% free */
+ while (pHeap->cFreePages > cFreePageTarget)
+ {
+ pHeap->uLastMinimizeCall = pHeap->cFreeCalls;
+
+ pBlock = NULL;
+ RTAvlrPVDoWithAll(&pHeap->BlockTree, false /*fFromLeft*/,
+ rtHeapPageFindUnusedBlockCallback, &pBlock);
+ if (!pBlock)
+ break;
+
+ void *pv2 = RTAvlrPVRemove(&pHeap->BlockTree, pBlock->Core.Key); Assert(pv2); NOREF(pv2);
+ pHeap->cHeapPages -= RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT;
+ pHeap->cFreePages -= RTMEMPAGEPOSIX_BLOCK_PAGE_COUNT;
+ pHeap->pHint1 = NULL;
+ pHeap->pHint2 = NULL;
+ RTCritSectLeave(&pHeap->CritSect);
+
+ munmap(pBlock->Core.Key, RTMEMPAGEPOSIX_BLOCK_SIZE);
+ pBlock->Core.Key = pBlock->Core.KeyLast = NULL;
+ pBlock->cFreePages = 0;
+#ifdef RTALLOC_REPLACE_MALLOC
+ if (g_pfnOrgFree)
+ g_pfnOrgFree(pBlock);
+ else
+#endif
+ rtMemBaseFree(pBlock);
+
+ RTCritSectEnter(&pHeap->CritSect);
+ }
+ }
+ }
+ else
+ rc = VERR_INVALID_POINTER;
+ }
+ else
+ rc = VERR_INVALID_POINTER;
+
+ RTCritSectLeave(&pHeap->CritSect);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Initializes the heap.
+ *
+ * @returns IPRT status code
+ * @param pvUser Unused.
+ */
+static DECLCALLBACK(int) rtMemPagePosixInitOnce(void *pvUser)
+{
+ NOREF(pvUser);
+ int rc = RTHeapPageInit(&g_MemPagePosixHeap, false /*fExec*/);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTHeapPageInit(&g_MemExecPosixHeap, true /*fExec*/);
+ if (RT_SUCCESS(rc))
+ return rc;
+ RTHeapPageDelete(&g_MemPagePosixHeap);
+ }
+ return rc;
+}
+
+
+/**
+ * Allocates memory from the specified heap.
+ *
+ * @returns Address of the allocated memory.
+ * @param cb The number of bytes to allocate.
+ * @param pszTag The tag.
+ * @param fFlags RTMEMPAGEALLOC_F_XXX.
+ * @param pHeap The heap to use.
+ */
+static void *rtMemPagePosixAlloc(size_t cb, const char *pszTag, uint32_t fFlags, PRTHEAPPAGE pHeap)
+{
+ /*
+ * Validate & adjust the input.
+ */
+ Assert(cb > 0);
+ NOREF(pszTag);
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+
+ /*
+ * If the allocation is relatively large, we use mmap/munmap directly.
+ */
+ void *pv;
+ if (cb >= RTMEMPAGEPOSIX_MMAP_THRESHOLD)
+ {
+
+ pv = mmap(NULL, cb,
+ PROT_READ | PROT_WRITE | (pHeap == &g_MemExecPosixHeap ? PROT_EXEC : 0),
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (pv != MAP_FAILED)
+ {
+ AssertPtr(pv);
+
+ if (fFlags)
+ rtMemPagePosixApplyFlags(pv, cb, fFlags);
+ }
+ else
+ pv = NULL;
+ }
+ else
+ {
+ int rc = RTOnce(&g_MemPagePosixInitOnce, rtMemPagePosixInitOnce, NULL);
+ if (RT_SUCCESS(rc))
+ rc = RTHeapPageAlloc(pHeap, cb >> PAGE_SHIFT, pszTag, fFlags, &pv);
+ if (RT_FAILURE(rc))
+ pv = NULL;
+ }
+
+ return pv;
+}
+
+
+/**
+ * Free memory allocated by rtMemPagePosixAlloc.
+ *
+ * @param pv The address of the memory to free.
+ * @param cb The size.
+ * @param pHeap The heap.
+ */
+static void rtMemPagePosixFree(void *pv, size_t cb, PRTHEAPPAGE pHeap)
+{
+ /*
+ * Validate & adjust the input.
+ */
+ if (!pv)
+ return;
+ AssertPtr(pv);
+ Assert(cb > 0);
+ Assert(!((uintptr_t)pv & PAGE_OFFSET_MASK));
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+
+ /*
+ * If the allocation is relatively large, we use mmap/munmap directly.
+ */
+ if (cb >= RTMEMPAGEPOSIX_MMAP_THRESHOLD)
+ {
+ int rc = munmap(pv, cb);
+ AssertMsg(rc == 0, ("rc=%d pv=%p cb=%#zx\n", rc, pv, cb)); NOREF(rc);
+ }
+ else
+ {
+ int rc = RTHeapPageFree(pHeap, pv, cb >> PAGE_SHIFT);
+ AssertRC(rc);
+ }
+}
+
+
+
+
+
+RTDECL(void *) RTMemPageAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtMemPagePosixAlloc(cb, pszTag, 0, &g_MemPagePosixHeap);
+}
+
+
+RTDECL(void *) RTMemPageAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtMemPagePosixAlloc(cb, pszTag, RTMEMPAGEALLOC_F_ZERO, &g_MemPagePosixHeap);
+}
+
+
+RTDECL(void *) RTMemPageAllocExTag(size_t cb, uint32_t fFlags, const char *pszTag) RT_NO_THROW_DEF
+{
+ AssertReturn(!(fFlags & ~RTMEMPAGEALLOC_F_VALID_MASK), NULL);
+ return rtMemPagePosixAlloc(cb, pszTag, fFlags, &g_MemPagePosixHeap);
+}
+
+
+RTDECL(void) RTMemPageFree(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ return rtMemPagePosixFree(pv, cb, &g_MemPagePosixHeap);
+}
+
diff --git a/src/VBox/Runtime/r3/posix/rtmempage-exec-mmap-posix.cpp b/src/VBox/Runtime/r3/posix/rtmempage-exec-mmap-posix.cpp
new file mode 100644
index 00000000..abe285a2
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/rtmempage-exec-mmap-posix.cpp
@@ -0,0 +1,182 @@
+/* $Id: rtmempage-exec-mmap-posix.cpp $ */
+/** @file
+ * IPRT - RTMemPage*, POSIX with mmap only.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
+
+/**
+ * Applies flags to an allocation.
+ *
+ * @param pv The allocation.
+ * @param cb The size of the allocation (page aligned).
+ * @param fFlags RTMEMPAGEALLOC_F_XXX.
+ */
+DECLINLINE(void) rtMemPagePosixApplyFlags(void *pv, size_t cb, uint32_t fFlags)
+{
+#ifndef RT_OS_OS2
+ if (fFlags & RTMEMPAGEALLOC_F_ADVISE_LOCKED)
+ {
+ int rc = mlock(pv, cb);
+# ifndef RT_OS_SOLARIS /* mlock(3C) on Solaris requires the priv_lock_memory privilege */
+ AssertMsg(rc == 0, ("mlock %p LB %#zx -> %d errno=%d\n", pv, cb, rc, errno));
+# endif
+ NOREF(rc);
+ }
+
+# ifdef MADV_DONTDUMP
+ if (fFlags & RTMEMPAGEALLOC_F_ADVISE_NO_DUMP)
+ {
+ int rc = madvise(pv, cb, MADV_DONTDUMP);
+ AssertMsg(rc == 0, ("madvice %p LB %#zx MADV_DONTDUMP -> %d errno=%d\n", pv, cb, rc, errno));
+ NOREF(rc);
+ }
+# endif
+#endif
+
+ if (fFlags & RTMEMPAGEALLOC_F_ZERO)
+ RT_BZERO(pv, cb);
+}
+
+
+/**
+ * Allocates memory from the specified heap.
+ *
+ * @returns Address of the allocated memory.
+ * @param cb The number of bytes to allocate.
+ * @param pszTag The tag.
+ * @param fFlags RTMEMPAGEALLOC_F_XXX.
+ * @param fProtExec PROT_EXEC or 0.
+ */
+static void *rtMemPagePosixAlloc(size_t cb, const char *pszTag, uint32_t fFlags, int fProtExec)
+{
+ /*
+ * Validate & adjust the input.
+ */
+ Assert(cb > 0);
+ NOREF(pszTag);
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+
+ /*
+ * Do the allocation.
+ */
+ void *pv = mmap(NULL, cb,
+ PROT_READ | PROT_WRITE | fProtExec,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (pv != MAP_FAILED)
+ {
+ AssertPtr(pv);
+
+ if (fFlags)
+ rtMemPagePosixApplyFlags(pv, cb, fFlags);
+ }
+ else
+ pv = NULL;
+
+ return pv;
+}
+
+
+/**
+ * Free memory allocated by rtMemPagePosixAlloc.
+ *
+ * @param pv The address of the memory to free.
+ * @param cb The size.
+ */
+static void rtMemPagePosixFree(void *pv, size_t cb)
+{
+ /*
+ * Validate & adjust the input.
+ */
+ if (!pv)
+ return;
+ AssertPtr(pv);
+ Assert(cb > 0);
+ Assert(!((uintptr_t)pv & PAGE_OFFSET_MASK));
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+
+ /*
+ * Free the memory.
+ */
+ int rc = munmap(pv, cb);
+ AssertMsg(rc == 0, ("rc=%d pv=%p cb=%#zx\n", rc, pv, cb)); NOREF(rc);
+}
+
+
+
+
+
+RTDECL(void *) RTMemPageAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtMemPagePosixAlloc(cb, pszTag, 0, 0);
+}
+
+
+RTDECL(void *) RTMemPageAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtMemPagePosixAlloc(cb, pszTag, RTMEMPAGEALLOC_F_ZERO, 0);
+}
+
+
+RTDECL(void *) RTMemPageAllocExTag(size_t cb, uint32_t fFlags, const char *pszTag) RT_NO_THROW_DEF
+{
+ AssertReturn(!(fFlags & ~RTMEMPAGEALLOC_F_VALID_MASK), NULL);
+ return rtMemPagePosixAlloc(cb, pszTag, fFlags, 0);
+}
+
+
+RTDECL(void) RTMemPageFree(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ return rtMemPagePosixFree(pv, cb);
+}
+
diff --git a/src/VBox/Runtime/r3/posix/sched-posix.cpp b/src/VBox/Runtime/r3/posix/sched-posix.cpp
new file mode 100644
index 00000000..b30b9c67
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/sched-posix.cpp
@@ -0,0 +1,849 @@
+/* $Id: sched-posix.cpp $ */
+/** @file
+ * IPRT - Scheduling, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/*
+ * !WARNING!
+ *
+ * When talking about lowering and raising priority, we do *NOT* refer to
+ * the common direction priority values takes on unix systems (lower means
+ * higher). So, when we raise the priority of a linux thread the nice
+ * value will decrease, and when we lower the priority the nice value
+ * will increase. Confusing, right?
+ *
+ * !WARNING!
+ */
+
+
+
+/** @def THREAD_LOGGING
+ * Be very careful with enabling this, it may cause deadlocks when combined
+ * with the 'thread' logging prefix.
+ */
+#ifdef DOXYGEN_RUNNING
+#define THREAD_LOGGING
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#include <iprt/thread.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/err.h>
+#include "internal/sched.h"
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/** Array scheduler attributes corresponding to each of the thread types. */
+typedef struct PROCPRIORITYTYPE
+{
+ /** For sanity include the array index. */
+ RTTHREADTYPE enmType;
+ /** The thread priority or nice delta - depends on which priority type. */
+ int iPriority;
+} PROCPRIORITYTYPE;
+
+
+/**
+ * Configuration of one priority.
+ */
+typedef struct
+{
+ /** The priority. */
+ RTPROCPRIORITY enmPriority;
+ /** The name of this priority. */
+ const char *pszName;
+ /** The process nice value. */
+ int iNice;
+ /** The delta applied to the iPriority value. */
+ int iDelta;
+ /** Array scheduler attributes corresponding to each of the thread types. */
+ const PROCPRIORITYTYPE *paTypes;
+} PROCPRIORITY;
+
+
+/**
+ * Saved priority settings
+ */
+typedef struct
+{
+ /** Process priority. */
+ int iPriority;
+ /** Process level. */
+ struct sched_param SchedParam;
+ /** Process level. */
+ int iPolicy;
+ /** pthread level. */
+ struct sched_param PthreadSchedParam;
+ /** pthread level. */
+ int iPthreadPolicy;
+} SAVEDPRIORITY, *PSAVEDPRIORITY;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Thread level priorities based on a 0..31 priority range
+ * as specified as the minimum for SCHED_RR/FIFO. FreeBSD
+ * seems to be using this (needs more research to be
+ * certain).
+ */
+static const PROCPRIORITYTYPE g_aTypesThread[RTTHREADTYPE_END] =
+{
+ { RTTHREADTYPE_INVALID, -999999999 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, 5 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, 12 },
+ { RTTHREADTYPE_EMULATION, 14 },
+ { RTTHREADTYPE_DEFAULT, 15 },
+ { RTTHREADTYPE_GUI, 16 },
+ { RTTHREADTYPE_MAIN_WORKER, 18 },
+ { RTTHREADTYPE_VRDP_IO, 24 },
+ { RTTHREADTYPE_DEBUGGER, 28 },
+ { RTTHREADTYPE_MSG_PUMP, 29 },
+ { RTTHREADTYPE_IO, 30 },
+ { RTTHREADTYPE_TIMER, 31 }
+};
+
+static const PROCPRIORITYTYPE g_aTypesThreadFlat[RTTHREADTYPE_END] =
+{
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, 15 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, 15 },
+ { RTTHREADTYPE_EMULATION, 15 },
+ { RTTHREADTYPE_DEFAULT, 15 },
+ { RTTHREADTYPE_GUI, 15 },
+ { RTTHREADTYPE_MAIN_WORKER, 15 },
+ { RTTHREADTYPE_VRDP_IO, 15 },
+ { RTTHREADTYPE_DEBUGGER, 15 },
+ { RTTHREADTYPE_MSG_PUMP, 15 },
+ { RTTHREADTYPE_IO, 15 },
+ { RTTHREADTYPE_TIMER, 15 }
+};
+
+/**
+ * Process and thread level priority, full access at thread level.
+ */
+static const PROCPRIORITY g_aProcessAndThread[] =
+{
+ { RTPROCPRIORITY_FLAT, "Flat", 0, 0, g_aTypesThreadFlat },
+ { RTPROCPRIORITY_LOW, "Low", 9, 0, g_aTypesThread },
+ { RTPROCPRIORITY_LOW, "Low", 11, 0, g_aTypesThread },
+ { RTPROCPRIORITY_LOW, "Low", 15, 0, g_aTypesThread },
+ { RTPROCPRIORITY_LOW, "Low", 17, 0, g_aTypesThread },
+ { RTPROCPRIORITY_LOW, "Low", 19, 0, g_aTypesThread },
+ { RTPROCPRIORITY_LOW, "Low", 7, 0, g_aTypesThread },
+ { RTPROCPRIORITY_LOW, "Low", 5, 0, g_aTypesThread },
+ { RTPROCPRIORITY_LOW, "Low", 3, 0, g_aTypesThread },
+ { RTPROCPRIORITY_LOW, "Low", 1, 0, g_aTypesThread },
+ { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesThread },
+ { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesThreadFlat },
+ { RTPROCPRIORITY_HIGH, "High", -9, 0, g_aTypesThread },
+ { RTPROCPRIORITY_HIGH, "High", -7, 0, g_aTypesThread },
+ { RTPROCPRIORITY_HIGH, "High", -5, 0, g_aTypesThread },
+ { RTPROCPRIORITY_HIGH, "High", -3, 0, g_aTypesThread },
+ { RTPROCPRIORITY_HIGH, "High", -1, 0, g_aTypesThread },
+ { RTPROCPRIORITY_HIGH, "High", -9, 0, g_aTypesThreadFlat },
+ { RTPROCPRIORITY_HIGH, "High", -1, 0, g_aTypesThreadFlat }
+};
+
+/**
+ * Deltas for a process in which we are not restricted
+ * to only be lowering the priority.
+ */
+static const PROCPRIORITYTYPE g_aTypesUnixFree[RTTHREADTYPE_END] =
+{
+ { RTTHREADTYPE_INVALID, -999999999 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, +3 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 },
+ { RTTHREADTYPE_EMULATION, +1 },
+ { RTTHREADTYPE_DEFAULT, 0 },
+ { RTTHREADTYPE_GUI, 0 },
+ { RTTHREADTYPE_MAIN_WORKER, 0 },
+ { RTTHREADTYPE_VRDP_IO, -1 },
+ { RTTHREADTYPE_DEBUGGER, -1 },
+ { RTTHREADTYPE_MSG_PUMP, -2 },
+ { RTTHREADTYPE_IO, -3 },
+ { RTTHREADTYPE_TIMER, -4 }
+};
+
+/**
+ * Deltas for a process in which we are restricted
+ * to only be lowering the priority.
+ */
+static const PROCPRIORITYTYPE g_aTypesUnixRestricted[RTTHREADTYPE_END] =
+{
+ { RTTHREADTYPE_INVALID, -999999999 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, +3 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, +2 },
+ { RTTHREADTYPE_EMULATION, +1 },
+ { RTTHREADTYPE_DEFAULT, 0 },
+ { RTTHREADTYPE_GUI, 0 },
+ { RTTHREADTYPE_MAIN_WORKER, 0 },
+ { RTTHREADTYPE_VRDP_IO, 0 },
+ { RTTHREADTYPE_DEBUGGER, 0 },
+ { RTTHREADTYPE_MSG_PUMP, 0 },
+ { RTTHREADTYPE_IO, 0 },
+ { RTTHREADTYPE_TIMER, 0 }
+};
+
+/**
+ * Deltas for a process in which we are restricted
+ * to only be lowering the priority.
+ */
+static const PROCPRIORITYTYPE g_aTypesUnixFlat[RTTHREADTYPE_END] =
+{
+ { RTTHREADTYPE_INVALID, -999999999 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, 0 },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, 0 },
+ { RTTHREADTYPE_EMULATION, 0 },
+ { RTTHREADTYPE_DEFAULT, 0 },
+ { RTTHREADTYPE_GUI, 0 },
+ { RTTHREADTYPE_MAIN_WORKER, 0 },
+ { RTTHREADTYPE_VRDP_IO, 0 },
+ { RTTHREADTYPE_DEBUGGER, 0 },
+ { RTTHREADTYPE_MSG_PUMP, 0 },
+ { RTTHREADTYPE_IO, 0 },
+ { RTTHREADTYPE_TIMER, 0 }
+};
+
+/**
+ * Process and thread level priority, full access at thread level.
+ */
+static const PROCPRIORITY g_aUnixConfigs[] =
+{
+ { RTPROCPRIORITY_FLAT, "Flat", 0, 0, g_aTypesUnixFlat },
+ { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesUnixFree },
+ { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesUnixFlat },
+ { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesUnixFree },
+ { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesUnixFlat },
+ { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesUnixFree },
+ { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesUnixFlat },
+ { RTPROCPRIORITY_LOW, "Low", 19, 19, g_aTypesUnixFlat },
+ { RTPROCPRIORITY_LOW, "Low", 9, 9, g_aTypesUnixRestricted },
+ { RTPROCPRIORITY_LOW, "Low", 15, 15, g_aTypesUnixRestricted },
+ { RTPROCPRIORITY_LOW, "Low", 17, 17, g_aTypesUnixRestricted },
+ { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesUnixFree },
+ { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesUnixRestricted },
+ { RTPROCPRIORITY_NORMAL, "Normal", 0, 0, g_aTypesUnixFlat },
+ { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesUnixFree },
+ { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesUnixFree },
+ { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesUnixFree },
+ { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesUnixFree },
+ { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesUnixFree },
+ { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesUnixRestricted },
+ { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesUnixRestricted },
+ { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesUnixRestricted },
+ { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesUnixRestricted },
+ { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesUnixRestricted },
+ { RTPROCPRIORITY_HIGH, "High", -9, -9, g_aTypesUnixFlat },
+ { RTPROCPRIORITY_HIGH, "High", -7, -7, g_aTypesUnixFlat },
+ { RTPROCPRIORITY_HIGH, "High", -5, -5, g_aTypesUnixFlat },
+ { RTPROCPRIORITY_HIGH, "High", -3, -3, g_aTypesUnixFlat },
+ { RTPROCPRIORITY_HIGH, "High", -1, -1, g_aTypesUnixFlat }
+};
+
+/**
+ * The dynamic default priority configuration.
+ *
+ * This will be recalulated at runtime depending on what the
+ * system allow us to do and what the current priority is.
+ */
+static PROCPRIORITY g_aDefaultPriority =
+{
+ RTPROCPRIORITY_LOW, "Default", 0, 0, g_aTypesUnixRestricted
+};
+
+/** Pointer to the current priority configuration. */
+static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority;
+
+
+/** Set to what kind of scheduling priority support the host
+ * OS seems to be offering. Determined at runtime.
+ */
+static enum
+{
+ OSPRIOSUP_UNDETERMINED = 0,
+ /** An excellent combination of process and thread level
+ * I.e. setpriority() works on process level, one have to be supervisor
+ * to raise priority as is the custom in unix. While pthread_setschedparam()
+ * works on thread level and we can raise the priority just like we want.
+ *
+ * I think this is what FreeBSD offers. (It is certainly analogous to what
+ * NT offers if you wondered.) Linux on the other hand doesn't provide this
+ * for processes with SCHED_OTHER policy, and I'm not sure if we want to
+ * play around with using the real-time SCHED_RR and SCHED_FIFO which would
+ * require special privileges anyway.
+ */
+ OSPRIOSUP_PROCESS_AND_THREAD_LEVEL,
+ /** A rough thread level priority only.
+ * setpriority() is the only real game in town, and it works on thread level.
+ */
+ OSPRIOSUP_THREAD_LEVEL
+} volatile g_enmOsPrioSup = OSPRIOSUP_UNDETERMINED;
+
+/** Set if we figure we have nice capability, meaning we can use setpriority
+ * to raise the priority. */
+static bool g_fCanNice = false;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+/**
+ * Saves all the scheduling attributes we can think of.
+ */
+static void rtSchedNativeSave(PSAVEDPRIORITY pSave)
+{
+ memset(pSave, 0xff, sizeof(*pSave));
+
+ errno = 0;
+ pSave->iPriority = getpriority(PRIO_PROCESS, 0 /* current process */);
+ Assert(errno == 0);
+
+ errno = 0;
+ sched_getparam(0 /* current process */, &pSave->SchedParam);
+ Assert(errno == 0);
+
+ errno = 0;
+ pSave->iPolicy = sched_getscheduler(0 /* current process */);
+ Assert(errno == 0);
+
+ int rc = pthread_getschedparam(pthread_self(), &pSave->iPthreadPolicy, &pSave->PthreadSchedParam);
+ Assert(rc == 0); NOREF(rc);
+}
+
+
+/**
+ * Restores scheduling attributes.
+ * Most of this won't work right, but anyway...
+ */
+static void rtSchedNativeRestore(PSAVEDPRIORITY pSave)
+{
+ setpriority(PRIO_PROCESS, 0, pSave->iPriority);
+ sched_setscheduler(0, pSave->iPolicy, &pSave->SchedParam);
+ sched_setparam(0, &pSave->SchedParam);
+ pthread_setschedparam(pthread_self(), pSave->iPthreadPolicy, &pSave->PthreadSchedParam);
+}
+
+
+/**
+ * Starts a worker thread and wait for it to complete.
+ * We cannot use RTThreadCreate since we're already owner of the RW lock.
+ */
+static int rtSchedCreateThread(void *(*pfnThread)(void *pvArg), void *pvArg)
+{
+ /*
+ * Setup thread attributes.
+ */
+ pthread_attr_t ThreadAttr;
+ int rc = pthread_attr_init(&ThreadAttr);
+ if (!rc)
+ {
+ rc = pthread_attr_setdetachstate(&ThreadAttr, PTHREAD_CREATE_JOINABLE);
+ if (!rc)
+ {
+ rc = pthread_attr_setstacksize(&ThreadAttr, 128*1024);
+ if (!rc)
+ {
+ /*
+ * Create the thread.
+ */
+ pthread_t Thread;
+ rc = pthread_create(&Thread, &ThreadAttr, pfnThread, pvArg);
+ if (!rc)
+ {
+ pthread_attr_destroy(&ThreadAttr);
+ /*
+ * Wait for the thread to finish.
+ */
+ void *pvRet = (void *)-1;
+ do
+ {
+ rc = pthread_join(Thread, &pvRet);
+ } while (rc == EINTR);
+ if (rc)
+ return RTErrConvertFromErrno(rc);
+ return (int)(uintptr_t)pvRet;
+ }
+ }
+ }
+ pthread_attr_destroy(&ThreadAttr);
+ }
+ return RTErrConvertFromErrno(rc);
+}
+
+
+static void rtSchedDumpPriority(void)
+{
+#ifdef THREAD_LOGGING
+ Log(("Priority: g_fCanNice=%d g_enmOsPrioSup=%d\n", g_fCanNice, g_enmOsPrioSup));
+ Log(("Priority: enmPriority=%d \"%s\" iNice=%d iDelta=%d\n",
+ g_pProcessPriority->enmPriority,
+ g_pProcessPriority->pszName,
+ g_pProcessPriority->iNice,
+ g_pProcessPriority->iDelta));
+ Log(("Priority: %2d INFREQUENT_POLLER = %d\n", RTTHREADTYPE_INFREQUENT_POLLER, g_pProcessPriority->paTypes[RTTHREADTYPE_INFREQUENT_POLLER].iPriority));
+ Log(("Priority: %2d MAIN_HEAVY_WORKER = %d\n", RTTHREADTYPE_MAIN_HEAVY_WORKER, g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_HEAVY_WORKER].iPriority));
+ Log(("Priority: %2d EMULATION = %d\n", RTTHREADTYPE_EMULATION , g_pProcessPriority->paTypes[RTTHREADTYPE_EMULATION ].iPriority));
+ Log(("Priority: %2d DEFAULT = %d\n", RTTHREADTYPE_DEFAULT , g_pProcessPriority->paTypes[RTTHREADTYPE_DEFAULT ].iPriority));
+ Log(("Priority: %2d GUI = %d\n", RTTHREADTYPE_GUI , g_pProcessPriority->paTypes[RTTHREADTYPE_GUI ].iPriority));
+ Log(("Priority: %2d MAIN_WORKER = %d\n", RTTHREADTYPE_MAIN_WORKER , g_pProcessPriority->paTypes[RTTHREADTYPE_MAIN_WORKER ].iPriority));
+ Log(("Priority: %2d VRDP_IO = %d\n", RTTHREADTYPE_VRDP_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_VRDP_IO ].iPriority));
+ Log(("Priority: %2d DEBUGGER = %d\n", RTTHREADTYPE_DEBUGGER , g_pProcessPriority->paTypes[RTTHREADTYPE_DEBUGGER ].iPriority));
+ Log(("Priority: %2d MSG_PUMP = %d\n", RTTHREADTYPE_MSG_PUMP , g_pProcessPriority->paTypes[RTTHREADTYPE_MSG_PUMP ].iPriority));
+ Log(("Priority: %2d IO = %d\n", RTTHREADTYPE_IO , g_pProcessPriority->paTypes[RTTHREADTYPE_IO ].iPriority));
+ Log(("Priority: %2d TIMER = %d\n", RTTHREADTYPE_TIMER , g_pProcessPriority->paTypes[RTTHREADTYPE_TIMER ].iPriority));
+#endif
+}
+
+
+/**
+ * The prober thread.
+ * We don't want to mess with the priority of the calling thread.
+ *
+ * @remark This is pretty presumptive stuff, but if it works on Linux and
+ * FreeBSD it does what I want.
+ */
+static void *rtSchedNativeProberThread(void *pvUser)
+{
+ SAVEDPRIORITY SavedPriority;
+ rtSchedNativeSave(&SavedPriority);
+
+ /*
+ * Let's first try and see what we get on a thread level.
+ */
+ int iMax = sched_get_priority_max(SavedPriority.iPthreadPolicy);
+ int iMin = sched_get_priority_min(SavedPriority.iPthreadPolicy);
+ if (iMax - iMin >= 32)
+ {
+ pthread_t Self = pthread_self();
+ int i = iMin;
+ while (i <= iMax)
+ {
+ struct sched_param SchedParam = SavedPriority.PthreadSchedParam;
+ SchedParam.sched_priority = i;
+ if (pthread_setschedparam(Self, SavedPriority.iPthreadPolicy, &SchedParam))
+ break;
+ i++;
+ }
+ if (i == iMax)
+ g_enmOsPrioSup = OSPRIOSUP_PROCESS_AND_THREAD_LEVEL;
+ }
+
+ /*
+ * Ok, we didn't have the good stuff, so let's fall back on the unix stuff.
+ */
+ if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED)
+ g_enmOsPrioSup = OSPRIOSUP_THREAD_LEVEL;
+
+ /*
+ * Check if we can get higher priority (typically only root can do this).
+ * (Won't work right if our priority is -19 to start with, but what the heck.)
+ *
+ * We assume that the unix priority is -19 to 19. I know there are defines
+ * for this, but I don't remember which and if I'm awake enough to make sense
+ * of them from any SuS spec.
+ */
+ int iStart = getpriority(PRIO_PROCESS, 0);
+ int i = iStart;
+ while (i-- > -19)
+ {
+ if (setpriority(PRIO_PROCESS, 0, i))
+ break;
+ }
+ if (getpriority(PRIO_PROCESS, 0) != iStart)
+ g_fCanNice = true;
+ else
+ g_fCanNice = false;
+
+ /* done */
+ rtSchedNativeRestore(&SavedPriority);
+ RT_NOREF(pvUser);
+ return (void *)VINF_SUCCESS;
+}
+
+
+/**
+ * Calculate the scheduling properties for all the threads in the default
+ * process priority, assuming the current thread have the type enmType.
+ *
+ * @returns iprt status code.
+ * @param enmType The thread type to be assumed for the current thread.
+ */
+DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+
+ /*
+ * First figure out what's supported by the OS.
+ */
+ if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED)
+ {
+ int iPriority = getpriority(PRIO_PROCESS, 0);
+ int rc = rtSchedCreateThread(rtSchedNativeProberThread, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (g_enmOsPrioSup == OSPRIOSUP_UNDETERMINED)
+ g_enmOsPrioSup = OSPRIOSUP_THREAD_LEVEL;
+ Assert(getpriority(PRIO_PROCESS, 0) == iPriority); NOREF(iPriority);
+ }
+
+ /*
+ * Now let's see what we can do...
+ */
+ int iPriority = getpriority(PRIO_PROCESS, 0);
+ switch (g_enmOsPrioSup)
+ {
+ case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL:
+ {
+ g_aDefaultPriority.iNice = iPriority;
+ g_aDefaultPriority.iDelta = 0;
+ g_aDefaultPriority.paTypes = g_aTypesThread;
+ Assert(enmType == g_aDefaultPriority.paTypes[enmType].enmType);
+ break;
+ }
+
+ case OSPRIOSUP_THREAD_LEVEL:
+ {
+ if (g_fCanNice)
+ g_aDefaultPriority.paTypes = g_aTypesUnixFree;
+ else
+ g_aDefaultPriority.paTypes = g_aTypesUnixRestricted;
+ Assert(enmType == g_aDefaultPriority.paTypes[enmType].enmType);
+ g_aDefaultPriority.iNice = iPriority - g_aDefaultPriority.paTypes[enmType].iPriority;
+ g_aDefaultPriority.iDelta = g_aDefaultPriority.iNice;
+ break;
+ }
+
+ default:
+ AssertFailed();
+ break;
+ }
+ rtSchedDumpPriority();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * The validator thread.
+ * We don't want to mess with the priority of the calling thread.
+ *
+ * @remark This is pretty presumptive stuff, but if it works on Linux and
+ * FreeBSD it does what I want.
+ */
+static void *rtSchedNativeValidatorThread(void *pvUser)
+{
+ const PROCPRIORITY *pCfg = (const PROCPRIORITY *)pvUser;
+ SAVEDPRIORITY SavedPriority;
+ rtSchedNativeSave(&SavedPriority);
+
+ int rc = VINF_SUCCESS;
+ switch (g_enmOsPrioSup)
+ {
+ /*
+ * Try set the specified process priority and then try
+ * out all the thread priorities which are used.
+ */
+ case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL:
+ {
+ if (!setpriority(PRIO_PROCESS, 0, pCfg->iNice))
+ {
+ int iMin = sched_get_priority_min(SavedPriority.iPolicy);
+ pthread_t Self = pthread_self();
+ for (int i = RTTHREADTYPE_INVALID + 1; i < RTTHREADTYPE_END; i++)
+ {
+ struct sched_param SchedParam = SavedPriority.PthreadSchedParam;
+ SchedParam.sched_priority = pCfg->paTypes[i].iPriority
+ + pCfg->iDelta + iMin;
+ rc = pthread_setschedparam(Self, SavedPriority.iPthreadPolicy, &SchedParam);
+ if (rc)
+ {
+ rc = RTErrConvertFromErrno(rc);
+ break;
+ }
+ }
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+
+ /*
+ * Try out the priorities from the top and down.
+ */
+ case OSPRIOSUP_THREAD_LEVEL:
+ {
+ int i = RTTHREADTYPE_END;
+ while (--i > RTTHREADTYPE_INVALID)
+ {
+ int iPriority = pCfg->paTypes[i].iPriority + pCfg->iDelta;
+ if (setpriority(PRIO_PROCESS, 0, iPriority))
+ {
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ AssertFailed();
+ break;
+ }
+
+ /* done */
+ rtSchedNativeRestore(&SavedPriority);
+ return (void *)(intptr_t)rc;
+}
+
+
+DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority)
+{
+ Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST);
+
+#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+ /*
+ * Make sure the proxy creation thread is started so we don't 'lose' our
+ * initial priority if it's lowered.
+ */
+ rtThreadPosixPriorityProxyStart();
+#endif
+
+ /*
+ * Nothing to validate for the default priority (assuming no external renice).
+ */
+ int rc = VINF_SUCCESS;
+ if (enmPriority == RTPROCPRIORITY_DEFAULT)
+ g_pProcessPriority = &g_aDefaultPriority;
+ else
+ {
+ /*
+ * Select the array to search.
+ */
+ const PROCPRIORITY *pa;
+ unsigned c;
+ switch (g_enmOsPrioSup)
+ {
+ case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL:
+ pa = g_aProcessAndThread;
+ c = RT_ELEMENTS(g_aProcessAndThread);
+ break;
+ case OSPRIOSUP_THREAD_LEVEL:
+ pa = g_aUnixConfigs;
+ c = RT_ELEMENTS(g_aUnixConfigs);
+ break;
+ default:
+ pa = NULL;
+ c = 0;
+ break;
+ }
+
+ /*
+ * Search the array.
+ */
+ rc = VERR_FILE_NOT_FOUND;
+ unsigned i;
+ for (i = 0; i < c; i++)
+ {
+ if (pa[i].enmPriority == enmPriority)
+ {
+ /*
+ * Validate it.
+ */
+ int iPriority = getpriority(PRIO_PROCESS, 0);
+ int rc3 = rtSchedCreateThread(rtSchedNativeValidatorThread, (void *)&pa[i]);
+ Assert(getpriority(PRIO_PROCESS, 0) == iPriority); NOREF(iPriority);
+ if (RT_SUCCESS(rc))
+ rc = rc3;
+ if (RT_SUCCESS(rc))
+ break;
+ }
+ }
+
+ /*
+ * Did we get lucky?
+ * If so update process priority and globals.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ switch (g_enmOsPrioSup)
+ {
+ case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL:
+ if (setpriority(PRIO_PROCESS, 0, pa[i].iNice))
+ {
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("setpriority(,,%d) -> errno=%d rc=%Rrc\n", pa[i].iNice, errno, rc));
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ g_pProcessPriority = &pa[i];
+ }
+ }
+
+#ifdef THREAD_LOGGING
+ LogFlow(("rtProcNativeSetPriority: returns %Rrc enmPriority=%d\n", rc, enmPriority));
+ rtSchedDumpPriority();
+#endif
+ return rc;
+}
+
+
+/**
+ * Worker for rtThreadNativeSetPriority/OSPRIOSUP_PROCESS_AND_THREAD_LEVEL
+ * that's either called on the priority proxy thread or directly if no proxy.
+ */
+static DECLCALLBACK(int) rtThreadPosixSetPriorityOnProcAndThrdCallback(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ struct sched_param SchedParam = {-9999999};
+ int iPolicy = -7777777;
+ int rc = pthread_getschedparam((pthread_t)pThread->Core.Key, &iPolicy, &SchedParam);
+ if (!rc)
+ {
+ SchedParam.sched_priority = g_pProcessPriority->paTypes[enmType].iPriority
+ + g_pProcessPriority->iDelta
+ + sched_get_priority_min(iPolicy);
+
+ rc = pthread_setschedparam((pthread_t)pThread->Core.Key, iPolicy, &SchedParam);
+ if (!rc)
+ {
+#ifdef THREAD_LOGGING
+ Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPolicy=%d sched_priority=%d pid=%d\n",
+ pThread->Core.Key, enmType, iPolicy, SchedParam.sched_priority, getpid()));
+#endif
+ return VINF_SUCCESS;
+ }
+ }
+
+ int rcNative = rc;
+ rc = RTErrConvertFromErrno(rc);
+ AssertMsgFailed(("pthread_[gs]etschedparam(%p, %d, {%d}) -> rcNative=%d rc=%Rrc\n",
+ (void *)pThread->Core.Key, iPolicy, SchedParam.sched_priority, rcNative, rc)); NOREF(rcNative);
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ Assert(enmType == g_pProcessPriority->paTypes[enmType].enmType);
+
+ int rc = VINF_SUCCESS;
+ switch (g_enmOsPrioSup)
+ {
+ case OSPRIOSUP_PROCESS_AND_THREAD_LEVEL:
+ {
+#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+ if (rtThreadPosixPriorityProxyStart())
+ rc = rtThreadPosixPriorityProxyCall(pThread, (PFNRT)rtThreadPosixSetPriorityOnProcAndThrdCallback,
+ 2, pThread, enmType);
+ else
+#endif
+ rc = rtThreadPosixSetPriorityOnProcAndThrdCallback(pThread, enmType);
+ break;
+ }
+
+ case OSPRIOSUP_THREAD_LEVEL:
+ {
+ /* No cross platform way of getting the 'who' parameter value for
+ arbitrary threads, so this is restricted to the calling thread only. */
+ AssertReturn((pthread_t)pThread->Core.Key == pthread_self(), VERR_NOT_SUPPORTED);
+
+ int iPriority = g_pProcessPriority->paTypes[enmType].iPriority + g_pProcessPriority->iDelta;
+ if (!setpriority(PRIO_PROCESS, 0, iPriority))
+ {
+ AssertMsg(iPriority == getpriority(PRIO_PROCESS, 0), ("iPriority=%d getpriority()=%d\n", iPriority, getpriority(PRIO_PROCESS, 0)));
+#ifdef THREAD_LOGGING
+ Log(("rtThreadNativeSetPriority: Thread=%p enmType=%d iPriority=%d pid=%d\n", pThread->Core.Key, enmType, iPriority, getpid()));
+#endif
+ }
+ else
+ {
+#if 0
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("setpriority(,, %d) -> errno=%d rc=%Rrc\n", iPriority, errno, rc));
+#else
+ /** @todo
+ * Just keep quiet about failures now - we'll fail here because we're not
+ * allowed to raise our own priority. This is a problem when starting the
+ * threads with higher priority from EMT (i.e. most threads it starts).
+ * This is apparently inherited from the parent in some cases and not
+ * in other cases. I guess this would come down to which kind of pthread
+ * implementation is actually in use, and how many sensible patches which
+ * are installed.
+ * I need to find a system where this problem shows up in order to come up
+ * with a proper fix. There's an pthread_create attribute for not inheriting
+ * scheduler stuff I think...
+ */
+ rc = VINF_SUCCESS;
+#endif
+ }
+ break;
+ }
+
+ /*
+ * Any thread created before we determine the default config, remains unchanged!
+ * The prober thread above is one of those.
+ */
+ default:
+ break;
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/semevent-posix.cpp b/src/VBox/Runtime/r3/posix/semevent-posix.cpp
new file mode 100644
index 00000000..96d01bac
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/semevent-posix.cpp
@@ -0,0 +1,654 @@
+/* $Id: semevent-posix.cpp $ */
+/** @file
+ * IPRT - Event Semaphore, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/time.h>
+
+#include "internal/mem.h"
+#include "internal/strict.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sched.h>
+
+#include "semwait.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/** Internal representation of the POSIX implementation of an Event semaphore.
+ * The POSIX implementation uses a mutex and a condition variable to implement
+ * the automatic reset event semaphore semantics.
+ */
+struct RTSEMEVENTINTERNAL
+{
+ /** pthread condition. */
+ pthread_cond_t Cond;
+ /** pthread mutex which protects the condition and the event state. */
+ pthread_mutex_t Mutex;
+ /** The state of the semaphore.
+ * This is operated while owning mutex and using atomic updating. */
+ volatile uint32_t u32State;
+ /** Number of waiters. */
+ volatile uint32_t cWaiters;
+#ifdef RTSEMEVENT_STRICT
+ /** Signallers. */
+ RTLOCKVALRECSHRD Signallers;
+ /** Indicates that lock validation should be performed. */
+ bool volatile fEverHadSignallers;
+#endif
+ /** The creation flags. */
+ uint32_t fFlags;
+ /** Set if we're using the monotonic clock. */
+ bool fMonotonicClock;
+};
+
+/** The values of the u32State variable in a RTSEMEVENTINTERNAL.
+ * @{ */
+/** The object isn't initialized. */
+#define EVENT_STATE_UNINITIALIZED 0
+/** The semaphore is signaled. */
+#define EVENT_STATE_SIGNALED 0xff00ff00
+/** The semaphore is not signaled. */
+#define EVENT_STATE_NOT_SIGNALED 0x00ff00ff
+/** @} */
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+
+ /*
+ * Allocate semaphore handle.
+ */
+ int rc;
+ struct RTSEMEVENTINTERNAL *pThis;
+ if (!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ pThis = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(*pThis));
+ else
+ pThis = (struct RTSEMEVENTINTERNAL *)rtMemBaseAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ /*
+ * Create the condition variable.
+ */
+ pthread_condattr_t CondAttr;
+ rc = pthread_condattr_init(&CondAttr);
+ if (!rc)
+ {
+#if defined(CLOCK_MONOTONIC) && defined(IPRT_HAVE_PTHREAD_CONDATTR_SETCLOCK)
+ /* ASSUMES RTTimeSystemNanoTS() == RTTimeNanoTS() == clock_gettime(CLOCK_MONOTONIC). */
+ rc = pthread_condattr_setclock(&CondAttr, CLOCK_MONOTONIC);
+ pThis->fMonotonicClock = rc == 0;
+#else
+ pThis->fMonotonicClock = false;
+#endif
+ rc = pthread_cond_init(&pThis->Cond, &CondAttr);
+ if (!rc)
+ {
+ /*
+ * Create the semaphore.
+ */
+ rc = pthread_mutex_init(&pThis->Mutex, NULL);
+ if (!rc)
+ {
+ pthread_condattr_destroy(&CondAttr);
+
+ ASMAtomicWriteU32(&pThis->u32State, EVENT_STATE_NOT_SIGNALED);
+ ASMAtomicWriteU32(&pThis->cWaiters, 0);
+ pThis->fFlags = fFlags;
+#ifdef RTSEMEVENT_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemEventAnon = 0;
+ RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
+ "RTSemEvent-%u", ASMAtomicIncU32(&s_iSemEventAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
+ pszNameFmt, va);
+ va_end(va);
+ }
+ pThis->fEverHadSignallers = false;
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
+#endif
+
+ *phEventSem = pThis;
+ return VINF_SUCCESS;
+ }
+ pthread_cond_destroy(&pThis->Cond);
+ }
+ pthread_condattr_destroy(&CondAttr);
+ }
+
+ rc = RTErrConvertFromErrno(rc);
+ if (!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ RTMemFree(pThis);
+ else
+ rtMemBaseFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ uint32_t u32 = pThis->u32State;
+ AssertReturn(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED, VERR_INVALID_HANDLE);
+
+ /*
+ * Abort all waiters forcing them to return failure.
+ */
+ int rc;
+ for (int i = 30; i > 0; i--)
+ {
+ ASMAtomicWriteU32(&pThis->u32State, EVENT_STATE_UNINITIALIZED);
+ rc = pthread_cond_destroy(&pThis->Cond);
+ if (rc != EBUSY)
+ break;
+ pthread_cond_broadcast(&pThis->Cond);
+ usleep(1000);
+ }
+ if (rc)
+ {
+ AssertMsgFailed(("Failed to destroy event sem %p, rc=%d.\n", pThis, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ /*
+ * Destroy the semaphore
+ * If it's busy we'll wait a bit to give the threads a chance to be scheduled.
+ */
+ for (int i = 30; i > 0; i--)
+ {
+ rc = pthread_mutex_destroy(&pThis->Mutex);
+ if (rc != EBUSY)
+ break;
+ usleep(1000);
+ }
+ if (rc)
+ {
+ AssertMsgFailed(("Failed to destroy event sem %p, rc=%d. (mutex)\n", pThis, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ /*
+ * Free the semaphore memory and be gone.
+ */
+#ifdef RTSEMEVENT_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->Signallers);
+#endif
+ if (!(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ RTMemFree(pThis);
+ else
+ rtMemBaseFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ uint32_t u32 = pThis->u32State;
+ AssertReturn(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMEVENT_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Lock the mutex semaphore.
+ */
+ int rc = pthread_mutex_lock(&pThis->Mutex);
+ if (rc)
+ {
+ AssertMsgFailed(("Failed to lock event sem %p, rc=%d.\n", hEventSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ /*
+ * Check the state.
+ */
+ if (pThis->u32State == EVENT_STATE_NOT_SIGNALED)
+ {
+ ASMAtomicWriteU32(&pThis->u32State, EVENT_STATE_SIGNALED);
+ rc = pthread_cond_signal(&pThis->Cond);
+ AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d.\n", hEventSem, rc));
+ }
+ else if (pThis->u32State == EVENT_STATE_SIGNALED)
+ {
+ rc = pthread_cond_signal(&pThis->Cond); /* give'm another kick... */
+ AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d. (2)\n", hEventSem, rc));
+ }
+ else
+ rc = VERR_SEM_DESTROYED;
+
+ /*
+ * Release the mutex and return.
+ */
+ int rc2 = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc2, ("Failed to unlock event sem %p, rc=%d.\n", hEventSem, rc));
+ if (rc)
+ return RTErrConvertFromErrno(rc);
+ if (rc2)
+ return RTErrConvertFromErrno(rc2);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Handle polling (timeout already expired at the time of the call).
+ *
+ * @returns VINF_SUCCESS, VERR_TIMEOUT, VERR_SEM_DESTROYED.
+ * @param pThis The semaphore.
+ */
+DECLINLINE(int) rtSemEventPosixWaitPoll(struct RTSEMEVENTINTERNAL *pThis)
+{
+ int rc = pthread_mutex_lock(&pThis->Mutex);
+ AssertMsgReturn(!rc, ("Failed to lock event sem %p, rc=%d.\n", pThis, rc), RTErrConvertFromErrno(rc));
+
+ uint32_t u32OldState;
+ bool fSuccess = ASMAtomicCmpXchgExU32(&pThis->u32State, EVENT_STATE_NOT_SIGNALED, EVENT_STATE_SIGNALED, &u32OldState);
+
+ rc = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", pThis, rc)); NOREF(rc);
+
+ return fSuccess
+ ? VINF_SUCCESS
+ : u32OldState != EVENT_STATE_UNINITIALIZED
+ ? VERR_TIMEOUT
+ : VERR_SEM_DESTROYED;
+}
+
+
+/**
+ * Performs an indefinite wait on the event.
+ */
+static int rtSemEventPosixWaitIndefinite(struct RTSEMEVENTINTERNAL *pThis, uint32_t fFlags, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ RT_NOREF_PV(pSrcPos);
+
+ /* for fairness, yield before going to sleep. */
+ if ( ASMAtomicIncU32(&pThis->cWaiters) > 1
+ && pThis->u32State == EVENT_STATE_SIGNALED)
+ sched_yield();
+
+ /* take mutex */
+ int rc = pthread_mutex_lock(&pThis->Mutex);
+ if (rc)
+ {
+ ASMAtomicDecU32(&pThis->cWaiters);
+ AssertMsgFailed(("Failed to lock event sem %p, rc=%d.\n", pThis, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ for (;;)
+ {
+ /* check state. */
+ if (pThis->u32State == EVENT_STATE_SIGNALED)
+ {
+ ASMAtomicWriteU32(&pThis->u32State, EVENT_STATE_NOT_SIGNALED);
+ ASMAtomicDecU32(&pThis->cWaiters);
+ rc = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", pThis, rc)); NOREF(rc);
+ return VINF_SUCCESS;
+ }
+ if (pThis->u32State == EVENT_STATE_UNINITIALIZED)
+ {
+ rc = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc, ("Failed to unlock event sem %p, rc=%d.\n", pThis, rc)); NOREF(rc);
+ return VERR_SEM_DESTROYED;
+ }
+
+ /* wait */
+#ifdef RTSEMEVENT_STRICT
+ RTTHREAD hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)
+ ? RTThreadSelfAutoAdopt()
+ : RTThreadSelf();
+ if (pThis->fEverHadSignallers)
+ {
+ rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
+ RT_INDEFINITE_WAIT, RTTHREADSTATE_EVENT, true);
+ if (RT_FAILURE(rc))
+ {
+ ASMAtomicDecU32(&pThis->cWaiters);
+ pthread_mutex_unlock(&pThis->Mutex);
+ return rc;
+ }
+ }
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
+ RT_NOREF_PV(fFlags); /** @todo interruptible wait is not implementable... */
+ rc = pthread_cond_wait(&pThis->Cond, &pThis->Mutex);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
+ if (rc)
+ {
+ AssertMsgFailed(("Failed to wait on event sem %p, rc=%d.\n", pThis, rc));
+ ASMAtomicDecU32(&pThis->cWaiters);
+ int rc2 = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc2, ("Failed to unlock event sem %p, rc=%d.\n", pThis, rc2)); NOREF(rc2);
+ return RTErrConvertFromErrno(rc);
+ }
+ }
+}
+
+
+/**
+ * Performs an timed wait on the event.
+ */
+static int rtSemEventPosixWaitTimed(struct RTSEMEVENTINTERNAL *pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Convert the timeout specification to absolute and relative deadlines,
+ * divierting polling and infinite waits to the appropriate workers.
+ */
+ struct timespec AbsDeadline = { 0, 0 };
+ uint64_t const cNsRelativeDeadline = rtSemPosixCalcDeadline(fFlags, uTimeout, pThis->fMonotonicClock, &AbsDeadline);
+ if (cNsRelativeDeadline == 0)
+ return rtSemEventPosixWaitPoll(pThis);
+ if (cNsRelativeDeadline == UINT64_MAX)
+ return rtSemEventPosixWaitIndefinite(pThis, fFlags, pSrcPos);
+
+ /*
+ * Now to the business of waiting...
+ */
+
+ /* for fairness, yield before going to sleep. */
+ if (ASMAtomicIncU32(&pThis->cWaiters) > 1)
+ sched_yield();
+
+ /* take mutex */
+ int rc = pthread_mutex_lock(&pThis->Mutex);
+ if (rc)
+ {
+ ASMAtomicDecU32(&pThis->cWaiters);
+ AssertMsg(rc == ETIMEDOUT, ("Failed to lock event sem %p, rc=%d.\n", pThis, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ for (;;)
+ {
+ /* check state. */
+ uint32_t const u32State = pThis->u32State;
+ if (u32State != EVENT_STATE_NOT_SIGNALED)
+ {
+ if (u32State == EVENT_STATE_SIGNALED)
+ {
+ ASMAtomicWriteU32(&pThis->u32State, EVENT_STATE_NOT_SIGNALED);
+ ASMAtomicDecU32(&pThis->cWaiters);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Assert(u32State == EVENT_STATE_UNINITIALIZED);
+ rc = VERR_SEM_DESTROYED;
+ }
+ int rc2 = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc2, ("Failed to unlock event sem %p, rc2=%d.\n", pThis, rc2)); RT_NOREF(rc2);
+ return rc;
+ }
+
+ /* wait */
+#ifdef RTSEMEVENT_STRICT
+ RTTHREAD hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)
+ ? RTThreadSelfAutoAdopt()
+ : RTThreadSelf();
+ if (pThis->fEverHadSignallers)
+ {
+ rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
+ (cNsRelativeDeadline + RT_NS_1MS - 1) / RT_NS_1MS,
+ RTTHREADSTATE_EVENT, true);
+ if (RT_FAILURE(rc))
+ {
+ ASMAtomicDecU32(&pThis->cWaiters);
+ pthread_mutex_unlock(&pThis->Mutex);
+ return rc;
+ }
+ }
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
+ rc = pthread_cond_timedwait(&pThis->Cond, &pThis->Mutex, &AbsDeadline);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
+
+ /* According to SuS this function shall not return EINTR, but linux man page might have said differently at some point... */
+ if ( rc != 0
+ && ( rc != EINTR
+ || !(fFlags & RTSEMWAIT_FLAGS_NORESUME)))
+ {
+ AssertMsg(rc == ETIMEDOUT, ("Failed to wait on event sem %p, rc=%d.\n", pThis, rc));
+ ASMAtomicDecU32(&pThis->cWaiters);
+ int rc2 = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc2, ("Failed to unlock event sem %p, rc2=%d.\n", pThis, rc2)); NOREF(rc2);
+ return RTErrConvertFromErrno(rc);
+ }
+ } /* for (;;) */
+}
+
+
+/**
+ * Internal wait worker function.
+ */
+DECLINLINE(int) rtSemEventPosixWait(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ uint32_t u32 = pThis->u32State;
+ AssertReturn(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED, VERR_INVALID_HANDLE);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Timed or indefinite wait?
+ */
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ return rtSemEventPosixWaitIndefinite(pThis, fFlags, pSrcPos);
+ return rtSemEventPosixWaitTimed(hEventSem, fFlags, uTimeout, pSrcPos);
+}
+
+
+RTDECL(int) RTSemEventWait(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
+{
+ int rc;
+#ifndef RTSEMEVENT_STRICT
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = rtSemEventPosixWait(hEventSem, RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0, NULL);
+ else
+ rc = rtSemEventPosixWait(hEventSem, RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = rtSemEventPosixWait(hEventSem, RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0, &SrcPos);
+ else
+ rc = rtSemEventPosixWait(hEventSem, RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies, &SrcPos);
+#endif
+ Assert(rc != VERR_INTERRUPTED);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
+{
+ int rc;
+#ifndef RTSEMEVENT_STRICT
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = rtSemEventPosixWait(hEventSem, RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0, NULL);
+ else
+ rc = rtSemEventPosixWait(hEventSem, RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = rtSemEventPosixWait(hEventSem, RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_INDEFINITE, 0, &SrcPos);
+ else
+ rc = rtSemEventPosixWait(hEventSem, RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_MILLISECS,
+ cMillies, &SrcPos);
+#endif
+ Assert(rc != VERR_INTERRUPTED);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtSemEventPosixWait(hEventSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemEventPosixWait(hEventSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemEventPosixWait(hEventSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+RTDECL(uint32_t) RTSemEventGetResolution(void)
+{
+ /** @todo we have 1ns parameter resolution, but we need to check each host
+ * what the actual resolution might be once the parameter makes it to the
+ * kernel and is processed there. */
+ return 1;
+}
+
+
+RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ uint32_t u32 = pThis->u32State;
+ AssertReturnVoid(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ uint32_t u32 = pThis->u32State;
+ AssertReturnVoid(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ uint32_t u32 = pThis->u32State;
+ AssertReturnVoid(u32 == EVENT_STATE_NOT_SIGNALED || u32 == EVENT_STATE_SIGNALED);
+
+ RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
+#else
+ RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/posix/semeventmulti-posix.cpp b/src/VBox/Runtime/r3/posix/semeventmulti-posix.cpp
new file mode 100644
index 00000000..52171b4a
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/semeventmulti-posix.cpp
@@ -0,0 +1,613 @@
+/* $Id: semeventmulti-posix.cpp $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphore, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+
+#include "internal/strict.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "semwait.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Posix internal representation of a Mutex Multi semaphore.
+ * The POSIX implementation uses a mutex and a condition variable to implement
+ * the automatic reset event semaphore semantics. */
+struct RTSEMEVENTMULTIINTERNAL
+{
+ /** pthread condition. */
+ pthread_cond_t Cond;
+ /** pthread mutex which protects the condition and the event state. */
+ pthread_mutex_t Mutex;
+ /** The state of the semaphore.
+ * This is operated while owning mutex and using atomic updating. */
+ volatile uint32_t u32State;
+ /** Number of waiters. */
+ volatile uint32_t cWaiters;
+#ifdef RTSEMEVENTMULTI_STRICT
+ /** Signallers. */
+ RTLOCKVALRECSHRD Signallers;
+ /** Indicates that lock validation should be performed. */
+ bool volatile fEverHadSignallers;
+#endif
+ /** Set if we're using the monotonic clock. */
+ bool fMonotonicClock;
+};
+
+/** The values of the u32State variable in RTSEMEVENTMULTIINTERNAL.
+ * @{ */
+/** The object isn't initialized. */
+#define EVENTMULTI_STATE_UNINITIALIZED 0
+/** The semaphore is signaled. */
+#define EVENTMULTI_STATE_SIGNALED 0xff00ff00
+/** The semaphore is not signaled. */
+#define EVENTMULTI_STATE_NOT_SIGNALED 0x00ff00ff
+/** @} */
+
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate semaphore handle.
+ */
+ int rc;
+ struct RTSEMEVENTMULTIINTERNAL *pThis = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(struct RTSEMEVENTMULTIINTERNAL));
+ if (pThis)
+ {
+ /*
+ * Create the condition variable.
+ */
+ pthread_condattr_t CondAttr;
+ rc = pthread_condattr_init(&CondAttr);
+ if (!rc)
+ {
+#if defined(CLOCK_MONOTONIC) && defined(IPRT_HAVE_PTHREAD_CONDATTR_SETCLOCK)
+ /* ASSUMES RTTimeSystemNanoTS() == RTTimeNanoTS() == clock_gettime(CLOCK_MONOTONIC). */
+ rc = pthread_condattr_setclock(&CondAttr, CLOCK_MONOTONIC);
+ pThis->fMonotonicClock = rc == 0;
+#else
+ pThis->fMonotonicClock = false;
+#endif
+ rc = pthread_cond_init(&pThis->Cond, &CondAttr);
+ if (!rc)
+ {
+ /*
+ * Create the semaphore.
+ */
+ rc = pthread_mutex_init(&pThis->Mutex, NULL);
+ if (!rc)
+ {
+ pthread_condattr_destroy(&CondAttr);
+
+ ASMAtomicWriteU32(&pThis->u32State, EVENTMULTI_STATE_NOT_SIGNALED);
+ ASMAtomicWriteU32(&pThis->cWaiters, 0);
+#ifdef RTSEMEVENTMULTI_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemEventMultiAnon = 0;
+ RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
+ "RTSemEventMulti-%u", ASMAtomicIncU32(&s_iSemEventMultiAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
+ pszNameFmt, va);
+ va_end(va);
+ }
+ pThis->fEverHadSignallers = false;
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
+#endif
+
+ *phEventMultiSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ pthread_cond_destroy(&pThis->Cond);
+ }
+ pthread_condattr_destroy(&CondAttr);
+ }
+ rc = RTErrConvertFromErrno(rc);
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ uint32_t u32 = pThis->u32State;
+ AssertReturn(u32 == EVENTMULTI_STATE_NOT_SIGNALED || u32 == EVENTMULTI_STATE_SIGNALED, VERR_INVALID_HANDLE);
+
+ /*
+ * Abort all waiters forcing them to return failure.
+ */
+ int rc;
+ for (int i = 30; i > 0; i--)
+ {
+ ASMAtomicXchgU32(&pThis->u32State, EVENTMULTI_STATE_UNINITIALIZED);
+ rc = pthread_cond_destroy(&pThis->Cond);
+ if (rc != EBUSY)
+ break;
+ pthread_cond_broadcast(&pThis->Cond);
+ usleep(1000);
+ }
+ if (rc)
+ {
+ AssertMsgFailed(("Failed to destroy event sem %p, rc=%d.\n", hEventMultiSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ /*
+ * Destroy the semaphore
+ * If it's busy we'll wait a bit to give the threads a chance to be scheduled.
+ */
+ for (int i = 30; i > 0; i--)
+ {
+ rc = pthread_mutex_destroy(&pThis->Mutex);
+ if (rc != EBUSY)
+ break;
+ usleep(1000);
+ }
+ if (rc)
+ {
+ AssertMsgFailed(("Failed to destroy event sem %p, rc=%d. (mutex)\n", hEventMultiSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ /*
+ * Free the semaphore memory and be gone.
+ */
+#ifdef RTSEMEVENTMULTI_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->Signallers);
+#endif
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ uint32_t u32 = pThis->u32State;
+ AssertReturn(u32 == EVENTMULTI_STATE_NOT_SIGNALED || u32 == EVENTMULTI_STATE_SIGNALED, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMEVENTMULTI_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Lock the mutex semaphore.
+ */
+ int rc = pthread_mutex_lock(&pThis->Mutex);
+ if (rc)
+ {
+ AssertMsgFailed(("Failed to lock event sem %p, rc=%d.\n", hEventMultiSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ /*
+ * Check the state.
+ */
+ if (pThis->u32State == EVENTMULTI_STATE_NOT_SIGNALED)
+ {
+ ASMAtomicXchgU32(&pThis->u32State, EVENTMULTI_STATE_SIGNALED);
+ rc = pthread_cond_broadcast(&pThis->Cond);
+ AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d.\n", hEventMultiSem, rc));
+ }
+ else if (pThis->u32State == EVENTMULTI_STATE_SIGNALED)
+ {
+ rc = pthread_cond_broadcast(&pThis->Cond); /* give'm another kick... */
+ AssertMsg(!rc, ("Failed to signal event sem %p, rc=%d. (2)\n", hEventMultiSem, rc));
+ }
+ else
+ rc = VERR_SEM_DESTROYED;
+
+ /*
+ * Release the mutex and return.
+ */
+ int rc2 = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc2, ("Failed to unlock event sem %p, rc=%d.\n", hEventMultiSem, rc));
+ if (rc)
+ return RTErrConvertFromErrno(rc);
+ if (rc2)
+ return RTErrConvertFromErrno(rc2);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ int rc = VINF_SUCCESS;
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ uint32_t u32 = pThis->u32State;
+ AssertReturn(u32 == EVENTMULTI_STATE_NOT_SIGNALED || u32 == EVENTMULTI_STATE_SIGNALED, VERR_INVALID_HANDLE);
+
+ /*
+ * Lock the mutex semaphore.
+ */
+ int rcPosix = pthread_mutex_lock(&pThis->Mutex);
+ if (RT_UNLIKELY(rcPosix))
+ {
+ AssertMsgFailed(("Failed to lock event multi sem %p, rc=%d.\n", hEventMultiSem, rcPosix));
+ return RTErrConvertFromErrno(rcPosix);
+ }
+
+ /*
+ * Check the state.
+ */
+ if (pThis->u32State == EVENTMULTI_STATE_SIGNALED)
+ ASMAtomicXchgU32(&pThis->u32State, EVENTMULTI_STATE_NOT_SIGNALED);
+ else if (pThis->u32State != EVENTMULTI_STATE_NOT_SIGNALED)
+ rc = VERR_SEM_DESTROYED;
+
+ /*
+ * Release the mutex and return.
+ */
+ rcPosix = pthread_mutex_unlock(&pThis->Mutex);
+ if (RT_UNLIKELY(rcPosix))
+ {
+ AssertMsgFailed(("Failed to unlock event multi sem %p, rc=%d.\n", hEventMultiSem, rcPosix));
+ return RTErrConvertFromErrno(rcPosix);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Handle polling (timeout already expired at the time of the call).
+ *
+ * @returns VINF_SUCCESS, VERR_TIMEOUT, VERR_SEM_DESTROYED.
+ * @param pThis The semaphore.
+ */
+DECLINLINE(int) rtSemEventMultiPosixWaitPoll(struct RTSEMEVENTMULTIINTERNAL *pThis)
+{
+ int rc = pthread_mutex_lock(&pThis->Mutex);
+ AssertMsgReturn(!rc, ("Failed to lock event multi sem %p, rc=%d.\n", pThis, rc), RTErrConvertFromErrno(rc));
+
+ uint32_t const u32State = pThis->u32State;
+
+ rc = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", pThis, rc)); NOREF(rc);
+
+ return u32State == EVENTMULTI_STATE_SIGNALED
+ ? VINF_SUCCESS
+ : u32State != EVENTMULTI_STATE_UNINITIALIZED
+ ? VERR_TIMEOUT
+ : VERR_SEM_DESTROYED;
+}
+
+
+
+/**
+ * Implements the indefinite wait.
+ *
+ * @returns See RTSemEventMultiWaitEx.
+ * @param pThis The semaphore.
+ * @param fFlags See RTSemEventMultiWaitEx.
+ * @param pSrcPos The source position, can be NULL.
+ */
+static int rtSemEventMultiPosixWaitIndefinite(struct RTSEMEVENTMULTIINTERNAL *pThis, uint32_t fFlags, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /* take mutex */
+ int rc = pthread_mutex_lock(&pThis->Mutex);
+ AssertMsgReturn(!rc, ("Failed to lock event multi sem %p, rc=%d.\n", pThis, rc), RTErrConvertFromErrno(rc));
+ ASMAtomicIncU32(&pThis->cWaiters);
+
+ for (;;)
+ {
+ /* check state. */
+ uint32_t const u32State = pThis->u32State;
+ if (u32State != EVENTMULTI_STATE_NOT_SIGNALED)
+ {
+ ASMAtomicDecU32(&pThis->cWaiters);
+ rc = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", pThis, rc));
+ return u32State == EVENTMULTI_STATE_SIGNALED
+ ? VINF_SUCCESS
+ : VERR_SEM_DESTROYED;
+ }
+
+ /* wait */
+#ifdef RTSEMEVENTMULTI_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
+ if (pThis->fEverHadSignallers)
+ {
+ rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
+ RT_INDEFINITE_WAIT, RTTHREADSTATE_EVENT_MULTI, true);
+ if (RT_FAILURE(rc))
+ {
+ ASMAtomicDecU32(&pThis->cWaiters);
+ pthread_mutex_unlock(&pThis->Mutex);
+ return rc;
+ }
+ }
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+ RT_NOREF_PV(pSrcPos);
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true);
+ /** @todo interruptible wait is not implementable... */ NOREF(fFlags);
+ rc = pthread_cond_wait(&pThis->Cond, &pThis->Mutex);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI);
+ if (RT_UNLIKELY(rc))
+ {
+ AssertMsgFailed(("Failed to wait on event multi sem %p, rc=%d.\n", pThis, rc));
+ ASMAtomicDecU32(&pThis->cWaiters);
+ int rc2 = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc2, ("Failed to unlock event multi sem %p, rc=%d.\n", pThis, rc2)); NOREF(rc2);
+ return RTErrConvertFromErrno(rc);
+ }
+ }
+}
+
+
+/**
+ * Implements the timed wait.
+ *
+ * @returns See RTSemEventMultiWaitEx
+ * @param pThis The semaphore.
+ * @param fFlags See RTSemEventMultiWaitEx.
+ * @param uTimeout See RTSemEventMultiWaitEx.
+ * @param pSrcPos The source position, can be NULL.
+ */
+static int rtSemEventMultiPosixWaitTimed(struct RTSEMEVENTMULTIINTERNAL *pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Convert the timeout specification to absolute and relative deadlines,
+ * divierting polling and infinite waits to the appropriate workers.
+ */
+ struct timespec AbsDeadline = { 0, 0 };
+ uint64_t const cNsRelativeDeadline = rtSemPosixCalcDeadline(fFlags, uTimeout, pThis->fMonotonicClock, &AbsDeadline);
+ if (cNsRelativeDeadline == 0)
+ return rtSemEventMultiPosixWaitPoll(pThis);
+ if (cNsRelativeDeadline == UINT64_MAX)
+ return rtSemEventMultiPosixWaitIndefinite(pThis, fFlags, pSrcPos);
+
+ /*
+ * To business!
+ */
+ /* take mutex */
+ int rc = pthread_mutex_lock(&pThis->Mutex);
+ AssertMsgReturn(rc == 0, ("rc=%d pThis=%p\n", rc, pThis), RTErrConvertFromErrno(rc)); NOREF(rc);
+ ASMAtomicIncU32(&pThis->cWaiters);
+
+ for (;;)
+ {
+ /* check state. */
+ uint32_t const u32State = pThis->u32State;
+ if (u32State != EVENTMULTI_STATE_NOT_SIGNALED)
+ {
+ ASMAtomicDecU32(&pThis->cWaiters);
+ rc = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc, ("Failed to unlock event multi sem %p, rc=%d.\n", pThis, rc));
+ return u32State == EVENTMULTI_STATE_SIGNALED
+ ? VINF_SUCCESS
+ : VERR_SEM_DESTROYED;
+ }
+
+ /* wait */
+#ifdef RTSEMEVENTMULTI_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
+ if (pThis->fEverHadSignallers)
+ {
+ rc = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
+ (uTimeout + RT_NS_1MS - 1)/ RT_NS_1MS, RTTHREADSTATE_EVENT_MULTI, true);
+ if (RT_FAILURE(rc))
+ {
+ ASMAtomicDecU32(&pThis->cWaiters);
+ pthread_mutex_unlock(&pThis->Mutex);
+ return rc;
+ }
+ }
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true);
+ rc = pthread_cond_timedwait(&pThis->Cond, &pThis->Mutex, &AbsDeadline);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI);
+
+ /* According to SuS this function shall not return EINTR, but linux man page might have said differently at some point... */
+ if ( rc != 0
+ && ( rc != EINTR
+ || (fFlags & RTSEMWAIT_FLAGS_NORESUME)) )
+ {
+ AssertMsg(rc == ETIMEDOUT, ("Failed to wait on event multi sem %p, rc=%d.\n", pThis, rc));
+ ASMAtomicDecU32(&pThis->cWaiters);
+ int rc2 = pthread_mutex_unlock(&pThis->Mutex);
+ AssertMsg(!rc2, ("Failed to unlock event multi sem %p, rc=%d.\n", pThis, rc2)); NOREF(rc2);
+ return RTErrConvertFromErrno(rc);
+ }
+ }
+}
+
+
+DECLINLINE(int) rtSemEventMultiPosixWait(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ uint32_t u32 = pThis->u32State;
+ AssertReturn(u32 == EVENTMULTI_STATE_NOT_SIGNALED || u32 == EVENTMULTI_STATE_SIGNALED, VERR_INVALID_HANDLE);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Optimize the case where the event is signalled.
+ */
+ if (ASMAtomicUoReadU32(&pThis->u32State) == EVENTMULTI_STATE_SIGNALED)
+ {
+ int rc = rtSemEventMultiPosixWaitPoll(pThis);
+ if (RT_LIKELY(rc != VERR_TIMEOUT))
+ return rc;
+ }
+
+ /*
+ * Indefinite or timed wait?
+ */
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ return rtSemEventMultiPosixWaitIndefinite(pThis, fFlags, pSrcPos);
+ return rtSemEventMultiPosixWaitTimed(pThis, fFlags, uTimeout, pSrcPos);
+}
+
+
+#undef RTSemEventMultiWaitEx
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtSemEventMultiPosixWait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemEventMultiPosixWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemEventMultiPosixWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENTMULTI_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ uint32_t u32 = pThis->u32State;
+ AssertReturnVoid(u32 == EVENTMULTI_STATE_NOT_SIGNALED || u32 == EVENTMULTI_STATE_SIGNALED);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENTMULTI_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ uint32_t u32 = pThis->u32State;
+ AssertReturnVoid(u32 == EVENTMULTI_STATE_NOT_SIGNALED || u32 == EVENTMULTI_STATE_SIGNALED);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENTMULTI_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ uint32_t u32 = pThis->u32State;
+ AssertReturnVoid(u32 == EVENTMULTI_STATE_NOT_SIGNALED || u32 == EVENTMULTI_STATE_SIGNALED);
+
+ RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
+#else
+ RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/posix/semmutex-posix.cpp b/src/VBox/Runtime/r3/posix/semmutex-posix.cpp
new file mode 100644
index 00000000..f0df48ef
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/semmutex-posix.cpp
@@ -0,0 +1,467 @@
+/* $Id: semmutex-posix.cpp $ */
+/** @file
+ * IPRT - Mutex Semaphore, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Posix internal representation of a Mutex semaphore. */
+struct RTSEMMUTEXINTERNAL
+{
+ /** pthread mutex. */
+ pthread_mutex_t Mutex;
+ /** The owner of the mutex. */
+ volatile pthread_t Owner;
+ /** Nesting count. */
+ volatile uint32_t cNesting;
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t u32Magic;
+#ifdef RTSEMMUTEX_STRICT
+ /** Lock validator record associated with this mutex. */
+ RTLOCKVALRECEXCL ValidatorRec;
+#endif
+};
+
+#if defined(RT_OS_DARWIN) || defined(RT_OS_NETBSD)
+/**
+ * This function is a crude approximation of pthread_mutex_timedlock.
+ */
+int rtSemFallbackPthreadMutexTimedlock(pthread_mutex_t *mutex, RTMSINTERVAL cMillies)
+{
+ struct timespec ts;
+ int rc;
+
+ rc = pthread_mutex_trylock(mutex);
+ if (rc != EBUSY)
+ return rc;
+
+ ts.tv_sec = cMillies / 1000;
+ ts.tv_nsec = (cMillies % 1000) * 1000000;
+
+ while (ts.tv_sec > 0 || ts.tv_nsec > 0)
+ {
+ struct timespec delta, remaining;
+
+ if (ts.tv_sec > 0)
+ {
+ delta.tv_sec = 1;
+ delta.tv_nsec = 0;
+ ts.tv_sec--;
+ }
+ else
+ {
+ delta.tv_sec = 0;
+ delta.tv_nsec = ts.tv_nsec;
+ ts.tv_nsec = 0;
+ }
+
+ nanosleep(&delta, &remaining);
+
+ rc = pthread_mutex_trylock(mutex);
+ if (rc != EBUSY)
+ return rc;
+
+ if (RT_UNLIKELY(remaining.tv_nsec > 0 || remaining.tv_sec > 0))
+ {
+ ts.tv_sec += remaining.tv_sec;
+ ts.tv_nsec += remaining.tv_nsec;
+ if (ts.tv_nsec >= 1000000000)
+ {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec++;
+ }
+ }
+ }
+
+ return ETIMEDOUT;
+}
+#endif
+
+
+#undef RTSemMutexCreate
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+}
+
+
+RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate semaphore handle.
+ */
+ int rc;
+ struct RTSEMMUTEXINTERNAL *pThis = (struct RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(struct RTSEMMUTEXINTERNAL));
+ if (pThis)
+ {
+ /*
+ * Create the semaphore.
+ */
+ rc = pthread_mutex_init(&pThis->Mutex, NULL);
+ if (!rc)
+ {
+ pThis->Owner = (pthread_t)-1;
+ pThis->cNesting = 0;
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+#ifdef RTSEMMUTEX_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iMutexAnon = 0;
+ RTLockValidatorRecExclInit(&pThis->ValidatorRec, hClass, uSubClass, pThis,
+ !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL),
+ "RTSemMutex-%u", ASMAtomicIncU32(&s_iMutexAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecExclInitV(&pThis->ValidatorRec, hClass, uSubClass, pThis,
+ !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL), pszNameFmt, va);
+ va_end(va);
+ }
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(uSubClass); RT_NOREF_PV(pszNameFmt);
+#endif
+
+ *phMutexSem = pThis;
+ return VINF_SUCCESS;
+ }
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate input.
+ */
+ if (hMutexSem == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+ struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Try destroy it.
+ */
+ int rc = pthread_mutex_destroy(&pThis->Mutex);
+ if (rc)
+ {
+ AssertMsgFailed(("Failed to destroy mutex sem %p, rc=%d.\n", hMutexSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ /*
+ * Free the memory and be gone.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD);
+ pThis->Owner = (pthread_t)-1;
+ pThis->cNesting = UINT32_MAX;
+#ifdef RTSEMMUTEX_STRICT
+ RTLockValidatorRecExclDelete(&pThis->ValidatorRec);
+#endif
+ RTMemTmpFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTSemMutexSetSubClass(RTSEMMUTEX hMutexSem, uint32_t uSubClass)
+{
+#ifdef RTSEMMUTEX_STRICT
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+
+ return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorRec, uSubClass);
+#else
+ RT_NOREF_PV(hMutexSem); RT_NOREF_PV(uSubClass);
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+#endif
+}
+
+
+DECL_FORCE_INLINE(int) rtSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Check if nested request.
+ */
+ pthread_t Self = pthread_self();
+ if ( pThis->Owner == Self
+ && pThis->cNesting > 0)
+ {
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclRecursion(&pThis->ValidatorRec, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ ASMAtomicIncU32(&pThis->cNesting);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Lock it.
+ */
+ RTTHREAD hThreadSelf = NIL_RTTHREAD;
+ if (cMillies != 0)
+ {
+#ifdef RTSEMMUTEX_STRICT
+ hThreadSelf = RTThreadSelfAutoAdopt();
+ int rc9 = RTLockValidatorRecExclCheckOrderAndBlocking(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true,
+ cMillies, RTTHREADSTATE_MUTEX, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#else
+ hThreadSelf = RTThreadSelf();
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_MUTEX, true);
+ RT_NOREF_PV(pSrcPos);
+#endif
+ }
+
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ /* take mutex */
+ int rc = pthread_mutex_lock(&pThis->Mutex);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX);
+ if (rc)
+ {
+ AssertMsgFailed(("Failed to lock mutex sem %p, rc=%d.\n", hMutexSem, rc)); NOREF(rc);
+ return RTErrConvertFromErrno(rc);
+ }
+ }
+ else
+ {
+ int rc;
+#if !defined(RT_OS_DARWIN) && !defined(RT_OS_NETBSD)
+ struct timespec ts = {0,0};
+# if defined(RT_OS_HAIKU)
+ struct timeval tv = {0,0};
+ gettimeofday(&tv, NULL);
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+# else
+ clock_gettime(CLOCK_REALTIME, &ts);
+# endif
+ if (cMillies != 0)
+ {
+ ts.tv_nsec += (cMillies % 1000) * 1000000;
+ ts.tv_sec += cMillies / 1000;
+ if (ts.tv_nsec >= 1000000000)
+ {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec++;
+ }
+ }
+
+ /* take mutex */
+ rc = pthread_mutex_timedlock(&pThis->Mutex, &ts);
+#else
+ /*
+ * When there's no pthread_mutex_timedlock() use a crude sleep
+ * and retry approximation. Since the sleep interval is
+ * relative, we don't need to convert to the absolute time
+ * here only to convert back to relative in the fallback
+ * function.
+ */
+ rc = rtSemFallbackPthreadMutexTimedlock(&pThis->Mutex, cMillies);
+#endif
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX);
+ if (rc)
+ {
+ AssertMsg(rc == ETIMEDOUT, ("Failed to lock mutex sem %p, rc=%d.\n", hMutexSem, rc)); NOREF(rc);
+ return RTErrConvertFromErrno(rc);
+ }
+ }
+
+ /*
+ * Set the owner and nesting.
+ */
+ pThis->Owner = Self;
+ ASMAtomicWriteU32(&pThis->cNesting, 1);
+#ifdef RTSEMMUTEX_STRICT
+ RTLockValidatorRecExclSetOwner(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true);
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+#undef RTSemMutexRequest
+RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMMUTEX_STRICT
+ return rtSemMutexRequest(hMutexSem, cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemMutexRequest(hMutexSem, cMillies, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemMutexRequest(hMutexSem, cMillies, &SrcPos);
+}
+
+
+#undef RTSemMutexRequestNoResume
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ /* (EINTR isn't returned by the wait functions we're using.) */
+#ifndef RTSEMMUTEX_STRICT
+ return rtSemMutexRequest(hMutexSem, cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemMutexRequest(hMutexSem, cMillies, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemMutexRequest(hMutexSem, cMillies, &SrcPos);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorRec, pThis->cNesting == 1);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+
+ /*
+ * Check if nested.
+ */
+ pthread_t Self = pthread_self();
+ if (RT_UNLIKELY( pThis->Owner != Self
+ || pThis->cNesting == 0))
+ {
+ AssertMsgFailed(("Not owner of mutex %p!! Self=%08x Owner=%08x cNesting=%d\n",
+ pThis, Self, pThis->Owner, pThis->cNesting));
+ return VERR_NOT_OWNER;
+ }
+
+ /*
+ * If nested we'll just pop a nesting.
+ */
+ if (pThis->cNesting > 1)
+ {
+ ASMAtomicDecU32(&pThis->cNesting);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Clear the state. (cNesting == 1)
+ */
+ pThis->Owner = (pthread_t)-1;
+ ASMAtomicWriteU32(&pThis->cNesting, 0);
+
+ /*
+ * Unlock mutex semaphore.
+ */
+ int rc = pthread_mutex_unlock(&pThis->Mutex);
+ if (RT_UNLIKELY(rc))
+ {
+ AssertMsgFailed(("Failed to unlock mutex sem %p, rc=%d.\n", hMutexSem, rc)); NOREF(rc);
+ return RTErrConvertFromErrno(rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false);
+
+ return pThis->Owner != (pthread_t)-1;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/semrw-posix.cpp b/src/VBox/Runtime/r3/posix/semrw-posix.cpp
new file mode 100644
index 00000000..ab657566
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/semrw-posix.cpp
@@ -0,0 +1,741 @@
+/* $Id: semrw-posix.cpp $ */
+/** @file
+ * IPRT - Read-Write Semaphore, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @todo move this to r3/posix/something.h. */
+#ifdef RT_OS_SOLARIS
+# define ATOMIC_GET_PTHREAD_T(ppvVar, pThread) ASMAtomicReadSize(ppvVar, pThread)
+# define ATOMIC_SET_PTHREAD_T(ppvVar, pThread) ASMAtomicWriteSize(ppvVar, pThread)
+#else
+AssertCompileSize(pthread_t, sizeof(void *));
+# define ATOMIC_GET_PTHREAD_T(ppvVar, pThread) do { *(pThread) = (pthread_t)ASMAtomicReadPtr((void * volatile *)ppvVar); } while (0)
+# define ATOMIC_SET_PTHREAD_T(ppvVar, pThread) ASMAtomicWritePtr((void * volatile *)ppvVar, (void *)pThread)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Posix internal representation of a read-write semaphore. */
+struct RTSEMRWINTERNAL
+{
+ /** The usual magic. (RTSEMRW_MAGIC) */
+ uint32_t u32Magic;
+ /** The number of readers.
+ * (For preventing screwing up the lock on linux). */
+ uint32_t volatile cReaders;
+ /** Number of write recursions. */
+ uint32_t cWrites;
+ /** Number of read recursions by the writer. */
+ uint32_t cWriterReads;
+ /** The write owner of the lock. */
+ volatile pthread_t Writer;
+ /** pthread rwlock. */
+ pthread_rwlock_t RWLock;
+#ifdef RTSEMRW_STRICT
+ /** The validator record for the writer. */
+ RTLOCKVALRECEXCL ValidatorWrite;
+ /** The validator record for the readers. */
+ RTLOCKVALRECSHRD ValidatorRead;
+#endif
+};
+
+
+
+#undef RTSemRWCreate
+RTDECL(int) RTSemRWCreate(PRTSEMRW phRWSem)
+{
+ return RTSemRWCreateEx(phRWSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTSemRW");
+}
+
+
+RTDECL(int) RTSemRWCreateEx(PRTSEMRW phRWSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMRW_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate handle.
+ */
+ int rc;
+ struct RTSEMRWINTERNAL *pThis = (struct RTSEMRWINTERNAL *)RTMemAlloc(sizeof(struct RTSEMRWINTERNAL));
+ if (pThis)
+ {
+ /*
+ * Create the rwlock.
+ */
+ rc = pthread_rwlock_init(&pThis->RWLock, NULL);
+ if (!rc)
+ {
+ pThis->u32Magic = RTSEMRW_MAGIC;
+ pThis->cReaders = 0;
+ pThis->cWrites = 0;
+ pThis->cWriterReads = 0;
+ pThis->Writer = (pthread_t)-1;
+#ifdef RTSEMRW_STRICT
+ bool const fLVEnabled = !(fFlags & RTSEMRW_FLAGS_NO_LOCK_VAL);
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemRWAnon = 0;
+ uint32_t i = ASMAtomicIncU32(&s_iSemRWAnon) - 1;
+ RTLockValidatorRecExclInit(&pThis->ValidatorWrite, hClass, uSubClass, pThis,
+ fLVEnabled, "RTSemRW-%u", i);
+ RTLockValidatorRecSharedInit(&pThis->ValidatorRead, hClass, uSubClass, pThis,
+ false /*fSignaller*/, fLVEnabled, "RTSemRW-%u", i);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecExclInitV(&pThis->ValidatorWrite, hClass, uSubClass, pThis,
+ fLVEnabled, pszNameFmt, va);
+ va_end(va);
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->ValidatorRead, hClass, uSubClass, pThis,
+ false /*fSignaller*/, fLVEnabled, pszNameFmt, va);
+ va_end(va);
+ }
+ RTLockValidatorRecMakeSiblings(&pThis->ValidatorWrite.Core, &pThis->ValidatorRead.Core);
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(uSubClass); RT_NOREF_PV(pszNameFmt);
+#endif
+ *phRWSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromErrno(rc);
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTSemRWDestroy(RTSEMRW hRWSem)
+{
+ /*
+ * Validate input, nil handle is fine.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ if (pThis == NIL_RTSEMRW)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+ Assert(pThis->Writer == (pthread_t)-1);
+ Assert(!pThis->cReaders);
+ Assert(!pThis->cWrites);
+ Assert(!pThis->cWriterReads);
+
+ /*
+ * Try destroy it.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTSEMRW_MAGIC, RTSEMRW_MAGIC), VERR_INVALID_HANDLE);
+ int rc = pthread_rwlock_destroy(&pThis->RWLock);
+ if (!rc)
+ {
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->ValidatorRead);
+ RTLockValidatorRecExclDelete(&pThis->ValidatorWrite);
+#endif
+ RTMemFree(pThis);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMRW_MAGIC);
+ AssertMsgFailed(("Failed to destroy read-write sem %p, rc=%d.\n", hRWSem, rc));
+ rc = RTErrConvertFromErrno(rc);
+ }
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTSemRWSetSubClass(RTSEMRW hRWSem, uint32_t uSubClass)
+{
+#ifdef RTSEMRW_STRICT
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+
+ RTLockValidatorRecSharedSetSubClass(&pThis->ValidatorRead, uSubClass);
+ return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorWrite, uSubClass);
+#else
+ RT_NOREF_PV(hRWSem); RT_NOREF_PV(uSubClass);
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+#endif
+}
+
+
+DECL_FORCE_INLINE(int) rtSemRWRequestRead(RTSEMRW hRWSem, RTMSINTERVAL cMillies, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+
+ /*
+ * Check if it's the writer (implement write+read recursion).
+ */
+ pthread_t Self = pthread_self();
+ pthread_t Writer;
+ ATOMIC_GET_PTHREAD_T(&pThis->Writer, &Writer);
+ if (Writer == Self)
+ {
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecExclRecursionMixed(&pThis->ValidatorWrite, &pThis->ValidatorRead.Core, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ Assert(pThis->cWriterReads < INT32_MAX);
+ pThis->cWriterReads++;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Try lock it.
+ */
+ RTTHREAD hThreadSelf = NIL_RTTHREAD;
+ if (cMillies > 0)
+ {
+#ifdef RTSEMRW_STRICT
+ hThreadSelf = RTThreadSelfAutoAdopt();
+ int rc9 = RTLockValidatorRecSharedCheckOrderAndBlocking(&pThis->ValidatorRead, hThreadSelf, pSrcPos, true,
+ cMillies, RTTHREADSTATE_RW_READ, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#else
+ hThreadSelf = RTThreadSelf();
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_RW_READ, true);
+ RT_NOREF_PV(pSrcPos);
+#endif
+ }
+
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ /* take rwlock */
+ int rc = pthread_rwlock_rdlock(&pThis->RWLock);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_RW_READ);
+ if (rc)
+ {
+ AssertMsgFailed(("Failed read lock read-write sem %p, rc=%d.\n", hRWSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+ }
+ else
+ {
+#ifdef RT_OS_DARWIN
+ AssertMsgFailed(("Not implemented on Darwin yet because of incomplete pthreads API."));
+ return VERR_NOT_IMPLEMENTED;
+
+#else /* !RT_OS_DARWIN */
+ /*
+ * Get current time and calc end of wait time.
+ */
+ struct timespec ts = {0,0};
+ clock_gettime(CLOCK_REALTIME, &ts);
+ if (cMillies != 0)
+ {
+ ts.tv_nsec += (cMillies % 1000) * 1000000;
+ ts.tv_sec += cMillies / 1000;
+ if (ts.tv_nsec >= 1000000000)
+ {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec++;
+ }
+ }
+
+ /* take rwlock */
+ int rc = pthread_rwlock_timedrdlock(&pThis->RWLock, &ts);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_RW_READ);
+ if (rc)
+ {
+ AssertMsg(rc == ETIMEDOUT, ("Failed read lock read-write sem %p, rc=%d.\n", hRWSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+#endif /* !RT_OS_DARWIN */
+ }
+
+ ASMAtomicIncU32(&pThis->cReaders);
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecSharedAddOwner(&pThis->ValidatorRead, hThreadSelf, pSrcPos);
+#endif
+ return VINF_SUCCESS;
+}
+
+
+#undef RTSemRWRequestRead
+RTDECL(int) RTSemRWRequestRead(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestRead(hRWSem, cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemRWRequestReadDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, &SrcPos);
+}
+
+
+#undef RTSemRWRequestReadNoResume
+RTDECL(int) RTSemRWRequestReadNoResume(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+ /* EINTR isn't returned by the wait functions we're using. */
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestRead(hRWSem, cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemRWRequestReadNoResumeDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestRead(hRWSem, cMillies, &SrcPos);
+}
+
+
+RTDECL(int) RTSemRWReleaseRead(RTSEMRW hRWSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+
+ /*
+ * Check if it's the writer.
+ */
+ pthread_t Self = pthread_self();
+ pthread_t Writer;
+ ATOMIC_GET_PTHREAD_T(&pThis->Writer, &Writer);
+ if (Writer == Self)
+ {
+ AssertMsgReturn(pThis->cWriterReads > 0, ("pThis=%p\n", pThis), VERR_NOT_OWNER);
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecExclUnwindMixed(&pThis->ValidatorWrite, &pThis->ValidatorRead.Core);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ pThis->cWriterReads--;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Try unlock it.
+ */
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecSharedCheckAndRelease(&pThis->ValidatorRead, RTThreadSelf());
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+#ifdef RT_OS_LINUX /* glibc (at least 2.8) may screw up when unlocking a lock we don't own. */
+ if (ASMAtomicReadU32(&pThis->cReaders) == 0)
+ {
+ AssertMsgFailed(("Not owner of %p\n", pThis));
+ return VERR_NOT_OWNER;
+ }
+#endif
+ ASMAtomicDecU32(&pThis->cReaders);
+ int rc = pthread_rwlock_unlock(&pThis->RWLock);
+ if (rc)
+ {
+ ASMAtomicIncU32(&pThis->cReaders);
+ AssertMsgFailed(("Failed read unlock read-write sem %p, rc=%d.\n", hRWSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+ return VINF_SUCCESS;
+}
+
+
+DECL_FORCE_INLINE(int) rtSemRWRequestWrite(RTSEMRW hRWSem, RTMSINTERVAL cMillies, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+
+ /*
+ * Recursion?
+ */
+ pthread_t Self = pthread_self();
+ pthread_t Writer;
+ ATOMIC_GET_PTHREAD_T(&pThis->Writer, &Writer);
+ if (Writer == Self)
+ {
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecExclRecursion(&pThis->ValidatorWrite, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ Assert(pThis->cWrites < INT32_MAX);
+ pThis->cWrites++;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Try lock it.
+ */
+ RTTHREAD hThreadSelf = NIL_RTTHREAD;
+ if (cMillies)
+ {
+#ifdef RTSEMRW_STRICT
+ hThreadSelf = RTThreadSelfAutoAdopt();
+ int rc9 = RTLockValidatorRecExclCheckOrderAndBlocking(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, true,
+ cMillies, RTTHREADSTATE_RW_WRITE, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#else
+ hThreadSelf = RTThreadSelf();
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_RW_WRITE, true);
+ RT_NOREF_PV(pSrcPos);
+#endif
+ }
+
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ /* take rwlock */
+ int rc = pthread_rwlock_wrlock(&pThis->RWLock);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_RW_WRITE);
+ if (rc)
+ {
+ AssertMsgFailed(("Failed write lock read-write sem %p, rc=%d.\n", hRWSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+ }
+ else
+ {
+#ifdef RT_OS_DARWIN
+ AssertMsgFailed(("Not implemented on Darwin yet because of incomplete pthreads API."));
+ return VERR_NOT_IMPLEMENTED;
+#else /* !RT_OS_DARWIN */
+ /*
+ * Get current time and calc end of wait time.
+ */
+ struct timespec ts = {0,0};
+ clock_gettime(CLOCK_REALTIME, &ts);
+ if (cMillies != 0)
+ {
+ ts.tv_nsec += (cMillies % 1000) * 1000000;
+ ts.tv_sec += cMillies / 1000;
+ if (ts.tv_nsec >= 1000000000)
+ {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec++;
+ }
+ }
+
+ /* take rwlock */
+ int rc = pthread_rwlock_timedwrlock(&pThis->RWLock, &ts);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_RW_WRITE);
+ if (rc)
+ {
+ AssertMsg(rc == ETIMEDOUT, ("Failed read lock read-write sem %p, rc=%d.\n", hRWSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+#endif /* !RT_OS_DARWIN */
+ }
+
+ ATOMIC_SET_PTHREAD_T(&pThis->Writer, Self);
+ pThis->cWrites = 1;
+ Assert(!pThis->cReaders);
+#ifdef RTSEMRW_STRICT
+ RTLockValidatorRecExclSetOwner(&pThis->ValidatorWrite, hThreadSelf, pSrcPos, true);
+#endif
+ return VINF_SUCCESS;
+}
+
+
+#undef RTSemRWRequestWrite
+RTDECL(int) RTSemRWRequestWrite(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestWrite(hRWSem, cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemRWRequestWriteDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, &SrcPos);
+}
+
+
+#undef RTSemRWRequestWriteNoResume
+RTDECL(int) RTSemRWRequestWriteNoResume(RTSEMRW hRWSem, RTMSINTERVAL cMillies)
+{
+ /* EINTR isn't returned by the wait functions we're using. */
+#ifndef RTSEMRW_STRICT
+ return rtSemRWRequestWrite(hRWSem, cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemRWRequestWriteNoResumeDebug(RTSEMRW hRWSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ /* EINTR isn't returned by the wait functions we're using. */
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemRWRequestWrite(hRWSem, cMillies, &SrcPos);
+}
+
+
+RTDECL(int) RTSemRWReleaseWrite(RTSEMRW hRWSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+
+ /*
+ * Verify ownership and implement recursion.
+ */
+ pthread_t Self = pthread_self();
+ pthread_t Writer;
+ ATOMIC_GET_PTHREAD_T(&pThis->Writer, &Writer);
+ AssertMsgReturn(Writer == Self, ("pThis=%p\n", pThis), VERR_NOT_OWNER);
+ AssertReturn(pThis->cWriterReads == 0 || pThis->cWrites > 1, VERR_WRONG_ORDER);
+
+ if (pThis->cWrites > 1)
+ {
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecExclUnwind(&pThis->ValidatorWrite);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ pThis->cWrites--;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Try unlock it.
+ */
+#ifdef RTSEMRW_STRICT
+ int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorWrite, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+
+ pThis->cWrites--;
+ ATOMIC_SET_PTHREAD_T(&pThis->Writer, (pthread_t)-1);
+ int rc = pthread_rwlock_unlock(&pThis->RWLock);
+ if (rc)
+ {
+ AssertMsgFailed(("Failed write unlock read-write sem %p, rc=%d.\n", hRWSem, rc));
+ return RTErrConvertFromErrno(rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTSemRWIsWriteOwner(RTSEMRW hRWSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, false);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ false);
+
+ /*
+ * Check ownership.
+ */
+ pthread_t Self = pthread_self();
+ pthread_t Writer;
+ ATOMIC_GET_PTHREAD_T(&pThis->Writer, &Writer);
+ return Writer == Self;
+}
+
+
+RTDECL(bool) RTSemRWIsReadOwner(RTSEMRW hRWSem, bool fWannaHear)
+{
+ /*
+ * Validate handle.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMRW_MAGIC, false);
+
+ /*
+ * Check write ownership. The writer is also a valid reader.
+ */
+ pthread_t Self = pthread_self();
+ pthread_t Writer;
+ ATOMIC_GET_PTHREAD_T(&pThis->Writer, &Writer);
+ if (Writer == Self)
+ return true;
+ if (Writer != (pthread_t)-1)
+ return false;
+
+ /*
+ * If there are no readers, we cannot be one of them, can we?
+ */
+ if (ASMAtomicReadU32(&pThis->cReaders) == 0)
+ return false;
+
+#ifdef RTSEMRW_STRICT
+ /*
+ * Ask the lock validator.
+ */
+ NOREF(fWannaHear);
+ return RTLockValidatorRecSharedIsOwner(&pThis->ValidatorRead, NIL_RTTHREAD);
+#else
+ /*
+ * Just tell the caller what he want to hear.
+ */
+ return fWannaHear;
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemRWIsReadOwner);
+
+
+RTDECL(uint32_t) RTSemRWGetWriteRecursion(RTSEMRW hRWSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, 0);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ 0);
+
+ /*
+ * Return the requested data.
+ */
+ return pThis->cWrites;
+}
+
+
+RTDECL(uint32_t) RTSemRWGetWriterReadRecursion(RTSEMRW hRWSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, 0);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ 0);
+
+ /*
+ * Return the requested data.
+ */
+ return pThis->cWriterReads;
+}
+
+
+RTDECL(uint32_t) RTSemRWGetReadCount(RTSEMRW hRWSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMRWINTERNAL *pThis = hRWSem;
+ AssertPtrReturn(pThis, 0);
+ AssertMsgReturn(pThis->u32Magic == RTSEMRW_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ 0);
+
+ /*
+ * Return the requested data.
+ */
+ return pThis->cReaders;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/semwait.h b/src/VBox/Runtime/r3/posix/semwait.h
new file mode 100644
index 00000000..ade8c4a7
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/semwait.h
@@ -0,0 +1,162 @@
+/* $Id: semwait.h $ */
+/** @file
+ * IPRT - Common semaphore wait code.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_posix_semwait_h
+#define IPRT_INCLUDED_SRC_r3_posix_semwait_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+
+/** @def IPRT_HAVE_PTHREAD_CONDATTR_SETCLOCK
+ * Set if the platform implements pthread_condattr_setclock().
+ * Enables the use of the monotonic clock for waiting on condition variables. */
+#ifndef IPRT_HAVE_PTHREAD_CONDATTR_SETCLOCK
+/* Linux detection */
+# if defined(RT_OS_LINUX) && defined(__USE_XOPEN2K)
+# include <features.h>
+# if __GLIBC_PREREQ(2,6) /** @todo figure the exact version where this was added */
+# define IPRT_HAVE_PTHREAD_CONDATTR_SETCLOCK
+# endif
+# endif
+/** @todo check other platforms */
+#endif
+
+
+/**
+ * Converts a extended wait timeout specification to an absolute timespec and a
+ * relative nanosecond count.
+ *
+ * @note This does not check for RTSEMWAIT_FLAGS_INDEFINITE, caller should've
+ * done that already.
+ *
+ * @returns The relative wait in nanoseconds. 0 for a poll call, UINT64_MAX for
+ * an effectively indefinite wait.
+ * @param fFlags RTSEMWAIT_FLAGS_XXX.
+ * @param fMonotonicClock Whether the timeout is in monotonic (true) or real
+ * (false) time.
+ * @param uTimeout The timeout.
+ * @param pAbsDeadline Where to return the absolute deadline.
+ */
+DECLINLINE(uint64_t) rtSemPosixCalcDeadline(uint32_t fFlags, uint64_t uTimeout, bool fMonotonicClock,
+ struct timespec *pAbsDeadline)
+{
+ Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE));
+
+ /*
+ * Convert uTimeout to a relative value in nanoseconds.
+ */
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ {
+ if (uTimeout < UINT64_MAX / RT_NS_1MS)
+ uTimeout = uTimeout * RT_NS_1MS;
+ else
+ return UINT64_MAX;
+ }
+ else if (uTimeout == UINT64_MAX) /* unofficial way of indicating an indefinite wait */
+ return UINT64_MAX;
+
+ /*
+ * Make uTimeout relative and check for polling (zero timeout) calls.
+ */
+ uint64_t uAbsTimeout = uTimeout;
+ if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE)
+ {
+ uint64_t const u64Now = RTTimeSystemNanoTS();
+ if (uTimeout > u64Now)
+ uTimeout -= u64Now;
+ else
+ return 0;
+ }
+ else if (uTimeout == 0)
+ return 0;
+
+ /*
+ * Calculate the deadline according to the clock we're using.
+ */
+ if (!fMonotonicClock)
+ {
+#if defined(RT_OS_DARWIN) || defined(RT_OS_HAIKU)
+ struct timeval tv = {0,0};
+ gettimeofday(&tv, NULL);
+ pAbsDeadline->tv_sec = tv.tv_sec;
+ pAbsDeadline->tv_nsec = tv.tv_usec * 1000;
+#else
+ clock_gettime(CLOCK_REALTIME, pAbsDeadline);
+#endif
+ struct timespec TsAdd;
+ TsAdd.tv_nsec = uTimeout % RT_NS_1SEC;
+ TsAdd.tv_sec = uTimeout / RT_NS_1SEC;
+
+ /* Check for 32-bit tv_sec overflows: */
+ if ( sizeof(pAbsDeadline->tv_sec) < sizeof(uint64_t)
+ && ( uTimeout >= (uint64_t)RT_NS_1SEC * UINT32_MAX
+ || (uint64_t)pAbsDeadline->tv_sec + pAbsDeadline->tv_sec >= UINT32_MAX) )
+ return UINT64_MAX;
+
+ pAbsDeadline->tv_sec += TsAdd.tv_sec;
+ pAbsDeadline->tv_nsec += TsAdd.tv_nsec;
+ if ((uint32_t)pAbsDeadline->tv_nsec >= RT_NS_1SEC)
+ {
+ pAbsDeadline->tv_nsec -= RT_NS_1SEC;
+ pAbsDeadline->tv_sec++;
+ }
+ }
+ else
+ {
+ /* ASSUMES RTTimeSystemNanoTS() == RTTimeNanoTS() == clock_gettime(CLOCK_MONOTONIC). */
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
+ {
+ uint64_t const nsNow = RTTimeSystemNanoTS();
+ uAbsTimeout += nsNow;
+ if (uAbsTimeout < nsNow)
+ return UINT64_MAX;
+ }
+
+ /* Check for 32-bit tv_sec overflows: */
+ if ( sizeof(pAbsDeadline->tv_sec) < sizeof(uint64_t)
+ && uAbsTimeout >= (uint64_t)RT_NS_1SEC * UINT32_MAX)
+ return UINT64_MAX;
+
+ pAbsDeadline->tv_nsec = uAbsTimeout % RT_NS_1SEC;
+ pAbsDeadline->tv_sec = uAbsTimeout / RT_NS_1SEC;
+ }
+
+ return uTimeout;
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r3_posix_semwait_h */
+
diff --git a/src/VBox/Runtime/r3/posix/serialport-posix.cpp b/src/VBox/Runtime/r3/posix/serialport-posix.cpp
new file mode 100644
index 00000000..0afbb676
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/serialport-posix.cpp
@@ -0,0 +1,1269 @@
+/* $Id: serialport-posix.cpp $ */
+/** @file
+ * IPRT - Serial Port API, POSIX Implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/serialport.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/cdefs.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+
+#include <errno.h>
+#ifdef RT_OS_SOLARIS
+# include <sys/termios.h>
+#else
+# include <termios.h>
+#endif
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef RT_OS_DARWIN
+# include <sys/poll.h>
+#else
+# include <sys/poll.h>
+#endif
+#include <sys/ioctl.h>
+#include <pthread.h>
+
+#ifdef RT_OS_LINUX
+/*
+ * TIOCM_LOOP is not defined in the above header files for some reason but in asm/termios.h.
+ * But inclusion of this file however leads to compilation errors because of redefinition of some
+ * structs. That's why it is defined here until a better solution is found.
+ */
+# ifndef TIOCM_LOOP
+# define TIOCM_LOOP 0x8000
+# endif
+/* For linux custom baudrate code we also need serial_struct */
+# include <linux/serial.h>
+#endif /* linux */
+
+/** Define fallback if not supported. */
+#if !defined(CMSPAR)
+# define CMSPAR 0
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal serial port state.
+ */
+typedef struct RTSERIALPORTINTERNAL
+{
+ /** Magic value (RTSERIALPORT_MAGIC). */
+ uint32_t u32Magic;
+ /** Flags given while opening the serial port. */
+ uint32_t fOpenFlags;
+ /** The file descriptor of the serial port. */
+ int iFd;
+ /** The status line monitor thread if enabled. */
+ RTTHREAD hMonThrd;
+ /** Flag whether the monitoring thread should shutdown. */
+ volatile bool fMonThrdShutdown;
+ /** Reading end of wakeup pipe. */
+ int iFdPipeR;
+ /** Writing end of wakeup pipe. */
+ int iFdPipeW;
+ /** Event pending mask. */
+ volatile uint32_t fEvtsPending;
+ /** Flag whether we are in blocking or non blocking mode. */
+ bool fBlocking;
+ /** The current active config (we assume no one changes this behind our back). */
+ struct termios PortCfg;
+ /** Flag whether a custom baud rate was chosen (for hosts supporting this.). */
+ bool fBaudrateCust;
+ /** The custom baud rate. */
+ uint32_t uBaudRateCust;
+} RTSERIALPORTINTERNAL;
+/** Pointer to the internal serial port state. */
+typedef RTSERIALPORTINTERNAL *PRTSERIALPORTINTERNAL;
+
+
+/**
+ * Baud rate conversion table descriptor.
+ */
+typedef struct RTSERIALPORTBRATECONVDESC
+{
+ /** The platform independent baud rate used by the RTSerialPort* API. */
+ uint32_t uBaudRateCfg;
+ /** The speed identifier used in the termios structure. */
+ speed_t iSpeedTermios;
+} RTSERIALPORTBRATECONVDESC;
+/** Pointer to a baud rate converions table descriptor. */
+typedef RTSERIALPORTBRATECONVDESC *PRTSERIALPORTBRATECONVDESC;
+/** Pointer to a const baud rate conversion table descriptor. */
+typedef const RTSERIALPORTBRATECONVDESC *PCRTSERIALPORTBRATECONVDESC;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** The event poller was woken up due to an external interrupt. */
+#define RTSERIALPORT_WAKEUP_PIPE_REASON_INTERRUPT 0x0
+/** The event poller was woken up due to a change in the monitored status lines. */
+#define RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_CHANGED 0x1
+/** The monitor thread encoutnered repeating errors querying the status lines and terminated. */
+#define RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_MONITOR_FAILED 0x2
+
+
+/*********************************************************************************************************************************
+* Global variables *
+*********************************************************************************************************************************/
+
+/** The baud rate conversion table. */
+static const RTSERIALPORTBRATECONVDESC s_rtSerialPortBaudrateConv[] =
+{
+ { 50, B50 },
+ { 75, B75 },
+ { 110, B110 },
+ { 134, B134 },
+ { 150, B150 },
+ { 200, B200 },
+ { 300, B300 },
+ { 600, B600 },
+ { 1200, B1200 },
+ { 1800, B1800 },
+ { 2400, B2400 },
+ { 4800, B4800 },
+ { 9600, B9600 },
+ { 19200, B19200 },
+ { 38400, B38400 },
+ { 57600, B57600 },
+ { 115200, B115200 }
+};
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Converts the given termios speed identifier to the baud rate used in the API.
+ *
+ * @returns Baud rate or 0 if not a standard baud rate
+ */
+DECLINLINE(uint32_t) rtSerialPortGetBaudrateFromTermiosSpeed(speed_t enmSpeed)
+{
+ for (unsigned i = 0; i < RT_ELEMENTS(s_rtSerialPortBaudrateConv); i++)
+ {
+ if (s_rtSerialPortBaudrateConv[i].iSpeedTermios == enmSpeed)
+ return s_rtSerialPortBaudrateConv[i].uBaudRateCfg;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Converts the given baud rate to proper termios speed identifier.
+ *
+ * @returns Speed identifier if available or B0 if no matching speed for the baud rate
+ * could be found.
+ * @param uBaudRate The baud rate to convert.
+ * @param pfBaudrateCust Where to store the flag whether a custom baudrate was selected.
+ */
+DECLINLINE(speed_t) rtSerialPortGetTermiosSpeedFromBaudrate(uint32_t uBaudRate, bool *pfBaudrateCust)
+{
+ *pfBaudrateCust = false;
+
+ for (unsigned i = 0; i < RT_ELEMENTS(s_rtSerialPortBaudrateConv); i++)
+ {
+ if (s_rtSerialPortBaudrateConv[i].uBaudRateCfg == uBaudRate)
+ return s_rtSerialPortBaudrateConv[i].iSpeedTermios;
+ }
+
+#ifdef RT_OS_LINUX
+ *pfBaudrateCust = true;
+ return B38400;
+#else
+ return B0;
+#endif
+}
+
+
+/**
+ * Tries to set the default config on the given serial port.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ */
+static int rtSerialPortSetDefaultCfg(PRTSERIALPORTINTERNAL pThis)
+{
+ pThis->fBaudrateCust = false;
+ pThis->uBaudRateCust = 0;
+ pThis->PortCfg.c_iflag = INPCK; /* Input parity checking. */
+ cfsetispeed(&pThis->PortCfg, B9600);
+ cfsetospeed(&pThis->PortCfg, B9600);
+ pThis->PortCfg.c_cflag |= CS8 | CLOCAL; /* 8 data bits, ignore modem control lines. */
+ if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_READ)
+ pThis->PortCfg.c_cflag |= CREAD; /* Enable receiver. */
+
+ /* Set to raw input mode. */
+ pThis->PortCfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ECHOK | ISIG | IEXTEN);
+ pThis->PortCfg.c_cc[VMIN] = 0; /* Achieve non-blocking behavior. */
+ pThis->PortCfg.c_cc[VTIME] = 0;
+
+ int rc = VINF_SUCCESS;
+ int rcPsx = tcflush(pThis->iFd, TCIOFLUSH);
+ if (!rcPsx)
+ {
+ rcPsx = tcsetattr(pThis->iFd, TCSANOW, &pThis->PortCfg);
+ if (rcPsx == -1)
+ rc = RTErrConvertFromErrno(errno);
+
+ if (RT_SUCCESS(rc))
+ {
+#ifdef RT_OS_LINUX
+ if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_ENABLE_LOOPBACK)
+ {
+ int fTiocmSet = TIOCM_LOOP;
+ rcPsx = ioctl(pThis->iFd, TIOCMBIS, &fTiocmSet);
+ if (rcPsx == -1)
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ {
+ /* Make sure it is clear. */
+ int fTiocmClear = TIOCM_LOOP;
+ rcPsx = ioctl(pThis->iFd, TIOCMBIC, &fTiocmClear);
+ if (rcPsx == -1 && errno != EINVAL) /* Pseudo terminals don't support loopback mode so ignore an error here. */
+ rc = RTErrConvertFromErrno(errno);
+ }
+#else
+ if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_ENABLE_LOOPBACK)
+ return VERR_NOT_SUPPORTED;
+#endif
+ }
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ return rc;
+}
+
+
+/**
+ * Converts the given serial port config to the appropriate termios counterpart.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param pCfg Pointer to the serial port config descriptor.
+ * @param pTermios Pointer to the termios structure to fill.
+ * @param pfBaudrateCust Where to store the flag whether a custom baudrate was selected.
+ * @param pErrInfo Additional error to be set when the conversion fails.
+ */
+static int rtSerialPortCfg2Termios(PRTSERIALPORTINTERNAL pThis, PCRTSERIALPORTCFG pCfg, struct termios *pTermios,
+ bool *pfBaudrateCust, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(pErrInfo); /** @todo Make use of the error info. */
+ speed_t enmSpeed = rtSerialPortGetTermiosSpeedFromBaudrate(pCfg->uBaudRate, pfBaudrateCust);
+ if (enmSpeed != B0)
+ {
+ tcflag_t const fCFlagMask = (CS5 | CS6 | CS7 | CS8 | CSTOPB | PARENB | PARODD | CMSPAR);
+ tcflag_t fCFlagNew = CLOCAL;
+
+ switch (pCfg->enmDataBitCount)
+ {
+ case RTSERIALPORTDATABITS_5BITS:
+ fCFlagNew |= CS5;
+ break;
+ case RTSERIALPORTDATABITS_6BITS:
+ fCFlagNew |= CS6;
+ break;
+ case RTSERIALPORTDATABITS_7BITS:
+ fCFlagNew |= CS7;
+ break;
+ case RTSERIALPORTDATABITS_8BITS:
+ fCFlagNew |= CS8;
+ break;
+ default:
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ switch (pCfg->enmParity)
+ {
+ case RTSERIALPORTPARITY_NONE:
+ break;
+ case RTSERIALPORTPARITY_EVEN:
+ fCFlagNew |= PARENB;
+ break;
+ case RTSERIALPORTPARITY_ODD:
+ fCFlagNew |= PARENB | PARODD;
+ break;
+#if CMSPAR != 0
+ case RTSERIALPORTPARITY_MARK:
+ fCFlagNew |= PARENB | CMSPAR | PARODD;
+ break;
+ case RTSERIALPORTPARITY_SPACE:
+ fCFlagNew |= PARENB | CMSPAR;
+ break;
+#else
+ case RTSERIALPORTPARITY_MARK:
+ case RTSERIALPORTPARITY_SPACE:
+ return VERR_NOT_SUPPORTED;
+#endif
+ default:
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ switch (pCfg->enmStopBitCount)
+ {
+ case RTSERIALPORTSTOPBITS_ONE:
+ break;
+ case RTSERIALPORTSTOPBITS_ONEPOINTFIVE:
+ if (pCfg->enmDataBitCount == RTSERIALPORTDATABITS_5BITS)
+ fCFlagNew |= CSTOPB;
+ else
+ return VERR_NOT_SUPPORTED;
+ break;
+ case RTSERIALPORTSTOPBITS_TWO:
+ if (pCfg->enmDataBitCount != RTSERIALPORTDATABITS_5BITS)
+ fCFlagNew |= CSTOPB;
+ else
+ return VERR_NOT_SUPPORTED;
+ break;
+ default:
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Assign new flags. */
+ if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_READ)
+ pTermios->c_cflag |= CREAD; /* Enable receiver. */
+ pTermios->c_cflag = (pTermios->c_cflag & ~fCFlagMask) | fCFlagNew;
+ pTermios->c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ECHOK | ISIG | IEXTEN);
+ pTermios->c_iflag = INPCK; /* Input parity checking. */
+ pTermios->c_cc[VMIN] = 0; /* Achieve non-blocking behavior. */
+ pTermios->c_cc[VTIME] = 0;
+ cfsetispeed(pTermios, enmSpeed);
+ cfsetospeed(pTermios, enmSpeed);
+ }
+ else
+ return VERR_SERIALPORT_INVALID_BAUDRATE;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Converts the given termios structure to an appropriate serial port config.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param pTermios The termios structure to convert.
+ * @param pCfg The serial port config to fill in.
+ */
+static int rtSerialPortTermios2Cfg(PRTSERIALPORTINTERNAL pThis, struct termios *pTermios, PRTSERIALPORTCFG pCfg)
+{
+ int rc = VINF_SUCCESS;
+ bool f5DataBits = false;
+ speed_t enmSpeedIn = cfgetispeed(pTermios);
+ Assert(enmSpeedIn == cfgetospeed(pTermios)); /* Should always be the same. */
+
+ if (!pThis->fBaudrateCust)
+ {
+ pCfg->uBaudRate = rtSerialPortGetBaudrateFromTermiosSpeed(enmSpeedIn);
+ if (!pCfg->uBaudRate)
+ rc = VERR_SERIALPORT_INVALID_BAUDRATE;
+ }
+ else
+ pCfg->uBaudRate = pThis->uBaudRateCust;
+
+ switch (pTermios->c_cflag & CSIZE)
+ {
+ case CS5:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
+ f5DataBits = true;
+ break;
+ case CS6:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
+ break;
+ case CS7:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
+ break;
+ case CS8:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
+ break;
+ default:
+ AssertFailed(); /* Should not happen. */
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_INVALID;
+ rc = RT_FAILURE(rc) ? rc : VERR_INVALID_PARAMETER;
+ }
+
+ /* Convert parity. */
+ if (pTermios->c_cflag & PARENB)
+ {
+ /*
+ * CMSPAR is not supported on all systems, especially OS X. As configuring
+ * mark/space parity there is not supported and we start from a known config
+ * when opening the serial port it is not required to check for this here.
+ */
+#if CMSPAR == 0
+ bool fCmsParSet = RT_BOOL(pTermios->c_cflag & CMSPAR);
+#else
+ bool fCmsParSet = false;
+#endif
+ if (pTermios->c_cflag & PARODD)
+ pCfg->enmParity = fCmsParSet ? RTSERIALPORTPARITY_MARK : RTSERIALPORTPARITY_ODD;
+ else
+ pCfg->enmParity = fCmsParSet ? RTSERIALPORTPARITY_SPACE: RTSERIALPORTPARITY_EVEN;
+ }
+ else
+ pCfg->enmParity = RTSERIALPORTPARITY_NONE;
+
+ /*
+ * 1.5 stop bits are used with a data count of 5 bits when a UART derived from the 8250
+ * is used.
+ */
+ if (pTermios->c_cflag & CSTOPB)
+ pCfg->enmStopBitCount = f5DataBits ? RTSERIALPORTSTOPBITS_ONEPOINTFIVE : RTSERIALPORTSTOPBITS_TWO;
+ else
+ pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
+
+ return rc;
+}
+
+
+/**
+ * Wakes up any thread polling for a serial port event with the given reason.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param bWakeupReason The wakeup reason to pass to the event poller.
+ */
+DECLINLINE(int) rtSerialPortWakeupEvtPoller(PRTSERIALPORTINTERNAL pThis, uint8_t bWakeupReason)
+{
+ int rcPsx = write(pThis->iFdPipeW, &bWakeupReason, 1);
+ if (rcPsx != 1)
+ return RTErrConvertFromErrno(errno);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * The status line monitor thread worker.
+ *
+ * @returns IPRT status code.
+ * @param ThreadSelf Thread handle to this thread.
+ * @param pvUser User argument.
+ */
+static DECLCALLBACK(int) rtSerialPortStsLineMonitorThrd(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF(hThreadSelf);
+ PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)pvUser;
+ unsigned long const fStsLinesChk = TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS;
+ int rc = VINF_SUCCESS;
+ uint32_t fStsLinesOld = 0;
+ uint32_t cStsLineGetErrors = 0;
+#ifdef RT_OS_LINUX
+ bool fPoll = false;
+#endif
+
+ RTThreadUserSignal(hThreadSelf);
+
+ int rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLinesOld);
+ if (rcPsx == -1)
+ {
+ ASMAtomicXchgBool(&pThis->fMonThrdShutdown, true);
+ return RTErrConvertFromErrno(errno);
+ }
+
+ while ( !pThis->fMonThrdShutdown
+ && RT_SUCCESS(rc))
+ {
+# ifdef RT_OS_LINUX
+ /*
+ * Wait for status line change.
+ *
+ * XXX In Linux, if a thread calls tcsetattr while the monitor thread is
+ * waiting in ioctl for a modem status change then 8250.c wrongly disables
+ * modem irqs and so the monitor thread never gets released. The workaround
+ * is to send a signal after each tcsetattr.
+ *
+ * TIOCMIWAIT doesn't work for the DSR line with TIOCM_DSR set
+ * (see http://lxr.linux.no/#linux+v4.7/drivers/usb/class/cdc-acm.c#L949)
+ * However as it is possible to query the line state we will not just clear
+ * the TIOCM_DSR bit from the lines to check but resort to the polling
+ * approach just like on other hosts.
+ */
+ if (!fPoll)
+ {
+ rcPsx = ioctl(pThis->iFd, TIOCMIWAIT, fStsLinesChk);
+ if (!rcPsx)
+ {
+ rc = rtSerialPortWakeupEvtPoller(pThis, RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_CHANGED);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else if (rcPsx == -1 && errno != EINTR)
+ fPoll = true;
+ }
+ else
+#endif
+ {
+ uint32_t fStsLines = 0;
+ rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLines);
+ if (!rcPsx)
+ {
+ cStsLineGetErrors = 0; /* Reset the error counter once we had one successful query. */
+
+ if (((fStsLines ^ fStsLinesOld) & fStsLinesChk))
+ {
+ rc = rtSerialPortWakeupEvtPoller(pThis, RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_CHANGED);
+ if (RT_FAILURE(rc))
+ break;
+
+ fStsLinesOld = fStsLines;
+ }
+ else /* No change, sleep for a bit. */
+ RTThreadSleep(100 /*ms*/);
+ }
+ else if (rcPsx == -1 && errno != EINTR)
+ {
+ /*
+ * If querying the status line fails too often we have to shut down the
+ * thread and notify the user of the serial port.
+ */
+ if (cStsLineGetErrors++ >= 10)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ rtSerialPortWakeupEvtPoller(pThis, RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_MONITOR_FAILED);
+ break;
+ }
+
+ RTThreadSleep(100 /*ms*/);
+ }
+ }
+ }
+
+ ASMAtomicXchgBool(&pThis->fMonThrdShutdown, true);
+ return rc;
+}
+
+
+/**
+ * Creates the status line monitoring thread.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ */
+static int rtSerialPortMonitorThreadCreate(PRTSERIALPORTINTERNAL pThis)
+{
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Check whether querying the status lines is supported at all, pseudo terminals
+ * don't support it so an error returned in that case.
+ */
+ uint32_t fStsLines = 0;
+ int rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLines);
+ if (!rcPsx)
+ {
+ pThis->fMonThrdShutdown = false;
+ rc = RTThreadCreate(&pThis->hMonThrd, rtSerialPortStsLineMonitorThrd, pThis, 0 /*cbStack*/,
+ RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "IPRT-SerPortMon");
+ if (RT_SUCCESS(rc))
+ {
+ /* Wait for the thread to start up. */
+ rc = RTThreadUserWait(pThis->hMonThrd, 20*RT_MS_1SEC);
+ if ( rc == VERR_TIMEOUT
+ || pThis->fMonThrdShutdown)
+ {
+ /* Startup failed, try to reap the thread. */
+ int rcThrd;
+ rc = RTThreadWait(pThis->hMonThrd, 20*RT_MS_1SEC, &rcThrd);
+ if (RT_SUCCESS(rc))
+ rc = rcThrd;
+ else
+ rc = VERR_INTERNAL_ERROR;
+ /* The thread is lost otherwise. */
+ }
+ }
+ }
+ else if (errno == ENOTTY || errno == EINVAL)
+ rc = VERR_NOT_SUPPORTED;
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ return rc;
+}
+
+
+/**
+ * Shuts down the status line monitor thread.
+ *
+ * @param pThis The internal serial port instance data.
+ */
+static void rtSerialPortMonitorThreadShutdown(PRTSERIALPORTINTERNAL pThis)
+{
+ bool fShutDown = ASMAtomicXchgBool(&pThis->fMonThrdShutdown, true);
+ if (!fShutDown)
+ {
+ int rc = RTThreadPoke(pThis->hMonThrd);
+ AssertRC(rc);
+ }
+
+ int rcThrd = VINF_SUCCESS;
+ int rc = RTThreadWait(pThis->hMonThrd, 20*RT_MS_1SEC, &rcThrd);
+ AssertRC(rc);
+ AssertRC(rcThrd);
+}
+
+
+/**
+ * The slow path of rtSerialPortSwitchBlockingMode that does the actual switching.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param fBlocking The desired mode of operation.
+ * @remarks Do not call directly.
+ */
+static int rtSerialPortSwitchBlockingModeSlow(PRTSERIALPORTINTERNAL pThis, bool fBlocking)
+{
+ int fFlags = fcntl(pThis->iFd, F_GETFL, 0);
+ if (fFlags == -1)
+ return RTErrConvertFromErrno(errno);
+
+ if (fBlocking)
+ fFlags &= ~O_NONBLOCK;
+ else
+ fFlags |= O_NONBLOCK;
+ if (fcntl(pThis->iFd, F_SETFL, fFlags) == -1)
+ return RTErrConvertFromErrno(errno);
+
+ pThis->fBlocking = fBlocking;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Switches the serial port to the desired blocking mode if necessary.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param fBlocking The desired mode of operation.
+ */
+DECLINLINE(int) rtSerialPortSwitchBlockingMode(PRTSERIALPORTINTERNAL pThis, bool fBlocking)
+{
+ if (pThis->fBlocking != fBlocking)
+ return rtSerialPortSwitchBlockingModeSlow(pThis, fBlocking);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags)
+{
+ AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszPortAddress, VERR_INVALID_POINTER);
+ AssertReturn(*pszPortAddress != '\0', VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE),
+ VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ int fPsxFlags = O_NOCTTY | O_NONBLOCK;
+
+ if ((fFlags & RTSERIALPORT_OPEN_F_READ) && !(fFlags & RTSERIALPORT_OPEN_F_WRITE))
+ fPsxFlags |= O_RDONLY;
+ else if (!(fFlags & RTSERIALPORT_OPEN_F_READ) && (fFlags & RTSERIALPORT_OPEN_F_WRITE))
+ fPsxFlags |= O_WRONLY;
+ else
+ fPsxFlags |= O_RDWR;
+
+ pThis->u32Magic = RTSERIALPORT_MAGIC;
+ pThis->fOpenFlags = fFlags;
+ pThis->fEvtsPending = 0;
+ pThis->iFd = open(pszPortAddress, fPsxFlags);
+ pThis->fBlocking = false;
+ if (pThis->iFd != -1)
+ {
+ /* Create wakeup pipe for the event API. */
+ int aPipeFds[2];
+ int rcPsx = pipe(&aPipeFds[0]);
+ if (!rcPsx)
+ {
+ /* Make the pipes close on exec. */
+ pThis->iFdPipeR = aPipeFds[0];
+ pThis->iFdPipeW = aPipeFds[1];
+
+ if (fcntl(pThis->iFdPipeR, F_SETFD, FD_CLOEXEC))
+ rc = RTErrConvertFromErrno(errno);
+
+ if ( RT_SUCCESS(rc)
+ && fcntl(pThis->iFdPipeW, F_SETFD, FD_CLOEXEC))
+ rc = RTErrConvertFromErrno(errno);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtSerialPortSetDefaultCfg(pThis);
+ if ( RT_SUCCESS(rc)
+ && (fFlags & RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING))
+ rc = rtSerialPortMonitorThreadCreate(pThis);
+
+ if (RT_SUCCESS(rc))
+ {
+ *phSerialPort = pThis;
+ return VINF_SUCCESS;
+ }
+ }
+
+ close(pThis->iFdPipeR);
+ close(pThis->iFdPipeW);
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ close(pThis->iFd);
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ if (pThis == NIL_RTSERIALPORT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
+
+ if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING)
+ rtSerialPortMonitorThreadShutdown(pThis);
+
+ close(pThis->iFd);
+ close(pThis->iFdPipeR);
+ close(pThis->iFdPipeW);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1);
+
+ return pThis->iFd;
+}
+
+
+RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
+
+ int rc = rtSerialPortSwitchBlockingMode(pThis, true);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Attempt read.
+ */
+ ssize_t cbRead = read(pThis->iFd, pvBuf, cbToRead);
+ if (cbRead > 0)
+ {
+ if (pcbRead)
+ /* caller can handle partial read. */
+ *pcbRead = cbRead;
+ else
+ {
+ /* Caller expects all to be read. */
+ while ((ssize_t)cbToRead > cbRead)
+ {
+ ssize_t cbReadPart = read(pThis->iFd, (uint8_t *)pvBuf + cbRead, cbToRead - cbRead);
+ if (cbReadPart < 0)
+ return RTErrConvertFromErrno(errno);
+ else if (cbReadPart == 0)
+ return VERR_DEV_IO_ERROR;
+
+ cbRead += cbReadPart;
+ }
+ }
+ }
+ else if (cbRead == 0)
+ rc = VERR_DEV_IO_ERROR;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
+
+ *pcbRead = 0;
+
+ int rc = rtSerialPortSwitchBlockingMode(pThis, false);
+ if (RT_SUCCESS(rc))
+ {
+ ssize_t cbThisRead = read(pThis->iFd, pvBuf, cbToRead);
+ if (cbThisRead > 0)
+ {
+ /*
+ * The read data needs to be scanned for the BREAK condition marker encoded in the data stream,
+ * if break detection was enabled during open.
+ */
+ if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_DETECT_BREAK_CONDITION)
+ { /** @todo */ }
+
+ *pcbRead = cbThisRead;
+ }
+ else if (cbThisRead == 0)
+ rc = VERR_DEV_IO_ERROR;
+ else if ( errno == EAGAIN
+# ifdef EWOULDBLOCK
+# if EWOULDBLOCK != EAGAIN
+ || errno == EWOULDBLOCK
+# endif
+# endif
+ )
+ rc = VINF_TRY_AGAIN;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
+
+ int rc = rtSerialPortSwitchBlockingMode(pThis, true);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Attempt write.
+ */
+ ssize_t cbWritten = write(pThis->iFd, pvBuf, cbToWrite);
+ if (cbWritten > 0)
+ {
+ if (pcbWritten)
+ /* caller can handle partial write. */
+ *pcbWritten = cbWritten;
+ else
+ {
+ /* Caller expects all to be written. */
+ while ((ssize_t)cbToWrite > cbWritten)
+ {
+ ssize_t cbWrittenPart = write(pThis->iFd, (const uint8_t *)pvBuf + cbWritten, cbToWrite - cbWritten);
+ if (cbWrittenPart < 0)
+ return RTErrConvertFromErrno(errno);
+ else if (cbWrittenPart == 0)
+ return VERR_DEV_IO_ERROR;
+ cbWritten += cbWrittenPart;
+ }
+ }
+ }
+ else if (cbWritten == 0)
+ rc = VERR_DEV_IO_ERROR;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
+
+ *pcbWritten = 0;
+
+ int rc = rtSerialPortSwitchBlockingMode(pThis, false);
+ if (RT_SUCCESS(rc))
+ {
+ ssize_t cbThisWrite = write(pThis->iFd, pvBuf, cbToWrite);
+ if (cbThisWrite > 0)
+ *pcbWritten = cbThisWrite;
+ else if (cbThisWrite == 0)
+ rc = VERR_DEV_IO_ERROR;
+ else if ( errno == EAGAIN
+# ifdef EWOULDBLOCK
+# if EWOULDBLOCK != EAGAIN
+ || errno == EWOULDBLOCK
+# endif
+# endif
+ )
+ rc = VINF_TRY_AGAIN;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ return rtSerialPortTermios2Cfg(pThis, &pThis->PortCfg, pCfg);
+}
+
+
+RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ struct termios PortCfgNew; RT_ZERO(PortCfgNew);
+ bool fBaudrateCust = false;
+ int rc = rtSerialPortCfg2Termios(pThis, pCfg, &PortCfgNew, &fBaudrateCust, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ int rcPsx = tcflush(pThis->iFd, TCIOFLUSH);
+ if (!rcPsx)
+ {
+#ifdef RT_OS_LINUX
+ if (fBaudrateCust)
+ {
+ struct serial_struct SerLnx;
+ rcPsx = ioctl(pThis->iFd, TIOCGSERIAL, &SerLnx);
+ if (!rcPsx)
+ {
+ SerLnx.custom_divisor = SerLnx.baud_base / pCfg->uBaudRate;
+ if (!SerLnx.custom_divisor)
+ SerLnx.custom_divisor = 1;
+ SerLnx.flags &= ~ASYNC_SPD_MASK;
+ SerLnx.flags |= ASYNC_SPD_CUST;
+ rcPsx = ioctl(pThis->iFd, TIOCSSERIAL, &SerLnx);
+ }
+ }
+#else /* !RT_OS_LINUX */
+ /* Hosts not supporting custom baud rates should already fail in rtSerialPortCfg2Termios(). */
+ AssertMsgFailed(("Should not get here!\n"));
+#endif /* !RT_OS_LINUX */
+ pThis->fBaudrateCust = fBaudrateCust;
+ pThis->uBaudRateCust = pCfg->uBaudRate;
+
+ if (!rcPsx)
+ rcPsx = tcsetattr(pThis->iFd, TCSANOW, &PortCfgNew);
+ if (rcPsx == -1)
+ rc = RTErrConvertFromErrno(errno);
+ else
+ memcpy(&pThis->PortCfg, &PortCfgNew, sizeof(struct termios));
+
+#ifdef RT_OS_LINUX
+ /*
+ * XXX In Linux, if a thread calls tcsetattr while the monitor thread is
+ * waiting in ioctl for a modem status change then 8250.c wrongly disables
+ * modem irqs and so the monitor thread never gets released. The workaround
+ * is to send a signal after each tcsetattr.
+ */
+ if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING)
+ RTThreadPoke(pThis->hMonThrd);
+#endif
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv,
+ RTMSINTERVAL msTimeout)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
+
+ *pfEvtsRecv = 0;
+
+ fEvtMask |= RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED; /* This will be reported always, no matter what the caller wants. */
+
+ /* Return early if there are events pending from previous calls which weren't fetched yet. */
+ for (;;)
+ {
+ uint32_t fEvtsPending = ASMAtomicReadU32(&pThis->fEvtsPending);
+ if (fEvtsPending & fEvtMask)
+ {
+ *pfEvtsRecv = fEvtsPending & fEvtMask;
+ /* Write back, repeat the whole procedure if someone else raced us. */
+ if (ASMAtomicCmpXchgU32(&pThis->fEvtsPending, fEvtsPending & ~fEvtMask, fEvtsPending))
+ return VINF_SUCCESS;
+ }
+ else
+ break;
+ }
+
+ int rc = rtSerialPortSwitchBlockingMode(pThis, false);
+ if (RT_SUCCESS(rc))
+ {
+ struct pollfd aPollFds[2]; RT_ZERO(aPollFds);
+ aPollFds[0].fd = pThis->iFd;
+ aPollFds[0].events = POLLERR | POLLHUP;
+ aPollFds[0].revents = 0;
+ if ( (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_READ)
+ && (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX))
+ aPollFds[0].events |= POLLIN;
+ if ( (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_WRITE)
+ && (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX))
+ aPollFds[0].events |= POLLOUT;
+
+ aPollFds[1].fd = pThis->iFdPipeR;
+ aPollFds[1].events = POLLIN | POLLERR | POLLHUP;
+ aPollFds[1].revents = 0;
+
+ int rcPsx = 0;
+ int msTimeoutLeft = msTimeout == RT_INDEFINITE_WAIT ? -1 : (int)msTimeout;
+ while (msTimeoutLeft != 0)
+ {
+ uint64_t tsPollStart = RTTimeMilliTS();
+
+ rcPsx = poll(&aPollFds[0], RT_ELEMENTS(aPollFds), msTimeoutLeft);
+ if (rcPsx != -1 || errno != EINTR)
+ break;
+ /* Restart when getting interrupted. */
+ if (msTimeoutLeft > -1)
+ {
+ uint64_t tsPollEnd = RTTimeMilliTS();
+ uint64_t tsPollSpan = tsPollEnd - tsPollStart;
+ msTimeoutLeft -= RT_MIN(tsPollSpan, (uint32_t)msTimeoutLeft);
+ }
+ }
+
+ uint32_t fEvtsPending = 0;
+ if (rcPsx < 0 && errno != EINTR)
+ rc = RTErrConvertFromErrno(errno);
+ else if (rcPsx > 0)
+ {
+ if (aPollFds[0].revents != 0)
+ {
+ if (aPollFds[0].revents & POLLERR)
+ rc = VERR_DEV_IO_ERROR;
+ else
+ {
+ fEvtsPending |= (aPollFds[0].revents & POLLIN) ? RTSERIALPORT_EVT_F_DATA_RX : 0;
+ fEvtsPending |= (aPollFds[0].revents & POLLOUT) ? RTSERIALPORT_EVT_F_DATA_TX : 0;
+ /** @todo BREAK condition detection. */
+ }
+ }
+
+ if (aPollFds[1].revents != 0)
+ {
+ AssertReturn(!(aPollFds[1].revents & (POLLHUP | POLLERR | POLLNVAL)), VERR_INTERNAL_ERROR);
+ Assert(aPollFds[1].revents & POLLIN);
+
+ uint8_t bWakeupReason = 0;
+ ssize_t cbRead = read(pThis->iFdPipeR, &bWakeupReason, 1);
+ if (cbRead == 1)
+ {
+ switch (bWakeupReason)
+ {
+ case RTSERIALPORT_WAKEUP_PIPE_REASON_INTERRUPT:
+ rc = VERR_INTERRUPTED;
+ break;
+ case RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_CHANGED:
+ fEvtsPending |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
+ break;
+ case RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_MONITOR_FAILED:
+ fEvtsPending |= RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED;
+ break;
+ default:
+ AssertFailed();
+ rc = VERR_INTERNAL_ERROR;
+ }
+ }
+ else
+ rc = VERR_INTERNAL_ERROR;
+ }
+ }
+ else
+ rc = VERR_TIMEOUT;
+
+ *pfEvtsRecv = fEvtsPending & fEvtMask;
+ fEvtsPending &= ~fEvtMask;
+ ASMAtomicOrU32(&pThis->fEvtsPending, fEvtsPending);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ return rtSerialPortWakeupEvtPoller(pThis, RTSERIALPORT_WAKEUP_PIPE_REASON_INTERRUPT);
+}
+
+
+RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ int rc = VINF_SUCCESS;
+ int rcPsx = ioctl(pThis->iFd, fSet ? TIOCSBRK : TIOCCBRK);
+ if (rcPsx == -1)
+ rc = RTErrConvertFromErrno(errno);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ int rc = VINF_SUCCESS;
+ int fTiocmSet = 0;
+ int fTiocmClear = 0;
+
+ if (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS)
+ fTiocmClear |= TIOCM_RTS;
+ if (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR)
+ fTiocmClear |= TIOCM_DTR;
+
+ if (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS)
+ fTiocmSet |= TIOCM_RTS;
+ if (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
+ fTiocmSet |= TIOCM_DTR;
+
+ int rcPsx = ioctl(pThis->iFd, TIOCMBIS, &fTiocmSet);
+ if (!rcPsx)
+ {
+ rcPsx = ioctl(pThis->iFd, TIOCMBIC, &fTiocmClear);
+ if (rcPsx == -1)
+ rc = RTErrConvertFromErrno(errno);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
+
+ *pfStsLines = 0;
+
+ int rc = VINF_SUCCESS;
+ int fStsLines = 0;
+ int rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLines);
+ if (!rcPsx)
+ {
+ /* This resets the status line event pending flag. */
+ for (;;)
+ {
+ uint32_t fEvtsPending = ASMAtomicReadU32(&pThis->fEvtsPending);
+ if (ASMAtomicCmpXchgU32(&pThis->fEvtsPending, fEvtsPending & ~RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED, fEvtsPending))
+ break;
+ }
+
+ *pfStsLines |= (fStsLines & TIOCM_CAR) ? RTSERIALPORT_STS_LINE_DCD : 0;
+ *pfStsLines |= (fStsLines & TIOCM_RNG) ? RTSERIALPORT_STS_LINE_RI : 0;
+ *pfStsLines |= (fStsLines & TIOCM_DSR) ? RTSERIALPORT_STS_LINE_DSR : 0;
+ *pfStsLines |= (fStsLines & TIOCM_CTS) ? RTSERIALPORT_STS_LINE_CTS : 0;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/shmem-posix.cpp b/src/VBox/Runtime/r3/posix/shmem-posix.cpp
new file mode 100644
index 00000000..5e21f26b
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/shmem-posix.cpp
@@ -0,0 +1,419 @@
+/* $Id: shmem-posix.cpp $ */
+/** @file
+ * IPRT - Named shared memory object, POSIX Implementation.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/shmem.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/cdefs.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include "internal/magics.h"
+
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+
+/* Workaround on systems which do not provide this. */
+#ifndef NAME_MAX
+# define NAME_MAX 255
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Shared memory object mapping descriptor.
+ */
+typedef struct RTSHMEMMAPPINGDESC
+{
+ /** Number of references held to this mapping, 0 if the descriptor is free. */
+ volatile uint32_t cMappings;
+ /** Pointer to the region mapping. */
+ void *pvMapping;
+ /** Start offset */
+ size_t offRegion;
+ /** Size of the region. */
+ size_t cbRegion;
+ /** Access flags for this region .*/
+ uint32_t fFlags;
+} RTSHMEMMAPPINGDESC;
+/** Pointer to a shared memory object mapping descriptor. */
+typedef RTSHMEMMAPPINGDESC *PRTSHMEMMAPPINGDESC;
+/** Pointer to a constant shared memory object mapping descriptor. */
+typedef const RTSHMEMMAPPINGDESC *PCRTSHMEMMAPPINGDESC;
+
+
+/**
+ * Internal shared memory object state.
+ */
+typedef struct RTSHMEMINT
+{
+ /** Magic value (RTSHMEM_MAGIC). */
+ uint32_t u32Magic;
+ /** File descriptor for the underlying shared memory object. */
+ int iFdShm;
+ /** Pointer to the shared memory object name. */
+ char *pszName;
+ /** Flag whether this instance created the named shared memory object. */
+ bool fCreate;
+ /** Overall number of mappings active for this shared memory object. */
+ volatile uint32_t cMappings;
+ /** Maximum number of mapping descriptors allocated. */
+ uint32_t cMappingDescsMax;
+ /** Number of mapping descriptors used. */
+ volatile uint32_t cMappingDescsUsed;
+ /** Array of mapping descriptors - variable in size. */
+ RTSHMEMMAPPINGDESC aMappingDescs[1];
+} RTSHMEMINT;
+/** Pointer to the internal shared memory object state. */
+typedef RTSHMEMINT *PRTSHMEMINT;
+
+
+
+/**
+ * Returns a mapping descriptor matching the given region properties or NULL if none was found.
+ *
+ * @returns Pointer to the matching mapping descriptor or NULL if not found.
+ * @param pThis Pointer to the shared memory object instance.
+ * @param offRegion Offset into the shared memory object to start mapping at.
+ * @param cbRegion Size of the region to map.
+ * @param fFlags Desired properties of the mapped region, combination of RTSHMEM_MAP_F_* defines.
+ */
+DECLINLINE(PRTSHMEMMAPPINGDESC) rtShMemMappingDescFindByProp(PRTSHMEMINT pThis, size_t offRegion, size_t cbRegion, uint32_t fFlags)
+{
+ for (uint32_t i = 0; i < pThis->cMappingDescsMax; i++)
+ {
+ if ( pThis->aMappingDescs[i].offRegion == offRegion
+ && pThis->aMappingDescs[i].cbRegion == cbRegion
+ && pThis->aMappingDescs[i].fFlags == fFlags)
+ return &pThis->aMappingDescs[i];
+ }
+
+ return NULL;
+}
+
+
+RTDECL(int) RTShMemOpen(PRTSHMEM phShMem, const char *pszName, uint32_t fFlags, size_t cbMax, uint32_t cMappingsHint)
+{
+ AssertPtrReturn(phShMem, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSHMEM_O_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(cMappingsHint < 64, VERR_OUT_OF_RANGE);
+
+ size_t cchName = strlen(pszName);
+ AssertReturn(cchName, VERR_INVALID_PARAMETER);
+ AssertReturn(cchName < NAME_MAX - 1, VERR_INVALID_PARAMETER); /* account for the / we add later on. */
+ cMappingsHint = cMappingsHint == 0 ? 5 : cMappingsHint;
+ int rc = VINF_SUCCESS;
+ PRTSHMEMINT pThis = (PRTSHMEMINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTSHMEMINT, aMappingDescs[cMappingsHint]) + cchName + 2); /* '/' + terminator. */
+ if (RT_LIKELY(pThis))
+ {
+ pThis->u32Magic = RTSHMEM_MAGIC;
+ pThis->pszName = (char *)&pThis->aMappingDescs[cMappingsHint];
+ /*pThis->fCreate = false; */
+ /*pThis->cMappings = 0; */
+ pThis->cMappingDescsMax = cMappingsHint;
+ /*pThis->cMappingDescsUsed = 0; */
+ pThis->pszName[0] = '/';
+ memcpy(&pThis->pszName[1], pszName, cchName);
+ int fShmFlags = 0;
+ if (fFlags & RTSHMEM_O_F_CREATE)
+ {
+ fShmFlags |= O_CREAT;
+ pThis->fCreate = true;
+ }
+ if ((fFlags & RTSHMEM_O_F_CREATE_EXCL) == RTSHMEM_O_F_CREATE_EXCL)
+ fShmFlags |= O_EXCL;
+ if ( (fFlags & RTSHMEM_O_F_READWRITE) == RTSHMEM_O_F_READWRITE
+ || (fFlags & RTSHMEM_O_F_WRITE))
+ fShmFlags |= O_RDWR;
+ else
+ fShmFlags |= O_RDONLY;
+ if (fFlags & RTSHMEM_O_F_TRUNCATE)
+ fShmFlags |= O_TRUNC;
+ pThis->iFdShm = shm_open(pThis->pszName, fShmFlags , 0600);
+ if (pThis->iFdShm > 0)
+ {
+ if (cbMax)
+ rc = RTShMemSetSize(pThis, cbMax);
+ if (RT_SUCCESS(rc))
+ {
+ *phShMem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ close(pThis->iFdShm);
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemClose(RTSHMEM hShMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->cMappings, VERR_INVALID_STATE);
+
+ int rc = VINF_SUCCESS;
+ if (!close(pThis->iFdShm))
+ {
+ if (pThis->fCreate)
+ shm_unlink(pThis->pszName); /* Ignore any error here. */
+ pThis->u32Magic = RTSHMEM_MAGIC_DEAD;
+ RTMemFree(pThis);
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemDelete(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+
+ size_t cchName = strlen(pszName);
+ AssertReturn(cchName, VERR_INVALID_PARAMETER);
+ AssertReturn(cchName < NAME_MAX - 1, VERR_INVALID_PARAMETER); /* account for the / we add later on. */
+ char *psz = NULL;
+
+ int rc = RTStrAllocEx(&psz, cchName + 2); /* '/' + terminator */
+ if (RT_SUCCESS(rc))
+ {
+ psz[0] = '/';
+ memcpy(&psz[1], pszName, cchName + 1);
+ if (shm_unlink(psz))
+ rc = RTErrConvertFromErrno(errno);
+ RTStrFree(psz);
+ }
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTShMemRefCount(RTSHMEM hShMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, 0);
+
+ return pThis->cMappings;
+}
+
+
+RTDECL(int) RTShMemSetSize(RTSHMEM hShMem, size_t cbMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->cMappings, VERR_INVALID_STATE);
+
+ int rc = VINF_SUCCESS;
+ if (ftruncate(pThis->iFdShm, (off_t)cbMem))
+ rc = RTErrConvertFromErrno(errno);
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemQuerySize(RTSHMEM hShMem, size_t *pcbMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pcbMem, VERR_INVALID_PARAMETER);
+
+ struct stat st;
+ if (!fstat(pThis->iFdShm, &st))
+ {
+ *pcbMem = st.st_size;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(int) RTShMemMapRegion(RTSHMEM hShMem, size_t offRegion, size_t cbRegion, uint32_t fFlags, void **ppv)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(ppv, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSHMEM_MAP_F_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /* Try to find a mapping with compatible parameters first. */
+ PRTSHMEMMAPPINGDESC pMappingDesc = NULL;
+ for (uint32_t iTry = 0; iTry < 10; iTry++)
+ {
+ pMappingDesc = rtShMemMappingDescFindByProp(pThis, offRegion, cbRegion, fFlags);
+ if (!pMappingDesc)
+ break;
+
+ /* Increase the mapping count and check that the region is still accessible by us. */
+ if ( ASMAtomicIncU32(&pMappingDesc->cMappings) > 1
+ && pMappingDesc->offRegion == offRegion
+ && pMappingDesc->cbRegion == cbRegion
+ && pMappingDesc->fFlags == fFlags)
+ break;
+ /* Mapping was freed inbetween, next round. */
+ }
+
+ int rc = VINF_SUCCESS;
+ if (!pMappingDesc)
+ {
+ /* Find an empty region descriptor and map the region. */
+ for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++)
+ {
+ if (!pThis->aMappingDescs[i].cMappings)
+ {
+ pMappingDesc = &pThis->aMappingDescs[i];
+
+ /* Try to grab this one. */
+ if (ASMAtomicIncU32(&pMappingDesc->cMappings) == 1)
+ break;
+
+ /* Somebody raced us, drop reference and continue. */
+ ASMAtomicDecU32(&pMappingDesc->cMappings);
+ pMappingDesc = NULL;
+ }
+ }
+
+ if (RT_LIKELY(pMappingDesc))
+ {
+ /* Try to map it. */
+ int fMmapFlags = 0;
+ int fProt = 0;
+ if (fFlags & RTSHMEM_MAP_F_READ)
+ fProt |= PROT_READ;
+ if (fFlags & RTSHMEM_MAP_F_WRITE)
+ fProt |= PROT_WRITE;
+ if (fFlags & RTSHMEM_MAP_F_EXEC)
+ fProt |= PROT_EXEC;
+ if (fFlags & RTSHMEM_MAP_F_COW)
+ fMmapFlags |= MAP_PRIVATE;
+ else
+ fMmapFlags |= MAP_SHARED;
+
+ void *pv = mmap(NULL, cbRegion, fProt, fMmapFlags, pThis->iFdShm, (off_t)offRegion);
+ if (pv != MAP_FAILED)
+ {
+ pMappingDesc->pvMapping = pv;
+ pMappingDesc->offRegion = offRegion;
+ pMappingDesc->cbRegion = cbRegion;
+ pMappingDesc->fFlags = fFlags;
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ ASMAtomicDecU32(&pMappingDesc->cMappings);
+ }
+ }
+ else
+ rc = VERR_SHMEM_MAXIMUM_MAPPINGS_REACHED;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppv = pMappingDesc->pvMapping;
+ ASMAtomicIncU32(&pThis->cMappings);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemUnmapRegion(RTSHMEM hShMem, void *pv)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pv, VERR_INVALID_PARAMETER);
+
+ /* Find the mapping descriptor by the given region address. */
+ PRTSHMEMMAPPINGDESC pMappingDesc = NULL;
+ for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++)
+ {
+ if (pThis->aMappingDescs[i].pvMapping == pv)
+ {
+ pMappingDesc = &pThis->aMappingDescs[i];
+ break;
+ }
+ }
+
+ AssertPtrReturn(pMappingDesc, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ size_t cbRegion = pMappingDesc->cMappings;
+ if (!ASMAtomicDecU32(&pMappingDesc->cMappings))
+ {
+ /* Last mapping of this region was unmapped, so do the real unmapping now. */
+ if (munmap(pv, cbRegion))
+ {
+ ASMAtomicIncU32(&pMappingDesc->cMappings);
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ {
+ ASMAtomicDecU32(&pThis->cMappingDescsUsed);
+ ASMAtomicDecU32(&pThis->cMappings);
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/symlink-posix.cpp b/src/VBox/Runtime/r3/posix/symlink-posix.cpp
new file mode 100644
index 00000000..e43f5b36
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/symlink-posix.cpp
@@ -0,0 +1,247 @@
+/* $Id: symlink-posix.cpp $ */
+/** @file
+ * IPRT - Symbolic Links, POSIX.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYMLINK
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <iprt/symlink.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include "internal/path.h"
+
+
+
+RTDECL(bool) RTSymlinkExists(const char *pszSymlink)
+{
+ bool fRc = false;
+ char const *pszNativeSymlink;
+ int rc = rtPathToNative(&pszNativeSymlink, pszSymlink, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat s;
+ fRc = !lstat(pszNativeSymlink, &s)
+ && S_ISLNK(s.st_mode);
+
+ rtPathFreeNative(pszNativeSymlink, pszSymlink);
+ }
+
+ LogFlow(("RTSymlinkExists(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
+ return fRc;
+}
+
+
+RTDECL(bool) RTSymlinkIsDangling(const char *pszSymlink)
+{
+ bool fRc = false;
+ char const *pszNativeSymlink;
+ int rc = rtPathToNative(&pszNativeSymlink, pszSymlink, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat s;
+ fRc = !lstat(pszNativeSymlink, &s)
+ && S_ISLNK(s.st_mode);
+ if (fRc)
+ {
+ errno = 0;
+ fRc = stat(pszNativeSymlink, &s) != 0
+ && ( errno == ENOENT
+ || errno == ENOTDIR
+ || errno == ELOOP);
+ }
+
+ rtPathFreeNative(pszNativeSymlink, pszSymlink);
+ }
+
+ LogFlow(("RTSymlinkIsDangling(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
+ return fRc;
+}
+
+
+RTDECL(int) RTSymlinkCreate(const char *pszSymlink, const char *pszTarget, RTSYMLINKTYPE enmType, uint32_t fCreate)
+{
+ RT_NOREF_PV(fCreate);
+
+ /*
+ * Validate the input.
+ */
+ AssertReturn(enmType > RTSYMLINKTYPE_INVALID && enmType < RTSYMLINKTYPE_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszSymlink, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszTarget, VERR_INVALID_POINTER);
+
+ /*
+ * Convert the paths.
+ */
+ char const *pszNativeSymlink;
+ int rc = rtPathToNative(&pszNativeSymlink, pszSymlink, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ const char *pszNativeTarget;
+ rc = rtPathToNative(&pszNativeTarget, pszTarget, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the link.
+ */
+ if (symlink(pszNativeTarget, pszNativeSymlink) == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ rtPathFreeNative(pszNativeTarget, pszTarget);
+ }
+ rtPathFreeNative(pszNativeSymlink, pszSymlink);
+ }
+
+ LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns %Rrc\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTSymlinkDelete(const char *pszSymlink, uint32_t fDelete)
+{
+ RT_NOREF_PV(fDelete);
+
+ char const *pszNativeSymlink;
+ int rc = rtPathToNative(&pszNativeSymlink, pszSymlink, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ struct stat s;
+ if (!lstat(pszNativeSymlink, &s))
+ {
+ if (S_ISLNK(s.st_mode))
+ {
+ if (unlink(pszNativeSymlink) == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ rc = VERR_NOT_SYMLINK;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ rtPathFreeNative(pszNativeSymlink, pszSymlink);
+ }
+
+ LogFlow(("RTSymlinkDelete(%p={%s}, #%x): returns %Rrc\n", pszSymlink, pszSymlink, fDelete, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead)
+{
+ RT_NOREF_PV(fRead);
+
+ char *pszMyTarget;
+ int rc = RTSymlinkReadA(pszSymlink, &pszMyTarget);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrCopy(pszTarget, cbTarget, pszMyTarget);
+ RTStrFree(pszMyTarget);
+ }
+ LogFlow(("RTSymlinkRead(%p={%s}): returns %Rrc\n", pszSymlink, pszSymlink, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTSymlinkReadA(const char *pszSymlink, char **ppszTarget)
+{
+ AssertPtr(ppszTarget);
+ char const *pszNativeSymlink;
+ int rc = rtPathToNative(&pszNativeSymlink, pszSymlink, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /* Guess the initial buffer size. */
+ ssize_t cbBuf;
+ struct stat s;
+ if (!lstat(pszNativeSymlink, &s))
+ cbBuf = RT_MAX(RT_ALIGN_Z(s.st_size, 64), 64);
+ else
+ cbBuf = 1024;
+
+ /* Read loop that grows the buffer. */
+ char *pszBuf = NULL;
+ for (;;)
+ {
+ RTMemTmpFree(pszBuf);
+ pszBuf = (char *)RTMemTmpAlloc(cbBuf);
+ if (pszBuf)
+ {
+ ssize_t cbReturned = readlink(pszNativeSymlink, pszBuf, cbBuf);
+ if (cbReturned >= cbBuf)
+ {
+ /* Increase the buffer size and try again */
+ cbBuf *= 2;
+ continue;
+ }
+
+ if (cbReturned > 0)
+ {
+ pszBuf[cbReturned] = '\0';
+ rc = rtPathFromNativeDup(ppszTarget, pszBuf, pszSymlink);
+ }
+ else if (errno == EINVAL)
+ rc = VERR_NOT_SYMLINK;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ break;
+ } /* for loop */
+
+ RTMemTmpFree(pszBuf);
+ rtPathFreeNative(pszNativeSymlink, pszSymlink);
+ }
+
+ if (RT_SUCCESS(rc))
+ LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc *ppszTarget=%p:{%s}\n", pszSymlink, pszSymlink, ppszTarget, rc, *ppszTarget, *ppszTarget));
+ else
+ LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc\n", pszSymlink, pszSymlink, ppszTarget, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/thread-posix.cpp b/src/VBox/Runtime/r3/posix/thread-posix.cpp
new file mode 100644
index 00000000..cd621ce0
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/thread-posix.cpp
@@ -0,0 +1,780 @@
+/* $Id: thread-posix.cpp $ */
+/** @file
+ * IPRT - Threads, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#if defined(RT_OS_LINUX)
+# include <unistd.h>
+# include <sys/syscall.h>
+#endif
+#if defined(RT_OS_SOLARIS)
+# include <sched.h>
+# include <sys/resource.h>
+#endif
+#if defined(RT_OS_DARWIN)
+# include <mach/thread_act.h>
+# include <mach/thread_info.h>
+# include <mach/host_info.h>
+# include <mach/mach_init.h>
+# include <mach/mach_host.h>
+#endif
+#if defined(RT_OS_DARWIN) /*|| defined(RT_OS_FREEBSD) - later */ \
+ || (defined(RT_OS_LINUX) && !defined(IN_RT_STATIC) /* static + dlsym = trouble */) \
+ || defined(IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP)
+# define IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP
+# include <dlfcn.h>
+#endif
+#if defined(RT_OS_HAIKU)
+# include <OS.h>
+#endif
+#if defined(RT_OS_DARWIN)
+# define sigprocmask pthread_sigmask /* On xnu sigprocmask works on the process, not the calling thread as elsewhere. */
+#endif
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/semaphore.h>
+#include <iprt/list.h>
+#include <iprt/once.h>
+#include <iprt/critsect.h>
+#include <iprt/req.h>
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/*#ifndef IN_GUEST - shouldn't need to exclude this now with the non-obtrusive init option. */
+/** Includes RTThreadPoke. */
+# define RTTHREAD_POSIX_WITH_POKE
+/*#endif*/
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The pthread key in which we store the pointer to our own PRTTHREAD structure.
+ * @note There is a defined NIL value here, nor can we really assume this is an
+ * integer. However, zero is a valid key on Linux, so we get into trouble
+ * if we accidentally use it uninitialized.
+ *
+ * So, we ASSUME it's a integer value and the valid range is in approx 0
+ * to PTHREAD_KEYS_MAX. Solaris has at least one negative value (-1)
+ * defined. Thus, we go for 16 MAX values below zero and keep our fingers
+ * cross that it will always be an invalid key value everywhere...
+ *
+ * See also NIL_RTTLS, which is -1.
+ */
+static pthread_key_t g_SelfKey = (pthread_key_t)(-PTHREAD_KEYS_MAX * 16);
+#ifdef RTTHREAD_POSIX_WITH_POKE
+/** The signal we use for poking threads.
+ * This is set to -1 if no available signal was found. */
+static int volatile g_iSigPokeThread = -1;
+#endif
+
+#ifdef IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP
+# if defined(RT_OS_DARWIN)
+/**
+ * The Mac OS X (10.6 and later) variant of pthread_setname_np.
+ *
+ * @returns errno.h
+ * @param pszName The new thread name.
+ */
+typedef int (*PFNPTHREADSETNAME)(const char *pszName);
+# else
+/**
+ * The variant of pthread_setname_np most other unix-like systems implement.
+ *
+ * @returns errno.h
+ * @param hThread The thread.
+ * @param pszName The new thread name.
+ */
+typedef int (*PFNPTHREADSETNAME)(pthread_t hThread, const char *pszName);
+# endif
+
+/** Pointer to pthread_setname_np if found. */
+static PFNPTHREADSETNAME g_pfnThreadSetName = NULL;
+#endif /* IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP */
+
+#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+/** Atomic indicator of whether the priority proxy thread has been (attempted) started.
+ *
+ * The priority proxy thread is started under these circumstances:
+ * - RTThreadCreate
+ * - RTThreadSetType
+ * - RTProcSetPriority
+ *
+ * Which means that we'll be single threaded when this is modified.
+ *
+ * Speical values:
+ * - VERR_TRY_AGAIN: Not yet started.
+ * - VERR_WRONG_ORDER: Starting.
+ * - VINF_SUCCESS: Started successfully.
+ * - VERR_PROCESS_NOT_FOUND: Stopping or stopped
+ * - Other error status if failed to start.
+ *
+ * @note We could potentially optimize this by only start it when we lower the
+ * priority of ourselves, the process, or a newly created thread. But
+ * that would means we would need to take multi-threading into account, so
+ * let's not do that for now.
+ */
+static int32_t volatile g_rcPriorityProxyThreadStart = VERR_TRY_AGAIN;
+/** The IPRT thread handle for the priority proxy. */
+static RTTHREAD g_hRTThreadPosixPriorityProxyThread = NIL_RTTHREAD;
+/** The priority proxy queue. */
+static RTREQQUEUE g_hRTThreadPosixPriorityProxyQueue = NIL_RTREQQUEUE;
+#endif /* RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void *rtThreadNativeMain(void *pvArgs);
+static void rtThreadKeyDestruct(void *pvValue);
+#ifdef RTTHREAD_POSIX_WITH_POKE
+static void rtThreadPosixPokeSignal(int iSignal);
+#endif
+
+
+#ifdef RTTHREAD_POSIX_WITH_POKE
+/**
+ * Try register the dummy signal handler for RTThreadPoke.
+ */
+static void rtThreadPosixSelectPokeSignal(void)
+{
+ /*
+ * Note! Avoid SIGRTMIN thru SIGRTMIN+2 because of LinuxThreads.
+ */
+# if !defined(RT_OS_LINUX) && !defined(RT_OS_SOLARIS) /* glibc defines SIGRTMAX to __libc_current_sigrtmax() and Solaris libc defines it relying on _sysconf(), causing compiler to deploy serialization here. */
+ static
+# endif
+ const int s_aiSigCandidates[] =
+ {
+# ifdef SIGRTMAX
+ SIGRTMAX-3,
+ SIGRTMAX-2,
+ SIGRTMAX-1,
+# endif
+# ifndef RT_OS_SOLARIS
+ SIGUSR2,
+# endif
+ SIGWINCH
+ };
+
+ g_iSigPokeThread = -1;
+ if (!RTR3InitIsUnobtrusive())
+ {
+ for (unsigned iSig = 0; iSig < RT_ELEMENTS(s_aiSigCandidates); iSig++)
+ {
+ struct sigaction SigActOld;
+ if (!sigaction(s_aiSigCandidates[iSig], NULL, &SigActOld))
+ {
+ if ( SigActOld.sa_handler == SIG_DFL
+ || SigActOld.sa_handler == rtThreadPosixPokeSignal)
+ {
+ struct sigaction SigAct;
+ RT_ZERO(SigAct);
+ SigAct.sa_handler = rtThreadPosixPokeSignal;
+ SigAct.sa_flags = 0; /* no SA_RESTART! */
+ sigfillset(&SigAct.sa_mask);
+
+ /* ASSUMES no sigaction race... (lazy bird) */
+ if (!sigaction(s_aiSigCandidates[iSig], &SigAct, NULL))
+ {
+ g_iSigPokeThread = s_aiSigCandidates[iSig];
+ break;
+ }
+ AssertMsgFailed(("rc=%Rrc errno=%d\n", RTErrConvertFromErrno(errno), errno));
+ }
+ }
+ else
+ AssertMsgFailed(("rc=%Rrc errno=%d\n", RTErrConvertFromErrno(errno), errno));
+ }
+ }
+}
+#endif /* RTTHREAD_POSIX_WITH_POKE */
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ /*
+ * Allocate the TLS (key in posix terms) where we store the pointer to
+ * a threads RTTHREADINT structure.
+ */
+ int rc = pthread_key_create(&g_SelfKey, rtThreadKeyDestruct);
+ if (rc)
+ return VERR_NO_TLS_FOR_SELF;
+
+#ifdef RTTHREAD_POSIX_WITH_POKE
+ rtThreadPosixSelectPokeSignal();
+#endif
+
+#ifdef IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP
+ if (RT_SUCCESS(rc))
+ g_pfnThreadSetName = (PFNPTHREADSETNAME)(uintptr_t)dlsym(RTLD_DEFAULT, "pthread_setname_np");
+#endif
+ return rc;
+}
+
+static void rtThreadPosixBlockSignals(PRTTHREADINT pThread)
+{
+ /*
+ * Mask all signals, including the poke one, if requested.
+ */
+ if ( pThread
+ && (pThread->fFlags & RTTHREADFLAGS_NO_SIGNALS))
+ {
+ sigset_t SigSet;
+ sigfillset(&SigSet);
+ sigdelset(&SigSet, SIGILL); /* On the m1 we end up spinning on UDF ... */
+ sigdelset(&SigSet, SIGTRAP); /* ... and BRK instruction if these signals are masked. */
+ sigdelset(&SigSet, SIGFPE); /* Just adding the rest here to be on the safe side. */
+ sigdelset(&SigSet, SIGBUS);
+ sigdelset(&SigSet, SIGSEGV);
+ int rc = sigprocmask(SIG_BLOCK, &SigSet, NULL);
+ AssertMsg(rc == 0, ("rc=%Rrc errno=%d\n", RTErrConvertFromErrno(errno), errno)); RT_NOREF(rc);
+ }
+ /*
+ * Block SIGALRM - required for timer-posix.cpp.
+ * This is done to limit harm done by OSes which doesn't do special SIGALRM scheduling.
+ * It will not help much if someone creates threads directly using pthread_create. :/
+ */
+ else if (!RTR3InitIsUnobtrusive())
+ {
+ sigset_t SigSet;
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, SIGALRM);
+ sigprocmask(SIG_BLOCK, &SigSet, NULL);
+ }
+
+#ifdef RTTHREAD_POSIX_WITH_POKE
+ /*
+ * bird 2020-10-28: Not entirely sure why we do this, but it makes sure the signal works
+ * on the new thread. Probably some pre-NPTL linux reasons.
+ */
+ if (g_iSigPokeThread != -1)
+ {
+# if 1 /* siginterrupt() is typically implemented as two sigaction calls, this should be faster and w/o deprecations: */
+ struct sigaction SigActOld;
+ RT_ZERO(SigActOld);
+
+ struct sigaction SigAct;
+ RT_ZERO(SigAct);
+ SigAct.sa_handler = rtThreadPosixPokeSignal;
+ SigAct.sa_flags = 0; /* no SA_RESTART! */
+ sigfillset(&SigAct.sa_mask);
+
+ int rc = sigaction(g_iSigPokeThread, &SigAct, &SigActOld);
+ AssertMsg(rc == 0, ("rc=%Rrc errno=%d\n", RTErrConvertFromErrno(errno), errno)); RT_NOREF(rc);
+ AssertMsg(rc || SigActOld.sa_handler == rtThreadPosixPokeSignal, ("%p\n", SigActOld.sa_handler));
+# else
+ siginterrupt(g_iSigPokeThread, 1);
+# endif
+ }
+#endif
+}
+
+DECLHIDDEN(void) rtThreadNativeReInitObtrusive(void)
+{
+#ifdef RTTHREAD_POSIX_WITH_POKE
+ Assert(!RTR3InitIsUnobtrusive());
+ rtThreadPosixSelectPokeSignal();
+#endif
+ rtThreadPosixBlockSignals(NULL);
+}
+
+
+/**
+ * Destructor called when a thread terminates.
+ * @param pvValue The key value. PRTTHREAD in our case.
+ */
+static void rtThreadKeyDestruct(void *pvValue)
+{
+ /*
+ * Deal with alien threads.
+ */
+ PRTTHREADINT pThread = (PRTTHREADINT)pvValue;
+ if (pThread->fIntFlags & RTTHREADINT_FLAGS_ALIEN)
+ {
+ pthread_setspecific(g_SelfKey, pThread);
+ rtThreadTerminate(pThread, 0);
+ pthread_setspecific(g_SelfKey, NULL);
+ }
+}
+
+
+#ifdef RTTHREAD_POSIX_WITH_POKE
+/**
+ * Dummy signal handler for the poke signal.
+ *
+ * @param iSignal The signal number.
+ */
+static void rtThreadPosixPokeSignal(int iSignal)
+{
+ Assert(iSignal == g_iSigPokeThread);
+ NOREF(iSignal);
+}
+#endif
+
+
+/**
+ * Adopts a thread, this is called immediately after allocating the
+ * thread structure.
+ *
+ * @param pThread Pointer to the thread structure.
+ */
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ rtThreadPosixBlockSignals(pThread);
+
+ int rc = pthread_setspecific(g_SelfKey, pThread);
+ if (!rc)
+ return VINF_SUCCESS;
+ return VERR_FAILED_TO_SET_SELF_TLS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ if (pThread == (PRTTHREADINT)pthread_getspecific(g_SelfKey))
+ pthread_setspecific(g_SelfKey, NULL);
+}
+
+
+/**
+ * Wrapper which unpacks the params and calls thread function.
+ */
+static void *rtThreadNativeMain(void *pvArgs)
+{
+ PRTTHREADINT pThread = (PRTTHREADINT)pvArgs;
+ pthread_t Self = pthread_self();
+#if !defined(RT_OS_SOLARIS) /* On Solaris sizeof(pthread_t) = 4 and sizeof(NIL_RTNATIVETHREAD) = 8 */
+ Assert((uintptr_t)Self != NIL_RTNATIVETHREAD);
+#endif
+ Assert(Self == (pthread_t)(RTNATIVETHREAD)Self);
+
+#if defined(RT_OS_LINUX)
+ /*
+ * Set the TID.
+ */
+ pThread->tid = syscall(__NR_gettid);
+ ASMMemoryFence();
+#endif
+
+ rtThreadPosixBlockSignals(pThread);
+
+ /*
+ * Set the TLS entry and, if possible, the thread name.
+ */
+ int rc = pthread_setspecific(g_SelfKey, pThread);
+ AssertReleaseMsg(!rc, ("failed to set self TLS. rc=%d thread '%s'\n", rc, pThread->szName));
+
+#ifdef IPRT_MAY_HAVE_PTHREAD_SET_NAME_NP
+ if (g_pfnThreadSetName)
+# ifdef RT_OS_DARWIN
+ g_pfnThreadSetName(pThread->szName);
+# else
+ g_pfnThreadSetName(Self, pThread->szName);
+# endif
+#endif
+
+ /*
+ * Call common main.
+ */
+ rc = rtThreadMain(pThread, (uintptr_t)Self, &pThread->szName[0]);
+
+ pthread_setspecific(g_SelfKey, NULL);
+ pthread_exit((void *)(intptr_t)rc);
+ return (void *)(intptr_t)rc;
+}
+
+#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+
+/**
+ * @callback_method_impl{FNRTTHREAD,
+ * Priority proxy thread that services g_hRTThreadPosixPriorityProxyQueue.}
+ */
+static DECLCALLBACK(int) rtThreadPosixPriorityProxyThread(PRTTHREADINT, void *)
+{
+ for (;;)
+ {
+ RTREQQUEUE hReqQueue = g_hRTThreadPosixPriorityProxyQueue;
+ if (hReqQueue != NIL_RTREQQUEUE)
+ RTReqQueueProcess(hReqQueue, RT_INDEFINITE_WAIT);
+ else
+ break;
+
+ int32_t rc = ASMAtomicUoReadS32(&g_rcPriorityProxyThreadStart);
+ if (rc != VINF_SUCCESS && rc != VERR_WRONG_ORDER)
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Just returns a non-success status codes to force the thread to re-evaluate
+ * the global shutdown variable.
+ */
+static DECLCALLBACK(int) rtThreadPosixPriorityProxyStopper(void)
+{
+ return VERR_CANCELLED;
+}
+
+
+/**
+ * An atexit() callback that stops the proxy creation/priority thread.
+ */
+static void rtThreadStopProxyThread(void)
+{
+ /*
+ * Signal to the thread that it's time to shut down.
+ */
+ int32_t rc = ASMAtomicXchgS32(&g_rcPriorityProxyThreadStart, VERR_PROCESS_NOT_FOUND);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Grab the associated handles.
+ */
+ RTTHREAD hThread = g_hRTThreadPosixPriorityProxyThread;
+ RTREQQUEUE hQueue = g_hRTThreadPosixPriorityProxyQueue;
+ g_hRTThreadPosixPriorityProxyQueue = NIL_RTREQQUEUE;
+ g_hRTThreadPosixPriorityProxyThread = NIL_RTTHREAD;
+ ASMCompilerBarrier(); /* paranoia */
+
+ AssertReturnVoid(hThread != NIL_RTTHREAD);
+ AssertReturnVoid(hQueue != NIL_RTREQQUEUE);
+
+ /*
+ * Kick the thread so it gets out of any pending RTReqQueueProcess call ASAP.
+ */
+ rc = RTReqQueueCallEx(hQueue, NULL, 0 /*cMillies*/, RTREQFLAGS_IPRT_STATUS | RTREQFLAGS_NO_WAIT,
+ (PFNRT)rtThreadPosixPriorityProxyStopper, 0);
+
+ /*
+ * Wait for the thread to complete.
+ */
+ rc = RTThreadWait(hThread, RT_SUCCESS(rc) ? RT_MS_1SEC * 5 : 32, NULL);
+ if (RT_SUCCESS(rc))
+ RTReqQueueDestroy(hQueue);
+ /* else: just leak the stuff, we're exitting, so nobody cares... */
+ }
+}
+
+
+/**
+ * Ensure that the proxy priority proxy thread has been started.
+ *
+ * Since we will always start a proxy thread when asked to create a thread,
+ * there is no need for serialization here.
+ *
+ * @retval true if started
+ * @retval false if it failed to start (caller must handle this scenario).
+ */
+DECLHIDDEN(bool) rtThreadPosixPriorityProxyStart(void)
+{
+ /*
+ * Read the result.
+ */
+ int rc = ASMAtomicUoReadS32(&g_rcPriorityProxyThreadStart);
+ if (rc != VERR_TRY_AGAIN)
+ return RT_SUCCESS(rc);
+
+ /* If this triggers then there is a very unexpected race somewhere. It
+ should be harmless though. */
+ AssertReturn(ASMAtomicCmpXchgS32(&g_rcPriorityProxyThreadStart, VERR_WRONG_ORDER, VERR_TRY_AGAIN), false);
+
+ /*
+ * Not yet started, so do that.
+ */
+ rc = RTReqQueueCreate(&g_hRTThreadPosixPriorityProxyQueue);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&g_hRTThreadPosixPriorityProxyThread, rtThreadPosixPriorityProxyThread, NULL, 0 /*cbStack*/,
+ RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "RTThrdPP");
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicWriteS32(&g_rcPriorityProxyThreadStart, VINF_SUCCESS);
+
+ atexit(rtThreadStopProxyThread);
+ return true;
+ }
+ RTReqQueueCreate(&g_hRTThreadPosixPriorityProxyQueue);
+ }
+ ASMAtomicWriteS32(&g_rcPriorityProxyThreadStart, rc != VERR_WRONG_ORDER ? rc : VERR_PROCESS_NOT_FOUND);
+ return false;
+}
+
+
+/**
+ * Calls @a pfnFunction from the priority proxy thread.
+ *
+ * Caller must have called rtThreadPosixStartProxy() to check that the priority
+ * proxy thread is running.
+ *
+ * @returns
+ * @param pTargetThread The target thread, NULL if not applicable. This is
+ * so we can skip calls pertaining to the priority
+ * proxy thread itself.
+ * @param pfnFunction The function to call. Must return IPRT status code.
+ * @param cArgs Number of arguments (see also RTReqQueueCall).
+ * @param ... Arguments (see also RTReqQueueCall).
+ */
+DECLHIDDEN(int) rtThreadPosixPriorityProxyCall(PRTTHREADINT pTargetThread, PFNRT pfnFunction, int cArgs, ...)
+{
+ int rc;
+ if ( !pTargetThread
+ || pTargetThread->pfnThread != rtThreadPosixPriorityProxyThread)
+ {
+ va_list va;
+ va_start(va, cArgs);
+ PRTREQ pReq;
+ rc = RTReqQueueCallV(g_hRTThreadPosixPriorityProxyQueue, &pReq, RT_INDEFINITE_WAIT, RTREQFLAGS_IPRT_STATUS,
+ pfnFunction, cArgs, va);
+ va_end(va);
+ RTReqRelease(pReq);
+ }
+ else
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
+#endif /* !RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY */
+
+/**
+ * Worker for rtThreadNativeCreate that's either called on the priority proxy
+ * thread or directly on the calling thread depending on the proxy state.
+ */
+static DECLCALLBACK(int) rtThreadNativeInternalCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread)
+{
+ /*
+ * Set the default stack size.
+ */
+ if (!pThread->cbStack)
+ pThread->cbStack = 512*1024;
+
+#ifdef RT_OS_LINUX
+ pThread->tid = -1;
+#endif
+
+ /*
+ * Setup thread attributes.
+ */
+ pthread_attr_t ThreadAttr;
+ int rc = pthread_attr_init(&ThreadAttr);
+ if (!rc)
+ {
+ rc = pthread_attr_setdetachstate(&ThreadAttr, PTHREAD_CREATE_DETACHED);
+ if (!rc)
+ {
+ rc = pthread_attr_setstacksize(&ThreadAttr, pThread->cbStack);
+ if (!rc)
+ {
+ /*
+ * Create the thread.
+ */
+ pthread_t ThreadId;
+ rc = pthread_create(&ThreadId, &ThreadAttr, rtThreadNativeMain, pThread);
+ if (!rc)
+ {
+ pthread_attr_destroy(&ThreadAttr);
+ *pNativeThread = (uintptr_t)ThreadId;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ pthread_attr_destroy(&ThreadAttr);
+ }
+ return RTErrConvertFromErrno(rc);
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread)
+{
+#ifdef RTTHREAD_POSIX_WITH_CREATE_PRIORITY_PROXY
+ /*
+ * If we have a priority proxy thread, use it. Make sure to ignore the
+ * staring of the proxy thread itself.
+ */
+ if ( pThread->pfnThread != rtThreadPosixPriorityProxyThread
+ && rtThreadPosixPriorityProxyStart())
+ {
+ PRTREQ pReq;
+ int rc = RTReqQueueCall(g_hRTThreadPosixPriorityProxyQueue, &pReq, RT_INDEFINITE_WAIT,
+ (PFNRT)rtThreadNativeInternalCreate, 2, pThread, pNativeThread);
+ RTReqRelease(pReq);
+ return rc;
+ }
+
+ /*
+ * Fall back on creating it directly without regard to priority proxying.
+ */
+#endif
+ return rtThreadNativeInternalCreate(pThread, pNativeThread);
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ /** @todo import alien threads? */
+#if defined(RT_OS_DARWIN)
+ /* On darwin, there seems to be input checking with pthread_getspecific.
+ So, we must prevent using g_SelfKey before rtThreadNativeInit has run,
+ otherwise we might crash or starting working with total garbage pointer
+ values here (see _os_tsd_get_direct in znu/libsyscall/os/tsd.h).
+
+ Now, since the init value is a "negative" one, we just have to check
+ that it's positive or zero before calling the API. */
+ if (RT_LIKELY((intptr_t)g_SelfKey >= 0))
+ return (PRTTHREADINT)pthread_getspecific(g_SelfKey);
+ return NIL_RTTHREAD;
+#else
+ return (PRTTHREADINT)pthread_getspecific(g_SelfKey);
+#endif
+}
+
+
+#ifdef RTTHREAD_POSIX_WITH_POKE
+
+RTDECL(int) RTThreadPoke(RTTHREAD hThread)
+{
+ AssertReturn(hThread != RTThreadSelf(), VERR_INVALID_PARAMETER);
+ PRTTHREADINT pThread = rtThreadGet(hThread);
+ AssertReturn(pThread, VERR_INVALID_HANDLE);
+
+ int rc;
+ if (g_iSigPokeThread != -1)
+ {
+ rc = pthread_kill((pthread_t)(uintptr_t)pThread->Core.Key, g_iSigPokeThread);
+ rc = RTErrConvertFromErrno(rc);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ rtThreadRelease(pThread);
+ return rc;
+}
+
+
+RTDECL(int) RTThreadControlPokeSignal(RTTHREAD hThread, bool fEnable)
+{
+ AssertReturn(hThread == RTThreadSelf() && hThread != NIL_RTTHREAD, VERR_INVALID_PARAMETER);
+ int rc;
+ if (g_iSigPokeThread != -1)
+ {
+ sigset_t SigSet;
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, g_iSigPokeThread);
+
+ int rc2 = sigprocmask(fEnable ? SIG_UNBLOCK : SIG_BLOCK, &SigSet, NULL);
+ if (rc2 == 0)
+ rc = VINF_SUCCESS;
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("rc=%Rrc errno=%d (rc2=%d)\n", rc, errno, rc2));
+ }
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
+
+#endif
+
+/** @todo move this into platform specific files. */
+RTR3DECL(int) RTThreadGetExecutionTimeMilli(uint64_t *pKernelTime, uint64_t *pUserTime)
+{
+#if defined(RT_OS_SOLARIS)
+ struct rusage ts;
+ int rc = getrusage(RUSAGE_LWP, &ts);
+ if (rc)
+ return RTErrConvertFromErrno(rc);
+
+ *pKernelTime = ts.ru_stime.tv_sec * 1000 + ts.ru_stime.tv_usec / 1000;
+ *pUserTime = ts.ru_utime.tv_sec * 1000 + ts.ru_utime.tv_usec / 1000;
+ return VINF_SUCCESS;
+
+#elif defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
+ /* on Linux, getrusage(RUSAGE_THREAD, ...) is available since 2.6.26 */
+ struct timespec ts;
+ int rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+ if (rc)
+ return RTErrConvertFromErrno(rc);
+
+ *pKernelTime = 0;
+ *pUserTime = (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ return VINF_SUCCESS;
+
+#elif defined(RT_OS_DARWIN)
+ thread_basic_info ThreadInfo;
+ mach_msg_type_number_t Count = THREAD_BASIC_INFO_COUNT;
+ kern_return_t krc = thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t)&ThreadInfo, &Count);
+ AssertReturn(krc == KERN_SUCCESS, RTErrConvertFromDarwinKern(krc));
+
+ *pKernelTime = ThreadInfo.system_time.seconds * 1000 + ThreadInfo.system_time.microseconds / 1000;
+ *pUserTime = ThreadInfo.user_time.seconds * 1000 + ThreadInfo.user_time.microseconds / 1000;
+
+ return VINF_SUCCESS;
+#elif defined(RT_OS_HAIKU)
+ thread_info ThreadInfo;
+ status_t status = get_thread_info(find_thread(NULL), &ThreadInfo);
+ AssertReturn(status == B_OK, RTErrConvertFromErrno(status));
+
+ *pKernelTime = ThreadInfo.kernel_time / 1000;
+ *pUserTime = ThreadInfo.user_time / 1000;
+
+ return VINF_SUCCESS;
+#else
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/posix/thread2-posix.cpp b/src/VBox/Runtime/r3/posix/thread2-posix.cpp
new file mode 100644
index 00000000..6eaee22e
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/thread2-posix.cpp
@@ -0,0 +1,133 @@
+/* $Id: thread2-posix.cpp $ */
+/** @file
+ * IPRT - Threads part 2, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sched.h>
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/errcore.h>
+#include "internal/thread.h"
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ return (RTNATIVETHREAD)pthread_self();
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ LogFlow(("RTThreadSleep: cMillies=%d\n", cMillies));
+ if (!cMillies)
+ {
+ if (!sched_yield())
+ {
+ LogFlow(("RTThreadSleep: returning %Rrc (cMillies=%d)\n", VINF_SUCCESS, cMillies));
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ struct timespec ts;
+ struct timespec tsrem = {0,0};
+
+ ts.tv_nsec = (cMillies % 1000) * 1000000;
+ ts.tv_sec = cMillies / 1000;
+ if (!nanosleep(&ts, &tsrem))
+ {
+ LogFlow(("RTThreadSleep: returning %Rrc (cMillies=%d)\n", VINF_SUCCESS, cMillies));
+ return VINF_SUCCESS;
+ }
+ }
+
+ int rc = RTErrConvertFromErrno(errno);
+ LogFlow(("RTThreadSleep: returning %Rrc (cMillies=%d)\n", rc, cMillies));
+ return rc;
+}
+
+
+RTDECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies)
+{
+ if (!cMillies)
+ {
+ if (!sched_yield())
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ struct timespec ts;
+ struct timespec tsrem = {0,0};
+
+ ts.tv_nsec = (cMillies % 1000) * 1000000;
+ ts.tv_sec = cMillies / 1000;
+ if (!nanosleep(&ts, &tsrem))
+ return VINF_SUCCESS;
+ }
+
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ uint64_t u64TS = ASMReadTSC();
+#endif
+
+ sched_yield();
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ u64TS = ASMReadTSC() - u64TS;
+ bool fRc = u64TS > 1500;
+ LogFlow(("RTThreadYield: returning %d (%llu ticks)\n", fRc, u64TS));
+#else
+ bool fRc = true; /* PORTME: Add heuristics for determining whether the cpus was yielded. */
+#endif
+ return fRc;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/time-posix.cpp b/src/VBox/Runtime/r3/posix/time-posix.cpp
new file mode 100644
index 00000000..3c028b73
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/time-posix.cpp
@@ -0,0 +1,99 @@
+/* $Id: time-posix.cpp $ */
+/** @file
+ * IPRT - Time, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#define RTTIME_INCL_TIMEVAL
+#include <sys/time.h>
+#include <time.h>
+
+#include <iprt/time.h>
+#include "internal/time.h"
+
+
+DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void)
+{
+#if defined(CLOCK_MONOTONIC) && !defined(RT_OS_L4) && !defined(RT_OS_OS2)
+ /* check monotonic clock first. */
+ static bool s_fMonoClock = true;
+ if (s_fMonoClock)
+ {
+ struct timespec ts;
+ if (!clock_gettime(CLOCK_MONOTONIC, &ts))
+ return (uint64_t)ts.tv_sec * RT_NS_1SEC_64
+ + ts.tv_nsec;
+ s_fMonoClock = false;
+ }
+#endif
+
+ /* fallback to gettimeofday(). */
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (uint64_t)tv.tv_sec * RT_NS_1SEC_64
+ + (uint64_t)(tv.tv_usec * RT_NS_1US);
+}
+
+
+/**
+ * Gets the current nanosecond timestamp.
+ *
+ * This differs from RTTimeNanoTS in that it will use system APIs and not do any
+ * resolution or performance optimizations.
+ *
+ * @returns nanosecond timestamp.
+ */
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+/**
+ * Gets the current millisecond timestamp.
+ *
+ * This differs from RTTimeNanoTS in that it will use system APIs and not do any
+ * resolution or performance optimizations.
+ *
+ * @returns millisecond timestamp.
+ */
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/timelocal-posix.cpp b/src/VBox/Runtime/r3/posix/timelocal-posix.cpp
new file mode 100644
index 00000000..40e2fc16
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/timelocal-posix.cpp
@@ -0,0 +1,215 @@
+/* $Id $ */
+/** @file
+ * IPRT - Local Time, Posix.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#define RTTIME_INCL_TIMEVAL
+#include <iprt/types.h>
+#include <iprt/assert.h>
+
+#include <sys/time.h>
+#include <time.h>
+
+#include <iprt/time.h>
+
+
+/**
+ * This tries to find the UTC offset for a given timespec.
+ *
+ * It does probably not take into account changes in daylight
+ * saving over the years or similar stuff.
+ *
+ * @returns UTC offset in nanoseconds.
+ * @param pTime The time.
+ * @param fCurrentTime Whether the input is current time or not.
+ * This is for avoid infinit recursion on errors in the fallback path.
+ */
+static int64_t rtTimeLocalUTCOffset(PCRTTIMESPEC pTime, bool fCurrentTime)
+{
+ RTTIMESPEC Fallback;
+
+ /*
+ * Convert to time_t.
+ */
+ int64_t i64UnixTime = RTTimeSpecGetSeconds(pTime);
+ time_t UnixTime = i64UnixTime;
+ if (UnixTime != i64UnixTime)
+ return fCurrentTime ? 0 : rtTimeLocalUTCOffset(RTTimeNow(&Fallback), true);
+
+ /*
+ * Explode it as both local and UTC time.
+ */
+ struct tm TmLocal;
+ if ( !localtime_r(&UnixTime, &TmLocal)
+ || !TmLocal.tm_year)
+ return fCurrentTime ? 0 : rtTimeLocalUTCOffset(RTTimeNow(&Fallback), true);
+ struct tm TmUtc;
+ if (!gmtime_r(&UnixTime, &TmUtc))
+ return fCurrentTime ? 0 : rtTimeLocalUTCOffset(RTTimeNow(&Fallback), true);
+
+ /*
+ * Calc the difference (if any).
+ * We ASSUME that the difference is less that 24 hours.
+ */
+ if ( TmLocal.tm_hour == TmUtc.tm_hour
+ && TmLocal.tm_min == TmUtc.tm_min
+ && TmLocal.tm_sec == TmUtc.tm_sec
+ && TmLocal.tm_mday == TmUtc.tm_mday)
+ return 0;
+
+ int cLocalSecs = TmLocal.tm_hour * 3600
+ + TmLocal.tm_min * 60
+ + TmLocal.tm_sec;
+ int cUtcSecs = TmUtc.tm_hour * 3600
+ + TmUtc.tm_min * 60
+ + TmUtc.tm_sec;
+ if (TmLocal.tm_mday != TmUtc.tm_mday)
+ {
+ /*
+ * Must add 24 hours to the value that is ahead of the other.
+ *
+ * To determine which is ahead was busted for a long long time (bugref:9078),
+ * so here are some examples and two different approaches.
+ *
+ * TmLocal TmUtc => Add 24:00 to => Diff
+ * 2007-04-02 01:00 2007-04-01 23:00 => TmLocal => +02:00
+ * 2007-04-01 01:00 2007-03-31 23:00 => TmLocal => +02:00
+ * 2007-03-31 01:00 2007-03-30 23:00 => TmLocal => +02:00
+ *
+ * 2007-04-01 01:00 2007-04-02 23:00 => TmUtc => -02:00
+ * 2007-03-31 23:00 2007-04-01 01:00 => TmUtc => -02:00
+ * 2007-03-30 23:00 2007-03-31 01:00 => TmUtc => -02:00
+ *
+ */
+#if 0
+ /* Using day of month turned out to be a little complicated. */
+ if ( ( TmLocal.tm_mday > TmUtc.tm_mday
+ && (TmUtc.tm_mday != 1 || TmLocal.tm_mday < 28) )
+ || (TmLocal.tm_mday == 1 && TmUtc.tm_mday >= 28) )
+ {
+ cLocalSecs += 24*60*60;
+ Assert( TmLocal.tm_yday - TmUtc.tm_yday == 1
+ || (TmLocal.tm_yday == 0 && TmUtc.tm_yday >= 364 && TmLocal.tm_year == TmUtc.tm_year + 1));
+ }
+ else
+ {
+ cUtcSecs += 24*60*60;
+ Assert( TmUtc.tm_yday - TmLocal.tm_yday == 1
+ || (TmUtc.tm_yday == 0 && TmLocal.tm_yday >= 364 && TmUtc.tm_year == TmLocal.tm_year + 1));
+ }
+#else
+ /* Using day of year and year is simpler. */
+ if ( ( TmLocal.tm_year == TmUtc.tm_year
+ && TmLocal.tm_yday > TmUtc.tm_yday)
+ || TmLocal.tm_year > TmUtc.tm_year)
+ {
+ cLocalSecs += 24*60*60;
+ Assert( TmLocal.tm_yday - TmUtc.tm_yday == 1
+ || (TmLocal.tm_yday == 0 && TmUtc.tm_yday >= 364 && TmLocal.tm_year == TmUtc.tm_year + 1));
+ }
+ else
+ {
+ cUtcSecs += 24*60*60;
+ Assert( TmUtc.tm_yday - TmLocal.tm_yday == 1
+ || (TmUtc.tm_yday == 0 && TmLocal.tm_yday >= 364 && TmUtc.tm_year == TmLocal.tm_year + 1));
+ }
+#endif
+ }
+
+ return (cLocalSecs - cUtcSecs) * INT64_C(1000000000);
+}
+
+
+/**
+ * Gets the current delta between UTC and local time.
+ *
+ * @code
+ * RTTIMESPEC LocalTime;
+ * RTTimeSpecAddNano(RTTimeNow(&LocalTime), RTTimeLocalDeltaNano());
+ * @endcode
+ *
+ * @returns Returns the nanosecond delta between UTC and local time.
+ */
+RTDECL(int64_t) RTTimeLocalDeltaNano(void)
+{
+ RTTIMESPEC Time;
+ return rtTimeLocalUTCOffset(RTTimeNow(&Time), true /* current time, skip fallback */);
+}
+
+
+/**
+ * Gets the delta between UTC and local time at the given time.
+ *
+ * @code
+ * RTTIMESPEC LocalTime;
+ * RTTimeNow(&LocalTime);
+ * RTTimeSpecAddNano(&LocalTime, RTTimeLocalDeltaNanoFor(&LocalTime));
+ * @endcode
+ *
+ * @param pTimeSpec The time spec giving the time to get the delta for.
+ * @returns Returns the nanosecond delta between UTC and local time.
+ */
+RTDECL(int64_t) RTTimeLocalDeltaNanoFor(PCRTTIMESPEC pTimeSpec)
+{
+ AssertPtr(pTimeSpec);
+ return rtTimeLocalUTCOffset(pTimeSpec, false /* current time, skip fallback */);
+}
+
+
+/**
+ * Explodes a time spec to the localized timezone.
+ *
+ * @returns pTime.
+ * @param pTime Where to store the exploded time.
+ * @param pTimeSpec The time spec to exploded. (UTC)
+ */
+RTDECL(PRTTIME) RTTimeLocalExplode(PRTTIME pTime, PCRTTIMESPEC pTimeSpec)
+{
+ RTTIMESPEC LocalTime = *pTimeSpec;
+ int64_t cNsUtcOffset = rtTimeLocalUTCOffset(&LocalTime, true /* current time, skip fallback */);
+ RTTimeSpecAddNano(&LocalTime, cNsUtcOffset);
+ pTime = RTTimeExplode(pTime, &LocalTime);
+ if (pTime)
+ {
+ pTime->fFlags = (pTime->fFlags & ~RTTIME_FLAGS_TYPE_MASK) | RTTIME_FLAGS_TYPE_LOCAL;
+ pTime->offUTC = cNsUtcOffset / RT_NS_1MIN;
+ }
+ return pTime;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/timer-posix.cpp b/src/VBox/Runtime/r3/posix/timer-posix.cpp
new file mode 100644
index 00000000..59c905b9
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/timer-posix.cpp
@@ -0,0 +1,847 @@
+/* $Id: timer-posix.cpp $ */
+/** @file
+ * IPRT - Timer, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Enables the use of POSIX RT timers. */
+#ifndef RT_OS_SOLARIS /* Solaris 10 doesn't have SIGEV_THREAD */
+# define IPRT_WITH_POSIX_TIMERS
+#endif /* !RT_OS_SOLARIS */
+
+/** @def RT_TIMER_SIGNAL
+ * The signal number that the timers use.
+ * We currently use SIGALRM for both setitimer and posix real time timers
+ * out of simplicity, but we might want change this later for the posix ones. */
+#ifdef IPRT_WITH_POSIX_TIMERS
+# define RT_TIMER_SIGNAL SIGALRM
+#else
+# define RT_TIMER_SIGNAL SIGALRM
+#endif
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIMER
+#include <iprt/timer.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/asm.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/once.h>
+#include <iprt/err.h>
+#include <iprt/initterm.h>
+#include <iprt/critsect.h>
+#include "internal/magics.h"
+
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#ifdef RT_OS_LINUX
+# include <linux/rtc.h>
+#endif
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <pthread.h>
+#if defined(RT_OS_DARWIN)
+# define sigprocmask pthread_sigmask /* On xnu sigprocmask works on the process, not the calling thread as elsewhere. */
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef IPRT_WITH_POSIX_TIMERS
+/** Init the critsect on first call. */
+static RTONCE g_TimerOnce = RTONCE_INITIALIZER;
+/** Global critsect that serializes timer creation and destruction.
+ * This is lazily created on the first RTTimerCreateEx call and will not be
+ * freed up (I'm afraid). */
+static RTCRITSECT g_TimerCritSect;
+/**
+ * Global counter of RTTimer instances. The signal thread is
+ * started when it changes from 0 to 1. The signal thread
+ * terminates when it becomes 0 again.
+ */
+static uint32_t volatile g_cTimerInstances;
+/** The signal handling thread. */
+static RTTHREAD g_TimerThread;
+#endif /* IPRT_WITH_POSIX_TIMERS */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal representation of a timer handle.
+ */
+typedef struct RTTIMER
+{
+ /** Magic.
+ * This is RTTIMER_MAGIC, but changes to something else before the timer
+ * is destroyed to indicate clearly that thread should exit. */
+ uint32_t volatile u32Magic;
+ /** Flag indicating the timer is suspended. */
+ uint8_t volatile fSuspended;
+ /** Flag indicating that the timer has been destroyed. */
+ uint8_t volatile fDestroyed;
+#ifndef IPRT_WITH_POSIX_TIMERS /** @todo We have to take the signals on a dedicated timer thread as
+ * we (might) have code assuming that signals doesn't screw around
+ * on existing threads. (It would be sufficient to have one thread
+ * per signal of course since the signal will be masked while it's
+ * running, however, it may just cause more complications than its
+ * worth - sigwait/sigwaitinfo work atomically anyway...)
+ * Also, must block the signal in the thread main procedure too. */
+ /** The timer thread. */
+ RTTHREAD Thread;
+ /** Event semaphore on which the thread is blocked. */
+ RTSEMEVENT Event;
+#endif /* !IPRT_WITH_POSIX_TIMERS */
+ /** User argument. */
+ void *pvUser;
+ /** Callback. */
+ PFNRTTIMER pfnTimer;
+ /** The timer interval. 0 if one-shot. */
+ uint64_t u64NanoInterval;
+#ifndef IPRT_WITH_POSIX_TIMERS
+ /** The first shot interval. 0 if ASAP. */
+ uint64_t volatile u64NanoFirst;
+#endif /* !IPRT_WITH_POSIX_TIMERS */
+ /** The current timer tick. */
+ uint64_t volatile iTick;
+#ifndef IPRT_WITH_POSIX_TIMERS
+ /** The error/status of the timer.
+ * Initially -1, set to 0 when the timer have been successfully started, and
+ * to errno on failure in starting the timer. */
+ int volatile iError;
+#else /* IPRT_WITH_POSIX_TIMERS */
+ timer_t NativeTimer;
+#endif /* IPRT_WITH_POSIX_TIMERS */
+
+} RTTIMER;
+
+
+
+#ifdef IPRT_WITH_POSIX_TIMERS
+
+/**
+ * RTOnce callback that initializes the critical section.
+ *
+ * @returns RTCritSectInit return code.
+ * @param pvUser NULL, ignored.
+ *
+ */
+static DECLCALLBACK(int) rtTimerOnce(void *pvUser)
+{
+ NOREF(pvUser);
+ return RTCritSectInit(&g_TimerCritSect);
+}
+#endif
+
+
+/**
+ * Signal handler which ignore everything it gets.
+ *
+ * @param iSignal The signal number.
+ */
+static void rttimerSignalIgnore(int iSignal)
+{
+ //AssertBreakpoint();
+ NOREF(iSignal);
+}
+
+
+/**
+ * RT_TIMER_SIGNAL wait thread.
+ */
+static DECLCALLBACK(int) rttimerThread(RTTHREAD hThreadSelf, void *pvArg)
+{
+ NOREF(hThreadSelf); NOREF(pvArg);
+#ifndef IPRT_WITH_POSIX_TIMERS
+ PRTTIMER pTimer = (PRTTIMER)pvArg;
+ RTTIMER Timer = *pTimer;
+ Assert(pTimer->u32Magic == RTTIMER_MAGIC);
+#endif /* !IPRT_WITH_POSIX_TIMERS */
+
+ /*
+ * Install signal handler.
+ */
+ struct sigaction SigAct;
+ memset(&SigAct, 0, sizeof(SigAct));
+ SigAct.sa_flags = SA_RESTART;
+ sigemptyset(&SigAct.sa_mask);
+ SigAct.sa_handler = rttimerSignalIgnore;
+ if (sigaction(RT_TIMER_SIGNAL, &SigAct, NULL))
+ {
+ SigAct.sa_flags &= ~SA_RESTART;
+ if (sigaction(RT_TIMER_SIGNAL, &SigAct, NULL))
+ AssertMsgFailed(("sigaction failed, errno=%d\n", errno));
+ }
+
+ /*
+ * Mask most signals except those which might be used by the pthread implementation (linux).
+ */
+ sigset_t SigSet;
+ sigfillset(&SigSet);
+ sigdelset(&SigSet, SIGTERM);
+ sigdelset(&SigSet, SIGHUP);
+ sigdelset(&SigSet, SIGINT);
+ sigdelset(&SigSet, SIGABRT);
+ sigdelset(&SigSet, SIGKILL);
+#ifdef SIGRTMIN
+ for (int iSig = SIGRTMIN; iSig < SIGRTMAX; iSig++)
+ sigdelset(&SigSet, iSig);
+#endif
+ if (sigprocmask(SIG_SETMASK, &SigSet, NULL))
+ {
+#ifdef IPRT_WITH_POSIX_TIMERS
+ int rc = RTErrConvertFromErrno(errno);
+#else
+ int rc = pTimer->iError = RTErrConvertFromErrno(errno);
+#endif
+ AssertMsgFailed(("sigprocmask -> errno=%d\n", errno));
+ return rc;
+ }
+
+ /*
+ * The work loop.
+ */
+ RTThreadUserSignal(hThreadSelf);
+
+#ifndef IPRT_WITH_POSIX_TIMERS
+ while ( !pTimer->fDestroyed
+ && pTimer->u32Magic == RTTIMER_MAGIC)
+ {
+ /*
+ * Wait for a start or destroy event.
+ */
+ if (pTimer->fSuspended)
+ {
+ int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)
+ {
+ AssertRC(rc);
+ if (pTimer->fDestroyed)
+ continue;
+ RTThreadSleep(1000); /* Don't cause trouble! */
+ }
+ if ( pTimer->fSuspended
+ || pTimer->fDestroyed)
+ continue;
+ }
+
+ /*
+ * Start the timer.
+ *
+ * For some SunOS (/SysV?) threading compatibility Linux will only
+ * deliver the RT_TIMER_SIGNAL to the thread calling setitimer(). Therefore
+ * we have to call it here.
+ *
+ * It turns out this might not always be the case, see RT_TIMER_SIGNAL killing
+ * processes on RH 2.4.21.
+ */
+ struct itimerval TimerVal;
+ if (pTimer->u64NanoFirst)
+ {
+ uint64_t u64 = RT_MAX(1000, pTimer->u64NanoFirst);
+ TimerVal.it_value.tv_sec = u64 / 1000000000;
+ TimerVal.it_value.tv_usec = (u64 % 1000000000) / 1000;
+ }
+ else
+ {
+ TimerVal.it_value.tv_sec = 0;
+ TimerVal.it_value.tv_usec = 10;
+ }
+ if (pTimer->u64NanoInterval)
+ {
+ uint64_t u64 = RT_MAX(1000, pTimer->u64NanoInterval);
+ TimerVal.it_interval.tv_sec = u64 / 1000000000;
+ TimerVal.it_interval.tv_usec = (u64 % 1000000000) / 1000;
+ }
+ else
+ {
+ TimerVal.it_interval.tv_sec = 0;
+ TimerVal.it_interval.tv_usec = 0;
+ }
+
+ if (setitimer(ITIMER_REAL, &TimerVal, NULL))
+ {
+ ASMAtomicXchgU8(&pTimer->fSuspended, true);
+ pTimer->iError = RTErrConvertFromErrno(errno);
+ RTThreadUserSignal(hThreadSelf);
+ continue; /* back to suspended mode. */
+ }
+ pTimer->iError = 0;
+ RTThreadUserSignal(hThreadSelf);
+
+ /*
+ * Timer Service Loop.
+ */
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, RT_TIMER_SIGNAL);
+ do
+ {
+ siginfo_t SigInfo;
+ RT_ZERO(SigInfo);
+#ifdef RT_OS_DARWIN
+ if (RT_LIKELY(sigwait(&SigSet, &SigInfo.si_signo) >= 0))
+ {
+#else
+ if (RT_LIKELY(sigwaitinfo(&SigSet, &SigInfo) >= 0))
+ {
+ if (RT_LIKELY(SigInfo.si_signo == RT_TIMER_SIGNAL))
+#endif
+ {
+ if (RT_UNLIKELY( pTimer->fSuspended
+ || pTimer->fDestroyed
+ || pTimer->u32Magic != RTTIMER_MAGIC))
+ break;
+
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
+
+ /* auto suspend one-shot timers. */
+ if (RT_UNLIKELY(!pTimer->u64NanoInterval))
+ {
+ ASMAtomicWriteU8(&pTimer->fSuspended, true);
+ break;
+ }
+ }
+ }
+ else if (errno != EINTR)
+ AssertMsgFailed(("sigwaitinfo -> errno=%d\n", errno));
+ } while (RT_LIKELY( !pTimer->fSuspended
+ && !pTimer->fDestroyed
+ && pTimer->u32Magic == RTTIMER_MAGIC));
+
+ /*
+ * Disable the timer.
+ */
+ struct itimerval TimerVal2 = {{0,0}, {0,0}};
+ if (setitimer(ITIMER_REAL, &TimerVal2, NULL))
+ AssertMsgFailed(("setitimer(ITIMER_REAL,&{0}, NULL) failed, errno=%d\n", errno));
+
+ /*
+ * ACK any pending suspend request.
+ */
+ if (!pTimer->fDestroyed)
+ {
+ pTimer->iError = 0;
+ RTThreadUserSignal(hThreadSelf);
+ }
+ }
+
+ /*
+ * Exit.
+ */
+ pTimer->iError = 0;
+ RTThreadUserSignal(hThreadSelf);
+
+#else /* IPRT_WITH_POSIX_TIMERS */
+
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, RT_TIMER_SIGNAL);
+ while (g_cTimerInstances)
+ {
+ siginfo_t SigInfo;
+ RT_ZERO(SigInfo);
+ if (RT_LIKELY(sigwaitinfo(&SigSet, &SigInfo) >= 0))
+ {
+ LogFlow(("rttimerThread: signo=%d pTimer=%p\n", SigInfo.si_signo, SigInfo.si_value.sival_ptr));
+ if (RT_LIKELY( SigInfo.si_signo == RT_TIMER_SIGNAL
+ && SigInfo.si_code == SI_TIMER)) /* The SI_TIMER check is *essential* because of the pthread_kill. */
+ {
+ PRTTIMER pTimer = (PRTTIMER)SigInfo.si_value.sival_ptr;
+ AssertPtr(pTimer);
+ if (RT_UNLIKELY( !RT_VALID_PTR(pTimer)
+ || ASMAtomicUoReadU8(&pTimer->fSuspended)
+ || ASMAtomicUoReadU8(&pTimer->fDestroyed)
+ || pTimer->u32Magic != RTTIMER_MAGIC))
+ continue;
+
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
+
+ /* auto suspend one-shot timers. */
+ if (RT_UNLIKELY(!pTimer->u64NanoInterval))
+ ASMAtomicWriteU8(&pTimer->fSuspended, true);
+ }
+ }
+ }
+#endif /* IPRT_WITH_POSIX_TIMERS */
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
+{
+ /*
+ * We don't support the fancy MP features.
+ */
+ if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * We need the signal masks to be set correctly, which they won't be in
+ * unobtrusive mode.
+ */
+ if (RTR3InitIsUnobtrusive())
+ return VERR_NOT_SUPPORTED;
+
+#ifndef IPRT_WITH_POSIX_TIMERS
+ /*
+ * Check if timer is busy.
+ */
+ struct itimerval TimerVal;
+ if (getitimer(ITIMER_REAL, &TimerVal))
+ {
+ AssertMsgFailed(("getitimer() -> errno=%d\n", errno));
+ return VERR_NOT_IMPLEMENTED;
+ }
+ if ( TimerVal.it_value.tv_usec
+ || TimerVal.it_value.tv_sec
+ || TimerVal.it_interval.tv_usec
+ || TimerVal.it_interval.tv_sec)
+ {
+ AssertMsgFailed(("A timer is running. System limit is one timer per process!\n"));
+ return VERR_TIMER_BUSY;
+ }
+#endif /* !IPRT_WITH_POSIX_TIMERS */
+
+ /*
+ * Block RT_TIMER_SIGNAL from calling thread.
+ */
+ sigset_t SigSet;
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, RT_TIMER_SIGNAL);
+ sigprocmask(SIG_BLOCK, &SigSet, NULL);
+
+#ifndef IPRT_WITH_POSIX_TIMERS /** @todo combine more of the setitimer/timer_create code. setitimer could also use the global thread. */
+ /** @todo Move this RTC hack else where... */
+ static bool fDoneRTC;
+ if (!fDoneRTC)
+ {
+ fDoneRTC = true;
+ /* check resolution. */
+ TimerVal.it_interval.tv_sec = 0;
+ TimerVal.it_interval.tv_usec = 1000;
+ TimerVal.it_value = TimerVal.it_interval;
+ if ( setitimer(ITIMER_REAL, &TimerVal, NULL)
+ || getitimer(ITIMER_REAL, &TimerVal)
+ || TimerVal.it_interval.tv_usec > 1000)
+ {
+ /*
+ * Try open /dev/rtc to set the irq rate to 1024 and
+ * turn periodic
+ */
+ Log(("RTTimerCreate: interval={%ld,%ld} trying to adjust /dev/rtc!\n", TimerVal.it_interval.tv_sec, TimerVal.it_interval.tv_usec));
+# ifdef RT_OS_LINUX
+ int fh = open("/dev/rtc", O_RDONLY);
+ if (fh >= 0)
+ {
+ if ( ioctl(fh, RTC_IRQP_SET, 1024) < 0
+ || ioctl(fh, RTC_PIE_ON, 0) < 0)
+ Log(("RTTimerCreate: couldn't configure rtc! errno=%d\n", errno));
+ ioctl(fh, F_SETFL, O_ASYNC);
+ ioctl(fh, F_SETOWN, getpid());
+ /* not so sure if closing it is a good idea... */
+ //close(fh);
+ }
+ else
+ Log(("RTTimerCreate: couldn't configure rtc! open failed with errno=%d\n", errno));
+# endif
+ }
+ /* disable it */
+ TimerVal.it_interval.tv_sec = 0;
+ TimerVal.it_interval.tv_usec = 0;
+ TimerVal.it_value = TimerVal.it_interval;
+ setitimer(ITIMER_REAL, &TimerVal, NULL);
+ }
+
+ /*
+ * Create a new timer.
+ */
+ int rc;
+ PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
+ if (pTimer)
+ {
+ pTimer->u32Magic = RTTIMER_MAGIC;
+ pTimer->fSuspended = true;
+ pTimer->fDestroyed = false;
+ pTimer->Thread = NIL_RTTHREAD;
+ pTimer->Event = NIL_RTSEMEVENT;
+ pTimer->pfnTimer = pfnTimer;
+ pTimer->pvUser = pvUser;
+ pTimer->u64NanoInterval = u64NanoInterval;
+ pTimer->u64NanoFirst = 0;
+ pTimer->iTick = 0;
+ pTimer->iError = 0;
+ rc = RTSemEventCreate(&pTimer->Event);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&pTimer->Thread, rttimerThread, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Wait for the timer thread to initialize it self.
+ * This might take a little while...
+ */
+ rc = RTThreadUserWait(pTimer->Thread, 45*1000);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadUserReset(pTimer->Thread); AssertRC(rc);
+ rc = pTimer->iError;
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RTThreadYield(); /* <-- Horrible hack to make tstTimer work. (linux 2.6.12) */
+ *ppTimer = pTimer;
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* bail out */
+ ASMAtomicXchgU8(&pTimer->fDestroyed, true);
+ ASMAtomicXchgU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
+ RTThreadWait(pTimer->Thread, 45*1000, NULL);
+ }
+ RTSemEventDestroy(pTimer->Event);
+ pTimer->Event = NIL_RTSEMEVENT;
+ }
+ RTMemFree(pTimer);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+#else /* IPRT_WITH_POSIX_TIMERS */
+
+ /*
+ * Do the global init first.
+ */
+ int rc = RTOnce(&g_TimerOnce, rtTimerOnce, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Create a new timer structure.
+ */
+ LogFlow(("RTTimerCreateEx: u64NanoInterval=%llu fFlags=%lu\n", u64NanoInterval, fFlags));
+ PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
+ if (pTimer)
+ {
+ /* Initialize timer structure. */
+ pTimer->u32Magic = RTTIMER_MAGIC;
+ pTimer->fSuspended = true;
+ pTimer->fDestroyed = false;
+ pTimer->pfnTimer = pfnTimer;
+ pTimer->pvUser = pvUser;
+ pTimer->u64NanoInterval = u64NanoInterval;
+ pTimer->iTick = 0;
+
+ /*
+ * Create a timer that deliver RT_TIMER_SIGNAL upon timer expiration.
+ */
+ struct sigevent SigEvt;
+ SigEvt.sigev_notify = SIGEV_SIGNAL;
+ SigEvt.sigev_signo = RT_TIMER_SIGNAL;
+ SigEvt.sigev_value.sival_ptr = pTimer; /* sigev_value gets copied to siginfo. */
+ int err = timer_create(CLOCK_REALTIME, &SigEvt, &pTimer->NativeTimer);
+ if (!err)
+ {
+ /*
+ * Increment the timer count, do this behind the critsect to avoid races.
+ */
+ RTCritSectEnter(&g_TimerCritSect);
+
+ if (ASMAtomicIncU32(&g_cTimerInstances) != 1)
+ {
+ Assert(g_cTimerInstances > 1);
+ RTCritSectLeave(&g_TimerCritSect);
+
+ LogFlow(("RTTimerCreateEx: rc=%Rrc pTimer=%p (thread already running)\n", rc, pTimer));
+ *ppTimer = pTimer;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Create the signal handling thread. It will wait for the signal
+ * and execute the timer functions.
+ */
+ rc = RTThreadCreate(&g_TimerThread, rttimerThread, NULL, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadUserWait(g_TimerThread, 45*1000); /* this better not fail... */
+ if (RT_SUCCESS(rc))
+ {
+ RTCritSectLeave(&g_TimerCritSect);
+
+ LogFlow(("RTTimerCreateEx: rc=%Rrc pTimer=%p (thread already running)\n", rc, pTimer));
+ *ppTimer = pTimer;
+ return VINF_SUCCESS;
+ }
+ /* darn, what do we do here? */
+ }
+
+ /* bail out */
+ ASMAtomicDecU32(&g_cTimerInstances);
+ Assert(!g_cTimerInstances);
+
+ RTCritSectLeave(&g_TimerCritSect);
+
+ timer_delete(pTimer->NativeTimer);
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(err);
+ Log(("RTTimerCreateEx: err=%d (%Rrc)\n", err, rc));
+ }
+
+ RTMemFree(pTimer);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+#endif /* IPRT_WITH_POSIX_TIMERS */
+ return rc;
+}
+
+
+RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
+{
+ LogFlow(("RTTimerDestroy: pTimer=%p\n", pTimer));
+
+ /*
+ * Validate input.
+ */
+ /* NULL is ok. */
+ if (!pTimer)
+ return VINF_SUCCESS;
+ int rc = VINF_SUCCESS;
+ AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+#ifdef IPRT_WITH_POSIX_TIMERS
+ AssertReturn(g_TimerThread != RTThreadSelf(), VERR_INTERNAL_ERROR);
+#else
+ AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
+#endif
+
+ /*
+ * Mark the semaphore as destroyed.
+ */
+ ASMAtomicWriteU8(&pTimer->fDestroyed, true);
+ ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
+
+#ifdef IPRT_WITH_POSIX_TIMERS
+ /*
+ * Suspend the timer if it's running.
+ */
+ if (!pTimer->fSuspended)
+ {
+ struct itimerspec TimerSpec;
+ TimerSpec.it_value.tv_sec = 0;
+ TimerSpec.it_value.tv_nsec = 0;
+ TimerSpec.it_interval.tv_sec = 0;
+ TimerSpec.it_interval.tv_nsec = 0;
+ int err = timer_settime(pTimer->NativeTimer, 0, &TimerSpec, NULL); NOREF(err);
+ AssertMsg(!err, ("%d / %d\n", err, errno));
+ }
+#endif
+
+ /*
+ * Poke the thread and wait for it to finish.
+ * This is only done for the last timer when using posix timers.
+ */
+#ifdef IPRT_WITH_POSIX_TIMERS
+ RTTHREAD Thread = NIL_RTTHREAD;
+ RTCritSectEnter(&g_TimerCritSect);
+ if (ASMAtomicDecU32(&g_cTimerInstances) == 0)
+ {
+ Thread = g_TimerThread;
+ g_TimerThread = NIL_RTTHREAD;
+ }
+ RTCritSectLeave(&g_TimerCritSect);
+#else /* IPRT_WITH_POSIX_TIMERS */
+ RTTHREAD Thread = pTimer->Thread;
+ rc = RTSemEventSignal(pTimer->Event);
+ AssertRC(rc);
+#endif /* IPRT_WITH_POSIX_TIMERS */
+ if (Thread != NIL_RTTHREAD)
+ {
+ /* Signal it so it gets out of the sigwait if it's stuck there... */
+ pthread_kill((pthread_t)RTThreadGetNative(Thread), RT_TIMER_SIGNAL);
+
+ /*
+ * Wait for the thread to complete.
+ */
+ rc = RTThreadWait(Thread, 30 * 1000, NULL);
+ AssertRC(rc);
+ }
+
+
+ /*
+ * Free up the resources associated with the timer.
+ */
+#ifdef IPRT_WITH_POSIX_TIMERS
+ timer_delete(pTimer->NativeTimer);
+#else
+ RTSemEventDestroy(pTimer->Event);
+ pTimer->Event = NIL_RTSEMEVENT;
+#endif /* !IPRT_WITH_POSIX_TIMERS */
+ if (RT_SUCCESS(rc))
+ RTMemFree(pTimer);
+ return rc;
+}
+
+
+RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+#ifndef IPRT_WITH_POSIX_TIMERS
+ AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
+#endif
+
+ /*
+ * Already running?
+ */
+ if (!ASMAtomicXchgU8(&pTimer->fSuspended, false))
+ return VERR_TIMER_ACTIVE;
+ LogFlow(("RTTimerStart: pTimer=%p u64First=%llu u64NanoInterval=%llu\n", pTimer, u64First, pTimer->u64NanoInterval));
+
+#ifndef IPRT_WITH_POSIX_TIMERS
+ /*
+ * Tell the thread to start servicing the timer.
+ * Wait for it to ACK the request to avoid reset races.
+ */
+ RTThreadUserReset(pTimer->Thread);
+ ASMAtomicUoWriteU64(&pTimer->u64NanoFirst, u64First);
+ ASMAtomicUoWriteU64(&pTimer->iTick, 0);
+ ASMAtomicWriteU8(&pTimer->fSuspended, false);
+ int rc = RTSemEventSignal(pTimer->Event);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadUserWait(pTimer->Thread, 45*1000);
+ AssertRC(rc);
+ RTThreadUserReset(pTimer->Thread);
+ }
+ else
+ AssertRC(rc);
+
+#else /* IPRT_WITH_POSIX_TIMERS */
+ /*
+ * Start the timer.
+ */
+ struct itimerspec TimerSpec;
+ TimerSpec.it_value.tv_sec = u64First / 1000000000; /* nanosec => sec */
+ TimerSpec.it_value.tv_nsec = u64First ? u64First % 1000000000 : 10; /* 0 means disable, replace it with 10. */
+ TimerSpec.it_interval.tv_sec = pTimer->u64NanoInterval / 1000000000;
+ TimerSpec.it_interval.tv_nsec = pTimer->u64NanoInterval % 1000000000;
+ int err = timer_settime(pTimer->NativeTimer, 0, &TimerSpec, NULL);
+ int rc = err == 0 ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
+#endif /* IPRT_WITH_POSIX_TIMERS */
+
+ if (RT_FAILURE(rc))
+ ASMAtomicXchgU8(&pTimer->fSuspended, false);
+ return rc;
+}
+
+
+RTDECL(int) RTTimerStop(PRTTIMER pTimer)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+
+ /*
+ * Already running?
+ */
+ if (ASMAtomicXchgU8(&pTimer->fSuspended, true))
+ return VERR_TIMER_SUSPENDED;
+ LogFlow(("RTTimerStop: pTimer=%p\n", pTimer));
+
+#ifndef IPRT_WITH_POSIX_TIMERS
+ /*
+ * Tell the thread to stop servicing the timer.
+ */
+ RTThreadUserReset(pTimer->Thread);
+ ASMAtomicXchgU8(&pTimer->fSuspended, true);
+ int rc = VINF_SUCCESS;
+ if (RTThreadSelf() != pTimer->Thread)
+ {
+ pthread_kill((pthread_t)RTThreadGetNative(pTimer->Thread), RT_TIMER_SIGNAL);
+ rc = RTThreadUserWait(pTimer->Thread, 45*1000);
+ AssertRC(rc);
+ RTThreadUserReset(pTimer->Thread);
+ }
+
+#else /* IPRT_WITH_POSIX_TIMERS */
+ /*
+ * Stop the timer.
+ */
+ struct itimerspec TimerSpec;
+ TimerSpec.it_value.tv_sec = 0;
+ TimerSpec.it_value.tv_nsec = 0;
+ TimerSpec.it_interval.tv_sec = 0;
+ TimerSpec.it_interval.tv_nsec = 0;
+ int err = timer_settime(pTimer->NativeTimer, 0, &TimerSpec, NULL);
+ int rc = err == 0 ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
+#endif /* IPRT_WITH_POSIX_TIMERS */
+
+ return rc;
+}
+
+
+RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
+{
+ AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+ NOREF(u64NanoInterval);
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/tls-posix.cpp b/src/VBox/Runtime/r3/posix/tls-posix.cpp
new file mode 100644
index 00000000..22ef28ab
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/tls-posix.cpp
@@ -0,0 +1,119 @@
+/* $Id: tls-posix.cpp $ */
+/** @file
+ * IPRT - Thread Local Storage (TLS), POSIX.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <errno.h>
+#include <pthread.h>
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+AssertCompile(sizeof(pthread_key_t) <= sizeof(RTTLS));
+
+
+RTR3DECL(RTTLS) RTTlsAlloc(void)
+{
+ pthread_key_t iTls = (pthread_key_t)NIL_RTTLS;
+ int rc = pthread_key_create(&iTls, NULL);
+ if (!rc)
+ {
+ Assert(iTls != (pthread_key_t)NIL_RTTLS);
+ return iTls;
+ }
+ return NIL_RTTLS;
+}
+
+
+RTR3DECL(int) RTTlsAllocEx(PRTTLS piTls, PFNRTTLSDTOR pfnDestructor)
+{
+ pthread_key_t iTls = (pthread_key_t)NIL_RTTLS;
+#if defined(__GNUC__) && defined(RT_ARCH_X86)
+ int rc = pthread_key_create(&iTls, (void (*)(void*))pfnDestructor);
+#else
+ int rc = pthread_key_create(&iTls, pfnDestructor);
+#endif
+ if (!rc)
+ {
+ *piTls = iTls;
+ Assert((pthread_key_t)*piTls == iTls);
+ Assert(*piTls != NIL_RTTLS);
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(rc);
+}
+
+
+RTR3DECL(int) RTTlsFree(RTTLS iTls)
+{
+ if (iTls == NIL_RTTLS)
+ return VINF_SUCCESS;
+ int rc = pthread_key_delete(iTls);
+ if (!rc)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(rc);
+}
+
+
+RTR3DECL(void *) RTTlsGet(RTTLS iTls)
+{
+ return pthread_getspecific(iTls);
+}
+
+
+RTR3DECL(int) RTTlsGetEx(RTTLS iTls, void **ppvValue)
+{
+ if (RT_UNLIKELY(iTls == NIL_RTTLS))
+ return VERR_INVALID_PARAMETER;
+ *ppvValue = pthread_getspecific(iTls);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTlsSet(RTTLS iTls, void *pvValue)
+{
+ int rc = pthread_setspecific(iTls, pvValue);
+ if (RT_UNLIKELY(rc != 0))
+ return RTErrConvertFromErrno(rc);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/posix/utf8-posix.cpp b/src/VBox/Runtime/r3/posix/utf8-posix.cpp
new file mode 100644
index 00000000..5f4c947e
--- /dev/null
+++ b/src/VBox/Runtime/r3/posix/utf8-posix.cpp
@@ -0,0 +1,709 @@
+/* $Id: utf8-posix.cpp $ */
+/** @file
+ * IPRT - UTF-8 helpers, POSIX.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/string.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+#include <errno.h>
+#include <locale.h>
+#ifdef RT_OS_DARWIN
+# include <stdlib.h>
+#endif
+
+/* iconv prototype changed with 165+ (thanks to PSARC/2010/160 Bugster 7037400) */
+#if defined(RT_OS_SOLARIS)
+# if !defined(_XPG6)
+# define IPRT_XPG6_TMP_DEF
+# define _XPG6
+# endif
+# if defined(__USE_LEGACY_PROTOTYPES__)
+# define IPRT_LEGACY_PROTO_TMP_DEF
+# undef __USE_LEGACY_PROTOTYPES__
+# endif
+#endif /* RT_OS_SOLARIS */
+
+# include <iconv.h>
+
+#if defined(RT_OS_SOLARIS)
+# if defined(IPRT_XPG6_TMP_DEF)
+# undef _XPG6
+# undef IPRT_XPG6_TMP_DEF
+# endif
+# if defined(IPRT_LEGACY_PROTO_TMP_DEF)
+# define __USE_LEGACY_PROTOTYPES__
+# undef IPRT_LEGACY_PROTO_TMP_DEF
+# endif
+#endif /* RT_OS_SOLARIS */
+
+#include <wctype.h>
+
+#include <langinfo.h>
+
+#include "internal/alignmentchecks.h"
+#include "internal/string.h"
+#ifdef RT_WITH_ICONV_CACHE
+# include "internal/thread.h"
+AssertCompile(sizeof(iconv_t) <= sizeof(void *));
+#endif
+
+
+/* There are different opinions about the constness of the input buffer. */
+#if defined(RT_OS_LINUX) || defined(RT_OS_HAIKU) || defined(RT_OS_SOLARIS) \
+ || (defined(RT_OS_DARWIN) && defined(_DARWIN_FEATURE_UNIX_CONFORMANCE))
+# define NON_CONST_ICONV_INPUT
+#endif
+#ifdef RT_OS_FREEBSD
+# include <sys/param.h>
+# if __FreeBSD_version >= 1002000 /* Changed around 10.2.2 (https://svnweb.freebsd.org/base?view=revision&revision=281550) */
+# define NON_CONST_ICONV_INPUT
+# else
+# error __FreeBSD_version__
+# endif
+#endif
+#ifdef RT_OS_NETBSD
+/* iconv constness was changed on 2019-10-24, shortly after 9.99.17 */
+# include <sys/param.h>
+# if __NetBSD_Prereq__(9,99,18)
+# define NON_CONST_ICONV_INPUT
+# endif
+#endif
+
+
+/**
+ * Gets the codeset of the current locale (LC_CTYPE).
+ *
+ * @returns Pointer to read-only string with the codeset name.
+ */
+DECLHIDDEN(const char *) rtStrGetLocaleCodeset(void)
+{
+#ifdef RT_OS_DARWIN
+ /*
+ * @bugref{10153}: If no locale specified in the environment (typically the
+ * case when launched via Finder, LaunchPad or similar) default to UTF-8.
+ */
+ static int8_t volatile s_fIsUtf8 = -1;
+ int8_t fIsUtf8 = s_fIsUtf8;
+ if (fIsUtf8)
+ {
+ if (fIsUtf8 == true)
+ return "UTF-8";
+
+ /* Initialize: */
+ fIsUtf8 = true;
+ static const char * const s_papszVariables[] = { "LC_ALL", "LC_CTYPE", "LANG" };
+ for (size_t i = 0; i < RT_ELEMENTS(s_papszVariables); i++)
+ {
+ const char *pszValue = getenv(s_papszVariables[i]);
+ if (pszValue && *pszValue)
+ {
+ fIsUtf8 = false;
+ break;
+ }
+ }
+ s_fIsUtf8 = fIsUtf8;
+ if (fIsUtf8 == true)
+ return "UTF-8";
+ }
+#endif
+ return nl_langinfo(CODESET);
+}
+
+
+/**
+ * Checks if the codeset specified by current locale (LC_CTYPE) is UTF-8.
+ *
+ * @returns true if UTF-8, false if not.
+ */
+DECLHIDDEN(bool) rtStrIsLocaleCodesetUtf8(void)
+{
+ return rtStrIsCodesetUtf8(rtStrGetLocaleCodeset());
+}
+
+
+/**
+ * Checks if @a pszCodeset specified UTF-8.
+ *
+ * @returns true if UTF-8, false if not.
+ * @param pszCodeset Codeset to test.
+ */
+DECLHIDDEN(bool) rtStrIsCodesetUtf8(const char *pszCodeset)
+{
+ if (pszCodeset)
+ {
+ /* Skip leading spaces just in case: */
+ while (RT_C_IS_SPACE(*pszCodeset))
+ pszCodeset++;
+
+ /* If prefixed by 'ISO-10646/' skip that (iconv access this, dunno about
+ LC_CTYPE et al., but play it safe): */
+ if ( strncmp(pszCodeset, RT_STR_TUPLE("ISO-10646/")) == 0
+ || strncmp(pszCodeset, RT_STR_TUPLE("iso-10646/")) == 0)
+ pszCodeset += sizeof("ISO-10646/") - 1;
+
+ /* Match 'utf': */
+ if ( (pszCodeset[0] == 'u' || pszCodeset[0] == 'U')
+ && (pszCodeset[1] == 't' || pszCodeset[1] == 'T')
+ && (pszCodeset[2] == 'f' || pszCodeset[2] == 'F'))
+ {
+ pszCodeset += 3;
+
+ /* Treat the dash as optional: */
+ if (*pszCodeset == '-')
+ pszCodeset++;
+
+ /* Match '8': */
+ if (*pszCodeset == '8')
+ {
+ do
+ pszCodeset++;
+ while (RT_C_IS_SPACE(*pszCodeset));
+
+ /* We ignore modifiers here (e.g. "[be_BY.]utf8@latin"). */
+ if (!*pszCodeset || *pszCodeset == '@')
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+
+#ifdef RT_WITH_ICONV_CACHE
+
+/**
+ * Initializes the iconv handle cache associated with a thread.
+ *
+ * @param pThread The thread in question.
+ */
+DECLHIDDEN(void) rtStrIconvCacheInit(PRTTHREADINT pThread)
+{
+ for (size_t i = 0; i < RT_ELEMENTS(pThread->ahIconvs); i++)
+ pThread->ahIconvs[i] = (iconv_t)-1;
+}
+
+/**
+ * Destroys the iconv handle cache associated with a thread.
+ *
+ * @param pThread The thread in question.
+ */
+DECLHIDDEN(void) rtStrIconvCacheDestroy(PRTTHREADINT pThread)
+{
+ for (size_t i = 0; i < RT_ELEMENTS(pThread->ahIconvs); i++)
+ {
+ iconv_t hIconv = (iconv_t)pThread->ahIconvs[i];
+ pThread->ahIconvs[i] = (iconv_t)-1;
+ if (hIconv != (iconv_t)-1)
+ iconv_close(hIconv);
+ }
+}
+
+
+/**
+ * Converts a string from one charset to another.
+ *
+ * @returns iprt status code.
+ * @param pvInput Pointer to intput string.
+ * @param cbInput Size (in bytes) of input string. Excludes any terminators.
+ * @param pszInputCS Codeset of the input string.
+ * @param ppvOutput Pointer to pointer to output buffer if cbOutput > 0.
+ * If cbOutput is 0 this is where the pointer to the allocated
+ * buffer is stored.
+ * @param cbOutput Size of the passed in buffer.
+ * @param pszOutputCS Codeset of the input string.
+ * @param cFactor Input vs. output size factor.
+ * @param phIconv Pointer to the cache entry.
+ */
+static int rtstrConvertCached(const void *pvInput, size_t cbInput, const char *pszInputCS,
+ void **ppvOutput, size_t cbOutput, const char *pszOutputCS,
+ unsigned cFactor, iconv_t *phIconv)
+{
+ /*
+ * Allocate buffer
+ */
+ bool fUcs2Term;
+ void *pvOutput;
+ size_t cbOutput2;
+ if (!cbOutput)
+ {
+ cbOutput2 = cbInput * cFactor;
+ pvOutput = RTMemTmpAlloc(cbOutput2 + sizeof(RTUTF16));
+ if (!pvOutput)
+ return VERR_NO_TMP_MEMORY;
+ fUcs2Term = true;
+ }
+ else
+ {
+ pvOutput = *ppvOutput;
+ fUcs2Term = !strcmp(pszOutputCS, "UCS-2")
+ || !strcmp(pszOutputCS, "UTF-16")
+ || !strcmp(pszOutputCS, "ucs-2")
+ || !strcmp(pszOutputCS, "utf-16");
+ cbOutput2 = cbOutput - (fUcs2Term ? sizeof(RTUTF16) : 1);
+ if (cbOutput2 > cbOutput)
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ /*
+ * Use a loop here to retry with bigger buffers.
+ */
+ for (unsigned cTries = 10; cTries > 0; cTries--)
+ {
+ /*
+ * Create conversion object if necessary.
+ */
+ iconv_t hIconv = (iconv_t)*phIconv;
+ if (hIconv == (iconv_t)-1)
+ {
+#if defined(RT_OS_SOLARIS) || defined(RT_OS_NETBSD) || /* @bugref{10153}: Default to UTF-8: */ defined(RT_OS_DARWIN)
+ /* Some systems don't grok empty codeset strings, so help them find the current codeset. */
+ if (!*pszInputCS)
+ pszInputCS = rtStrGetLocaleCodeset();
+ if (!*pszOutputCS)
+ pszOutputCS = rtStrGetLocaleCodeset();
+#endif
+ IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc causes trouble */
+ *phIconv = hIconv = iconv_open(pszOutputCS, pszInputCS);
+ IPRT_ALIGNMENT_CHECKS_ENABLE();
+ }
+ if (hIconv != (iconv_t)-1)
+ {
+ /*
+ * Do the conversion.
+ */
+ size_t cbInLeft = cbInput;
+ size_t cbOutLeft = cbOutput2;
+ const void *pvInputLeft = pvInput;
+ void *pvOutputLeft = pvOutput;
+ size_t cchNonRev;
+#ifdef NON_CONST_ICONV_INPUT
+ cchNonRev = iconv(hIconv, (char **)&pvInputLeft, &cbInLeft, (char **)&pvOutputLeft, &cbOutLeft);
+#else
+ cchNonRev = iconv(hIconv, (const char **)&pvInputLeft, &cbInLeft, (char **)&pvOutputLeft, &cbOutLeft);
+#endif
+ if (cchNonRev != (size_t)-1)
+ {
+ if (!cbInLeft)
+ {
+ /*
+ * We're done, just add the terminator and return.
+ * (Two terminators to support UCS-2 output, too.)
+ */
+ ((char *)pvOutputLeft)[0] = '\0';
+ if (fUcs2Term)
+ ((char *)pvOutputLeft)[1] = '\0';
+ *ppvOutput = pvOutput;
+ if (cchNonRev == 0)
+ return VINF_SUCCESS;
+ return VWRN_NO_TRANSLATION;
+ }
+ errno = E2BIG;
+ }
+
+ /*
+ * If we failed because of output buffer space we'll
+ * increase the output buffer size and retry.
+ */
+ if (errno == E2BIG)
+ {
+ if (!cbOutput)
+ {
+ RTMemTmpFree(pvOutput);
+ cbOutput2 *= 2;
+ pvOutput = RTMemTmpAlloc(cbOutput2 + sizeof(RTUTF16));
+ if (!pvOutput)
+ return VERR_NO_TMP_MEMORY;
+ continue;
+ }
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ /*
+ * Close the handle on all other errors to make sure we won't carry
+ * any bad state with us.
+ */
+ *phIconv = (iconv_t)-1;
+ iconv_close(hIconv);
+ }
+ break;
+ }
+
+ /* failure */
+ if (!cbOutput)
+ RTMemTmpFree(pvOutput);
+ return VERR_NO_TRANSLATION;
+}
+
+#endif /* RT_WITH_ICONV_CACHE */
+
+/**
+ * Converts a string from one charset to another without using the handle cache.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pvInput Pointer to intput string.
+ * @param cbInput Size (in bytes) of input string. Excludes any terminators.
+ * @param pszInputCS Codeset of the input string.
+ * @param ppvOutput Pointer to pointer to output buffer if cbOutput > 0.
+ * If cbOutput is 0 this is where the pointer to the allocated
+ * buffer is stored.
+ * @param cbOutput Size of the passed in buffer.
+ * @param pszOutputCS Codeset of the input string.
+ * @param cFactor Input vs. output size factor.
+ */
+static int rtStrConvertUncached(const void *pvInput, size_t cbInput, const char *pszInputCS,
+ void **ppvOutput, size_t cbOutput, const char *pszOutputCS,
+ unsigned cFactor)
+{
+ /*
+ * Allocate buffer
+ */
+ bool fUcs2Term;
+ void *pvOutput;
+ size_t cbOutput2;
+ if (!cbOutput)
+ {
+ cbOutput2 = cbInput * cFactor;
+ pvOutput = RTMemTmpAlloc(cbOutput2 + sizeof(RTUTF16));
+ if (!pvOutput)
+ return VERR_NO_TMP_MEMORY;
+ fUcs2Term = true;
+ }
+ else
+ {
+ pvOutput = *ppvOutput;
+ fUcs2Term = !strcmp(pszOutputCS, "UCS-2");
+ cbOutput2 = cbOutput - (fUcs2Term ? sizeof(RTUTF16) : 1);
+ if (cbOutput2 > cbOutput)
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ /*
+ * Use a loop here to retry with bigger buffers.
+ */
+ for (unsigned cTries = 10; cTries > 0; cTries--)
+ {
+ /*
+ * Create conversion object.
+ */
+#if defined(RT_OS_SOLARIS) || defined(RT_OS_NETBSD) || /* @bugref{10153}: Default to UTF-8: */ defined(RT_OS_DARWIN)
+ /* Some systems don't grok empty codeset strings, so help them find the current codeset. */
+ if (!*pszInputCS)
+ pszInputCS = rtStrGetLocaleCodeset();
+ if (!*pszOutputCS)
+ pszOutputCS = rtStrGetLocaleCodeset();
+#endif
+ IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc causes trouble */
+ iconv_t icHandle = iconv_open(pszOutputCS, pszInputCS);
+ IPRT_ALIGNMENT_CHECKS_ENABLE();
+ if (icHandle != (iconv_t)-1)
+ {
+ /*
+ * Do the conversion.
+ */
+ size_t cbInLeft = cbInput;
+ size_t cbOutLeft = cbOutput2;
+ const void *pvInputLeft = pvInput;
+ void *pvOutputLeft = pvOutput;
+ size_t cchNonRev;
+#ifdef NON_CONST_ICONV_INPUT
+ cchNonRev = iconv(icHandle, (char **)&pvInputLeft, &cbInLeft, (char **)&pvOutputLeft, &cbOutLeft);
+#else
+ cchNonRev = iconv(icHandle, (const char **)&pvInputLeft, &cbInLeft, (char **)&pvOutputLeft, &cbOutLeft);
+#endif
+ if (cchNonRev != (size_t)-1)
+ {
+ if (!cbInLeft)
+ {
+ /*
+ * We're done, just add the terminator and return.
+ * (Two terminators to support UCS-2 output, too.)
+ */
+ iconv_close(icHandle);
+ ((char *)pvOutputLeft)[0] = '\0';
+ if (fUcs2Term)
+ ((char *)pvOutputLeft)[1] = '\0';
+ *ppvOutput = pvOutput;
+ if (cchNonRev == 0)
+ return VINF_SUCCESS;
+ return VWRN_NO_TRANSLATION;
+ }
+ errno = E2BIG;
+ }
+ iconv_close(icHandle);
+
+ /*
+ * If we failed because of output buffer space we'll
+ * increase the output buffer size and retry.
+ */
+ if (errno == E2BIG)
+ {
+ if (!cbOutput)
+ {
+ RTMemTmpFree(pvOutput);
+ cbOutput2 *= 2;
+ pvOutput = RTMemTmpAlloc(cbOutput2 + sizeof(RTUTF16));
+ if (!pvOutput)
+ return VERR_NO_TMP_MEMORY;
+ continue;
+ }
+ return VERR_BUFFER_OVERFLOW;
+ }
+ }
+ break;
+ }
+
+ /* failure */
+ if (!cbOutput)
+ RTMemTmpFree(pvOutput);
+ return VERR_NO_TRANSLATION;
+}
+
+
+/**
+ * Wrapper that selects rtStrConvertCached or rtStrConvertUncached.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pszInput Pointer to intput string.
+ * @param cchInput Size (in bytes) of input string. Excludes any
+ * terminators.
+ * @param pszInputCS Codeset of the input string.
+ * @param ppszOutput Pointer to pointer to output buffer if cbOutput > 0.
+ * If cbOutput is 0 this is where the pointer to the
+ * allocated buffer is stored.
+ * @param cbOutput Size of the passed in buffer.
+ * @param pszOutputCS Codeset of the input string.
+ * @param cFactor Input vs. output size factor.
+ * @param enmCacheIdx The iconv cache index.
+ */
+DECLINLINE(int) rtStrConvertWrapper(const char *pchInput, size_t cchInput, const char *pszInputCS,
+ char **ppszOutput, size_t cbOutput, const char *pszOutputCS,
+ unsigned cFactor, RTSTRICONV enmCacheIdx)
+{
+#ifdef RT_WITH_ICONV_CACHE
+ RTTHREAD hSelf = RTThreadSelf();
+ if (hSelf != NIL_RTTHREAD)
+ {
+ PRTTHREADINT pThread = rtThreadGet(hSelf);
+ if (pThread)
+ {
+ if ((pThread->fIntFlags & (RTTHREADINT_FLAGS_ALIEN | RTTHREADINT_FLAGS_MAIN)) != RTTHREADINT_FLAGS_ALIEN)
+ {
+ int rc = rtstrConvertCached(pchInput, cchInput, pszInputCS,
+ (void **)ppszOutput, cbOutput, pszOutputCS,
+ cFactor, (iconv_t *)&pThread->ahIconvs[enmCacheIdx]);
+ rtThreadRelease(pThread);
+ return rc;
+ }
+ rtThreadRelease(pThread);
+ }
+ }
+#endif
+ return rtStrConvertUncached(pchInput, cchInput, pszInputCS,
+ (void **)ppszOutput, cbOutput, pszOutputCS,
+ cFactor);
+}
+
+
+/**
+ * Internal API for use by the path conversion code.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pszInput Pointer to intput string.
+ * @param cchInput Size (in bytes) of input string. Excludes any
+ * terminators.
+ * @param pszInputCS Codeset of the input string.
+ * @param ppszOutput Pointer to pointer to output buffer if cbOutput > 0.
+ * If cbOutput is 0 this is where the pointer to the
+ * allocated buffer is stored.
+ * @param cbOutput Size of the passed in buffer.
+ * @param pszOutputCS Codeset of the input string.
+ * @param cFactor Input vs. output size factor.
+ * @param enmCacheIdx The iconv cache index.
+ */
+DECLHIDDEN(int) rtStrConvert(const char *pchInput, size_t cchInput, const char *pszInputCS,
+ char **ppszOutput, size_t cbOutput, const char *pszOutputCS,
+ unsigned cFactor, RTSTRICONV enmCacheIdx)
+{
+ Assert(enmCacheIdx >= 0 && enmCacheIdx < RTSTRICONV_END);
+ return rtStrConvertWrapper(pchInput, cchInput, pszInputCS,
+ ppszOutput, cbOutput, pszOutputCS,
+ cFactor, enmCacheIdx);
+}
+
+
+/**
+ * Initializes a local conversion cache for use with rtStrLocalCacheConvert.
+ *
+ * Call rtStrLocalCacheDelete when done.
+ */
+DECLHIDDEN(void) rtStrLocalCacheInit(void **ppvTmpCache)
+{
+ *ppvTmpCache = (iconv_t)-1;
+}
+
+
+/**
+ * Cleans up a local conversion cache.
+ */
+DECLHIDDEN(void) rtStrLocalCacheDelete(void **ppvTmpCache)
+{
+#ifdef RT_WITH_ICONV_CACHE
+ iconv_t icHandle = (iconv_t)*ppvTmpCache;
+ if (icHandle != (iconv_t)-1)
+ iconv_close(icHandle);
+#endif
+ *ppvTmpCache = (iconv_t)-1;
+}
+
+
+/**
+ * Internal API for use by the process creation conversion code.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pszInput Pointer to intput string.
+ * @param cchInput Size (in bytes) of input string. Excludes any
+ * terminators.
+ * @param pszInputCS Codeset of the input string.
+ * @param ppszOutput Pointer to pointer to output buffer if cbOutput > 0.
+ * If cbOutput is 0 this is where the pointer to the
+ * allocated buffer is stored.
+ * @param cbOutput Size of the passed in buffer.
+ * @param pszOutputCS Codeset of the input string.
+ * @param ppvTmpCache Pointer to local temporary cache. Must be
+ * initialized by calling rtStrLocalCacheInit and
+ * cleaned up afterwards by rtStrLocalCacheDelete.
+ * Optional.
+ */
+DECLHIDDEN(int) rtStrLocalCacheConvert(const char *pchInput, size_t cchInput, const char *pszInputCS,
+ char **ppszOutput, size_t cbOutput, const char *pszOutputCS,
+ void **ppvTmpCache)
+{
+#ifdef RT_WITH_ICONV_CACHE
+ if (ppvTmpCache)
+ return rtstrConvertCached(pchInput, cchInput, pszInputCS, (void **)ppszOutput, cbOutput, pszOutputCS,
+ 1 /*cFactor*/, (iconv_t *)ppvTmpCache);
+#else
+ RT_NOREF(ppvTmpCache);
+#endif
+
+ return rtStrConvertUncached(pchInput, cchInput, pszInputCS, (void **)ppszOutput, cbOutput, pszOutputCS, 1 /*cFactor*/);
+}
+
+
+RTR3DECL(int) RTStrUtf8ToCurrentCPTag(char **ppszString, const char *pszString, const char *pszTag)
+{
+ Assert(ppszString);
+ Assert(pszString);
+ *ppszString = NULL;
+
+ /*
+ * Assume result string length is not longer than UTF-8 string.
+ */
+ size_t cch = strlen(pszString);
+ if (cch <= 0)
+ {
+ /* zero length string passed. */
+ *ppszString = (char *)RTMemTmpAllocZTag(sizeof(char), pszTag);
+ if (*ppszString)
+ return VINF_SUCCESS;
+ return VERR_NO_TMP_MEMORY;
+ }
+ return rtStrConvertWrapper(pszString, cch, "UTF-8", ppszString, 0, "", 1, RTSTRICONV_UTF8_TO_LOCALE);
+}
+
+
+RTR3DECL(int) RTStrUtf8ToCurrentCPExTag(char **ppszString, const char *pszString, size_t cchString, const char *pszTag)
+{
+ Assert(ppszString);
+ Assert(pszString);
+ *ppszString = NULL;
+
+ /*
+ * Assume result string length is not longer than UTF-8 string.
+ */
+ cchString = RTStrNLen(pszString, cchString);
+ if (cchString < 1)
+ {
+ /* zero length string passed. */
+ *ppszString = (char *)RTMemTmpAllocZTag(sizeof(char), pszTag);
+ if (*ppszString)
+ return VINF_SUCCESS;
+ return VERR_NO_TMP_MEMORY;
+ }
+ return rtStrConvertWrapper(pszString, cchString, "UTF-8", ppszString, 0, "", 1, RTSTRICONV_UTF8_TO_LOCALE);
+}
+
+
+RTR3DECL(int) RTStrCurrentCPToUtf8Tag(char **ppszString, const char *pszString, const char *pszTag)
+{
+ Assert(ppszString);
+ Assert(pszString);
+ *ppszString = NULL;
+
+ /*
+ * Attempt with UTF-8 length of 2x the native length.
+ */
+ size_t cch = strlen(pszString);
+ if (cch <= 0)
+ {
+ /* zero length string passed. */
+ *ppszString = (char *)RTMemTmpAllocZTag(sizeof(char), pszTag);
+ if (*ppszString)
+ return VINF_SUCCESS;
+ return VERR_NO_TMP_MEMORY;
+ }
+ return rtStrConvertWrapper(pszString, cch, "", ppszString, 0, "UTF-8", 2, RTSTRICONV_LOCALE_TO_UTF8);
+}
+
+
+RTR3DECL(int) RTStrConsoleCPToUtf8Tag(char **ppszString, const char *pszString, const char *pszTag)
+{
+ return RTStrCurrentCPToUtf8Tag(ppszString, pszString, pszTag);
+}
diff --git a/src/VBox/Runtime/r3/process-data.cpp b/src/VBox/Runtime/r3/process-data.cpp
new file mode 100644
index 00000000..1255d781
--- /dev/null
+++ b/src/VBox/Runtime/r3/process-data.cpp
@@ -0,0 +1,67 @@
+/* $Id: process-data.cpp $ */
+/** @file
+ * IPRT - Process Data Ring-3.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include "internal/process.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * The process identifier of the running process.
+ */
+DECL_HIDDEN_DATA(RTPROCESS) g_ProcessSelf = NIL_RTPROCESS;
+
+/**
+ * The current process priority.
+ */
+DECL_HIDDEN_DATA(RTPROCPRIORITY) g_enmProcessPriority = RTPROCPRIORITY_DEFAULT;
+
+/** The process path.
+ * This is used by RTPathExecDir and RTProcGetExecutablePath and set by rtProcInitName. */
+DECL_HIDDEN_DATA(char) g_szrtProcExePath[RTPATH_MAX];
+/** The length of g_szrtProcExePath. */
+DECL_HIDDEN_DATA(size_t) g_cchrtProcExePath;
+/** The offset of the process name into g_szrtProcExePath. */
+DECL_HIDDEN_DATA(size_t) g_offrtProcName;
+/** The length of directory path component of g_szrtProcExePath. */
+DECL_HIDDEN_DATA(size_t) g_cchrtProcExeDir;
+
diff --git a/src/VBox/Runtime/r3/process.cpp b/src/VBox/Runtime/r3/process.cpp
new file mode 100644
index 00000000..f9d1ecf2
--- /dev/null
+++ b/src/VBox/Runtime/r3/process.cpp
@@ -0,0 +1,135 @@
+/* $Id: process.cpp $ */
+/** @file
+ * IPRT - Process, Common.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include "internal/process.h"
+#include "internal/thread.h"
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+#else
+# include <unistd.h>
+#endif
+
+
+/**
+ * Get the identifier for the current process.
+ *
+ * @returns Process identifier.
+ */
+RTDECL(RTPROCESS) RTProcSelf(void)
+{
+ RTPROCESS Self = g_ProcessSelf;
+ if (Self != NIL_RTPROCESS)
+ return Self;
+
+ /* lazy init. */
+#ifdef _MSC_VER
+ Self = GetCurrentProcessId(); /* since NT 3.1, not 3.51+ as listed on geoffchappell.com */
+#else
+ Self = getpid();
+#endif
+ g_ProcessSelf = Self;
+ return Self;
+}
+
+
+/**
+ * Attempts to alter the priority of the current process.
+ *
+ * @returns iprt status code.
+ * @param enmPriority The new priority.
+ */
+RTR3DECL(int) RTProcSetPriority(RTPROCPRIORITY enmPriority)
+{
+ if (enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST)
+ return rtThreadDoSetProcPriority(enmPriority);
+ AssertMsgFailed(("enmPriority=%d\n", enmPriority));
+ return VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * Gets the current priority of this process.
+ *
+ * @returns The priority (see RTPROCPRIORITY).
+ */
+RTR3DECL(RTPROCPRIORITY) RTProcGetPriority(void)
+{
+ return g_enmProcessPriority;
+}
+
+
+RTR3DECL(char *) RTProcGetExecutablePath(char *pszExecPath, size_t cbExecPath)
+{
+ if (RT_UNLIKELY(g_szrtProcExePath[0] == '\0'))
+ return NULL;
+
+ /*
+ * Calc the length and check if there is space before copying.
+ */
+ size_t cch = g_cchrtProcExePath;
+ if (cch < cbExecPath)
+ {
+ memcpy(pszExecPath, g_szrtProcExePath, cch);
+ pszExecPath[cch] = '\0';
+ return pszExecPath;
+ }
+
+ AssertMsgFailed(("Buffer too small (%zu <= %zu)\n", cbExecPath, cch));
+ return NULL;
+}
+
+
+RTR3DECL(const char *) RTProcExecutablePath(void)
+{
+ return g_szrtProcExePath;
+}
+
+
+RTR3DECL(const char *) RTProcShortName(void)
+{
+ return &g_szrtProcExePath[g_offrtProcName];
+}
+
diff --git a/src/VBox/Runtime/r3/socket.cpp b/src/VBox/Runtime/r3/socket.cpp
new file mode 100644
index 00000000..4acfe73e
--- /dev/null
+++ b/src/VBox/Runtime/r3/socket.cpp
@@ -0,0 +1,3186 @@
+/* $Id: socket.cpp $ */
+/** @file
+ * IPRT - Network Sockets.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/winsock2.h>
+# include <iprt/win/ws2tcpip.h>
+#else /* !RT_OS_WINDOWS */
+# include <errno.h>
+# include <sys/select.h>
+# include <sys/stat.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <netinet/tcp.h>
+# include <arpa/inet.h>
+# ifdef IPRT_WITH_TCPIP_V6
+# include <netinet6/in6.h>
+# endif
+# include <sys/un.h>
+# include <netdb.h>
+# include <unistd.h>
+# include <fcntl.h>
+# include <sys/uio.h>
+# ifndef AF_LOCAL
+# define AF_LOCAL AF_UNIX
+# endif
+#endif /* !RT_OS_WINDOWS */
+#include <limits.h>
+
+#include "internal/iprt.h"
+#include <iprt/socket.h>
+
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/mempool.h>
+#include <iprt/poll.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/mem.h>
+#include <iprt/sg.h>
+#include <iprt/log.h>
+
+#include "internal/magics.h"
+#include "internal/socket.h"
+#include "internal/string.h"
+#ifdef RT_OS_WINDOWS
+# include "win/internal-r3-win.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* non-standard linux stuff (it seems). */
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+#endif
+
+/* Windows has different names for SHUT_XXX. */
+#ifndef SHUT_RDWR
+# ifdef SD_BOTH
+# define SHUT_RDWR SD_BOTH
+# else
+# define SHUT_RDWR 2
+# endif
+#endif
+#ifndef SHUT_WR
+# ifdef SD_SEND
+# define SHUT_WR SD_SEND
+# else
+# define SHUT_WR 1
+# endif
+#endif
+#ifndef SHUT_RD
+# ifdef SD_RECEIVE
+# define SHUT_RD SD_RECEIVE
+# else
+# define SHUT_RD 0
+# endif
+#endif
+
+/* fixup backlevel OSes. */
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+# define socklen_t int
+#endif
+
+/** How many pending connection. */
+#define RTTCP_SERVER_BACKLOG 10
+
+/* Limit read and write sizes on Windows and OS/2. */
+#ifdef RT_OS_WINDOWS
+# define RTSOCKET_MAX_WRITE (INT_MAX / 2)
+# define RTSOCKET_MAX_READ (INT_MAX / 2)
+#elif defined(RT_OS_OS2)
+# define RTSOCKET_MAX_WRITE 0x10000
+# define RTSOCKET_MAX_READ 0x10000
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Socket handle data.
+ *
+ * This is mainly required for implementing RTPollSet on Windows.
+ */
+typedef struct RTSOCKETINT
+{
+ /** Magic number (RTSOCKET_MAGIC). */
+ uint32_t u32Magic;
+ /** Exclusive user count.
+ * This is used to prevent two threads from accessing the handle concurrently.
+ * It can be higher than 1 if this handle is reference multiple times in a
+ * polling set (Windows). */
+ uint32_t volatile cUsers;
+ /** The native socket handle. */
+ RTSOCKETNATIVE hNative;
+ /** Indicates whether the handle has been closed or not. */
+ bool volatile fClosed;
+ /** Indicates whether the socket is operating in blocking or non-blocking mode
+ * currently. */
+ bool fBlocking;
+ /** Whether to leave the native socket open rather than closing it (for
+ * RTHandleGetStandard). */
+ bool fLeaveOpen;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ /** The pollset currently polling this socket. This is NIL if no one is
+ * polling. */
+ RTPOLLSET hPollSet;
+#endif
+#ifdef RT_OS_WINDOWS
+ /** The event semaphore we've associated with the socket handle.
+ * This is WSA_INVALID_EVENT if not done. */
+ WSAEVENT hEvent;
+ /** The events we're polling for. */
+ uint32_t fPollEvts;
+ /** The events we're currently subscribing to with WSAEventSelect.
+ * This is ZERO if we're currently not subscribing to anything. */
+ uint32_t fSubscribedEvts;
+ /** Saved events which are only posted once and events harvested for
+ * sockets entered multiple times into to a poll set. Imagine a scenario where
+ * you have a RTPOLL_EVT_READ entry and RTPOLL_EVT_ERROR entry. The READ
+ * condition can be triggered between checking the READ entry and the ERROR
+ * entry, and we don't want to drop the READ, so we store it here and make sure
+ * the event is signalled.
+ *
+ * The RTPOLL_EVT_ERROR is inconsistenly sticky at the momemnt... */
+ uint32_t fEventsSaved;
+ /** Set if fEventsSaved contains harvested events (used to avoid multiple
+ * calls to rtSocketPollCheck on the same socket during rtSocketPollDone). */
+ bool fHarvestedEvents;
+ /** Set if we're using the polling fallback. */
+ bool fPollFallback;
+ /** Set if the fallback polling is active (event not set). */
+ bool volatile fPollFallbackActive;
+ /** Set to shut down the fallback polling thread. */
+ bool volatile fPollFallbackShutdown;
+ /** Socket use to wake up the select thread. */
+ RTSOCKETNATIVE hPollFallbackNotifyW;
+ /** Socket the select thread always waits on. */
+ RTSOCKETNATIVE hPollFallbackNotifyR;
+ /** The fallback polling thread. */
+ RTTHREAD hPollFallbackThread;
+#endif /* RT_OS_WINDOWS */
+} RTSOCKETINT;
+
+
+/**
+ * Address union used internally for things like getpeername and getsockname.
+ */
+typedef union RTSOCKADDRUNION
+{
+ struct sockaddr Addr;
+ struct sockaddr_in IPv4;
+#ifdef IPRT_WITH_TCPIP_V6
+ struct sockaddr_in6 IPv6;
+#endif
+} RTSOCKADDRUNION;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+/** Indicates that we've successfully initialized winsock. */
+static uint32_t volatile g_uWinSockInitedVersion = 0;
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+static void rtSocketPokePollFallbackThread(RTSOCKETINT *pThis);
+#endif
+
+
+
+#ifdef RT_OS_WINDOWS
+/**
+ * Initializes winsock for the process.
+ *
+ * @returns IPRT status code.
+ */
+static int rtSocketInitWinsock(void)
+{
+ if (g_uWinSockInitedVersion != 0)
+ return VINF_SUCCESS;
+
+ if ( !g_pfnWSAGetLastError
+ || !g_pfnWSAStartup
+ || !g_pfnsocket
+ || !g_pfnclosesocket)
+ return VERR_NET_INIT_FAILED;
+
+ /*
+ * Initialize winsock. Try with 2.2 and back down till we get something that works.
+ */
+ static const WORD s_awVersions[] =
+ {
+ MAKEWORD(2, 2),
+ MAKEWORD(2, 1),
+ MAKEWORD(2, 0),
+ MAKEWORD(1, 1),
+ MAKEWORD(1, 0),
+ };
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_awVersions); i++)
+ {
+ WSADATA wsaData;
+ RT_ZERO(wsaData);
+ int rcWsa = g_pfnWSAStartup(s_awVersions[i], &wsaData);
+ if (rcWsa == 0)
+ {
+ /* AssertMsg(wsaData.wVersion >= s_awVersions[i]); - triggers with winsock 1.1 */
+ ASMAtomicWriteU32(&g_uWinSockInitedVersion, wsaData.wVersion);
+ return VINF_SUCCESS;
+ }
+ AssertLogRelMsg(rcWsa == WSAVERNOTSUPPORTED, ("rcWsa=%d (winsock version %#x)\n", rcWsa, s_awVersions[i]));
+ }
+ LogRel(("Failed to init winsock!\n"));
+ return VERR_NET_INIT_FAILED;
+}
+#endif
+
+
+/**
+ * Get the last error as an iprt status code.
+ *
+ * @returns IPRT status code.
+ */
+DECLINLINE(int) rtSocketError(void)
+{
+#ifdef RT_OS_WINDOWS
+ if (g_pfnWSAGetLastError)
+ return RTErrConvertFromWin32(g_pfnWSAGetLastError());
+ return VERR_NET_IO_ERROR;
+#else
+ return RTErrConvertFromErrno(errno);
+#endif
+}
+
+
+/**
+ * Resets the last error.
+ */
+DECLINLINE(void) rtSocketErrorReset(void)
+{
+#ifdef RT_OS_WINDOWS
+ if (g_pfnWSASetLastError)
+ g_pfnWSASetLastError(0);
+#else
+ errno = 0;
+#endif
+}
+
+
+/**
+ * Get the last resolver error as an iprt status code.
+ *
+ * @returns iprt status code.
+ */
+DECLHIDDEN(int) rtSocketResolverError(void)
+{
+#ifdef RT_OS_WINDOWS
+ if (g_pfnWSAGetLastError)
+ return RTErrConvertFromWin32(g_pfnWSAGetLastError());
+ return VERR_UNRESOLVED_ERROR;
+#else
+ switch (h_errno)
+ {
+ case HOST_NOT_FOUND:
+ return VERR_NET_HOST_NOT_FOUND;
+ case NO_DATA:
+ return VERR_NET_ADDRESS_NOT_AVAILABLE;
+ case NO_RECOVERY:
+ return VERR_IO_GEN_FAILURE;
+ case TRY_AGAIN:
+ return VERR_TRY_AGAIN;
+
+ default:
+ AssertLogRelMsgFailed(("Unhandled error %u\n", h_errno));
+ return VERR_UNRESOLVED_ERROR;
+ }
+#endif
+}
+
+
+/**
+ * Converts from a native socket address to a generic IPRT network address.
+ *
+ * @returns IPRT status code.
+ * @param pSrc The source address.
+ * @param cbSrc The size of the source address.
+ * @param pAddr Where to return the generic IPRT network
+ * address.
+ */
+static int rtSocketNetAddrFromAddr(RTSOCKADDRUNION const *pSrc, size_t cbSrc, PRTNETADDR pAddr)
+{
+ /*
+ * Convert the address.
+ */
+ if ( cbSrc == sizeof(struct sockaddr_in)
+ && pSrc->Addr.sa_family == AF_INET)
+ {
+ RT_ZERO(*pAddr);
+ pAddr->enmType = RTNETADDRTYPE_IPV4;
+ pAddr->uPort = RT_N2H_U16(pSrc->IPv4.sin_port);
+ pAddr->uAddr.IPv4.u = pSrc->IPv4.sin_addr.s_addr;
+ }
+#ifdef IPRT_WITH_TCPIP_V6
+ else if ( cbSrc == sizeof(struct sockaddr_in6)
+ && pSrc->Addr.sa_family == AF_INET6)
+ {
+ RT_ZERO(*pAddr);
+ pAddr->enmType = RTNETADDRTYPE_IPV6;
+ pAddr->uPort = RT_N2H_U16(pSrc->IPv6.sin6_port);
+ pAddr->uAddr.IPv6.au32[0] = pSrc->IPv6.sin6_addr.s6_addr32[0];
+ pAddr->uAddr.IPv6.au32[1] = pSrc->IPv6.sin6_addr.s6_addr32[1];
+ pAddr->uAddr.IPv6.au32[2] = pSrc->IPv6.sin6_addr.s6_addr32[2];
+ pAddr->uAddr.IPv6.au32[3] = pSrc->IPv6.sin6_addr.s6_addr32[3];
+ }
+#endif
+ else
+ return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Converts from a generic IPRT network address to a native socket address.
+ *
+ * @returns IPRT status code.
+ * @param pAddr Pointer to the generic IPRT network address.
+ * @param pDst The source address.
+ * @param cbDst The size of the source address.
+ * @param pcbAddr Where to store the size of the returned address.
+ * Optional
+ */
+static int rtSocketAddrFromNetAddr(PCRTNETADDR pAddr, RTSOCKADDRUNION *pDst, size_t cbDst, int *pcbAddr)
+{
+ RT_BZERO(pDst, cbDst);
+ if (pAddr->enmType == RTNETADDRTYPE_IPV4)
+ {
+ if (cbDst < sizeof(struct sockaddr_in))
+ return VERR_BUFFER_OVERFLOW;
+
+ pDst->Addr.sa_family = AF_INET;
+ pDst->IPv4.sin_port = RT_H2N_U16(pAddr->uPort);
+ pDst->IPv4.sin_addr.s_addr = pAddr->uAddr.IPv4.u;
+ if (pcbAddr)
+ *pcbAddr = sizeof(pDst->IPv4);
+ }
+#ifdef IPRT_WITH_TCPIP_V6
+ else if (pAddr->enmType == RTNETADDRTYPE_IPV6)
+ {
+ if (cbDst < sizeof(struct sockaddr_in6))
+ return VERR_BUFFER_OVERFLOW;
+
+ pDst->Addr.sa_family = AF_INET6;
+ pDst->IPv6.sin6_port = RT_H2N_U16(pAddr->uPort);
+ pSrc->IPv6.sin6_addr.s6_addr32[0] = pAddr->uAddr.IPv6.au32[0];
+ pSrc->IPv6.sin6_addr.s6_addr32[1] = pAddr->uAddr.IPv6.au32[1];
+ pSrc->IPv6.sin6_addr.s6_addr32[2] = pAddr->uAddr.IPv6.au32[2];
+ pSrc->IPv6.sin6_addr.s6_addr32[3] = pAddr->uAddr.IPv6.au32[3];
+ if (pcbAddr)
+ *pcbAddr = sizeof(pDst->IPv6);
+ }
+#endif
+ else
+ return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Tries to lock the socket for exclusive usage by the calling thread.
+ *
+ * Call rtSocketUnlock() to unlock.
+ *
+ * @returns @c true if locked, @c false if not.
+ * @param pThis The socket structure.
+ */
+DECLINLINE(bool) rtSocketTryLock(RTSOCKETINT *pThis)
+{
+ return ASMAtomicCmpXchgU32(&pThis->cUsers, 1, 0);
+}
+
+
+/**
+ * Unlocks the socket.
+ *
+ * @param pThis The socket structure.
+ */
+DECLINLINE(void) rtSocketUnlock(RTSOCKETINT *pThis)
+{
+ ASMAtomicCmpXchgU32(&pThis->cUsers, 0, 1);
+}
+
+
+/**
+ * The slow path of rtSocketSwitchBlockingMode that does the actual switching.
+ *
+ * @returns IPRT status code.
+ * @param pThis The socket structure.
+ * @param fBlocking The desired mode of operation.
+ * @remarks Do not call directly.
+ */
+static int rtSocketSwitchBlockingModeSlow(RTSOCKETINT *pThis, bool fBlocking)
+{
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnioctlsocket, VERR_NET_NOT_UNSUPPORTED);
+ u_long uBlocking = fBlocking ? 0 : 1;
+ if (g_pfnioctlsocket(pThis->hNative, FIONBIO, &uBlocking))
+ return rtSocketError();
+
+#else
+ int fFlags = fcntl(pThis->hNative, F_GETFL, 0);
+ if (fFlags == -1)
+ return rtSocketError();
+
+ if (fBlocking)
+ fFlags &= ~O_NONBLOCK;
+ else
+ fFlags |= O_NONBLOCK;
+ if (fcntl(pThis->hNative, F_SETFL, fFlags) == -1)
+ return rtSocketError();
+#endif
+
+ pThis->fBlocking = fBlocking;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Switches the socket to the desired blocking mode if necessary.
+ *
+ * The socket must be locked.
+ *
+ * @returns IPRT status code.
+ * @param pThis The socket structure.
+ * @param fBlocking The desired mode of operation.
+ */
+DECLINLINE(int) rtSocketSwitchBlockingMode(RTSOCKETINT *pThis, bool fBlocking)
+{
+ if (pThis->fBlocking != fBlocking)
+ return rtSocketSwitchBlockingModeSlow(pThis, fBlocking);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Creates an IPRT socket handle for a native one.
+ *
+ * @returns IPRT status code.
+ * @param ppSocket Where to return the IPRT socket handle.
+ * @param hNative The native handle.
+ * @param fLeaveOpen Whether to leave the native socket handle open when
+ * closed.
+ */
+DECLHIDDEN(int) rtSocketCreateForNative(RTSOCKETINT **ppSocket, RTSOCKETNATIVE hNative, bool fLeaveOpen)
+{
+ RTSOCKETINT *pThis = (RTSOCKETINT *)RTMemPoolAlloc(RTMEMPOOL_DEFAULT, sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ pThis->u32Magic = RTSOCKET_MAGIC;
+ pThis->cUsers = 0;
+ pThis->hNative = hNative;
+ pThis->fClosed = false;
+ pThis->fLeaveOpen = fLeaveOpen;
+ pThis->fBlocking = true;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ pThis->hPollSet = NIL_RTPOLLSET;
+#endif
+#ifdef RT_OS_WINDOWS
+ pThis->hEvent = WSA_INVALID_EVENT;
+ pThis->fPollEvts = 0;
+ pThis->fSubscribedEvts = 0;
+ pThis->fEventsSaved = 0;
+ pThis->fHarvestedEvents = false;
+ pThis->fPollFallback = g_uWinSockInitedVersion < MAKEWORD(2, 0)
+ || g_pfnWSACreateEvent == NULL
+ || g_pfnWSACloseEvent == NULL
+ || g_pfnWSAEventSelect == NULL
+ || g_pfnWSAEnumNetworkEvents == NULL;
+ pThis->fPollFallbackActive = false;
+ pThis->fPollFallbackShutdown = false;
+ pThis->hPollFallbackNotifyR = NIL_RTSOCKETNATIVE;
+ pThis->hPollFallbackNotifyW = NIL_RTSOCKETNATIVE;
+ pThis->hPollFallbackThread = NIL_RTTHREAD;
+#endif
+ *ppSocket = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSocketFromNative(PRTSOCKET phSocket, RTHCINTPTR uNative)
+{
+ AssertReturn(uNative != NIL_RTSOCKETNATIVE, VERR_INVALID_PARAMETER);
+#ifndef RT_OS_WINDOWS
+ AssertReturn(uNative >= 0, VERR_INVALID_PARAMETER);
+#endif
+ AssertPtrReturn(phSocket, VERR_INVALID_POINTER);
+ return rtSocketCreateForNative(phSocket, uNative, false /*fLeaveOpen*/);
+}
+
+
+/**
+ * Wrapper around socket().
+ *
+ * @returns IPRT status code.
+ * @param phSocket Where to store the handle to the socket on
+ * success.
+ * @param iDomain The protocol family (PF_XXX).
+ * @param iType The socket type (SOCK_XXX).
+ * @param iProtocol Socket parameter, usually 0.
+ * @param fInheritable Set to true if the socket should be inherted by
+ * child processes, false if not inheritable.
+ */
+DECLHIDDEN(int) rtSocketCreate(PRTSOCKET phSocket, int iDomain, int iType, int iProtocol, bool fInheritable)
+{
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnsocket, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfnclosesocket, VERR_NET_NOT_UNSUPPORTED);
+
+ /* Initialize WinSock. */
+ int rc2 = rtSocketInitWinsock();
+ if (RT_FAILURE(rc2))
+ return rc2;
+#endif
+
+ /*
+ * Create the socket.
+ *
+ * The RTSocketSetInheritance operation isn't necessarily reliable on windows,
+ * so try use WSA_FLAG_NO_HANDLE_INHERIT with WSASocketW when possible.
+ */
+#ifdef RT_OS_WINDOWS
+ bool fCallSetInheritance = true;
+ RTSOCKETNATIVE hNative;
+ if (g_pfnWSASocketW)
+ {
+ DWORD fWsaFlags = WSA_FLAG_OVERLAPPED | (!fInheritable ? WSA_FLAG_NO_HANDLE_INHERIT : 0);
+ hNative = g_pfnWSASocketW(iDomain, iType, iProtocol, NULL, 0 /*Group*/, fWsaFlags);
+ if (hNative != NIL_RTSOCKETNATIVE)
+ fCallSetInheritance = false;
+ else
+ {
+ if (!fInheritable)
+ hNative = g_pfnsocket(iDomain, iType, iProtocol);
+ if (hNative == NIL_RTSOCKETNATIVE)
+ return rtSocketError();
+ }
+ }
+ else
+ {
+ hNative = g_pfnsocket(iDomain, iType, iProtocol);
+ if (hNative == NIL_RTSOCKETNATIVE)
+ return rtSocketError();
+ }
+#else
+ RTSOCKETNATIVE hNative = socket(iDomain, iType, iProtocol);
+ if (hNative == NIL_RTSOCKETNATIVE)
+ return rtSocketError();
+#endif
+
+ /*
+ * Wrap it.
+ */
+ int rc = rtSocketCreateForNative(phSocket, hNative, false /*fLeaveOpen*/);
+ if (RT_SUCCESS(rc))
+ {
+#ifdef RT_OS_WINDOWS
+ if (fCallSetInheritance)
+#endif
+ RTSocketSetInheritance(*phSocket, fInheritable);
+ }
+ else
+ {
+#ifdef RT_OS_WINDOWS
+ g_pfnclosesocket(hNative);
+#else
+ close(hNative);
+#endif
+ }
+ return rc;
+}
+
+
+/**
+ * Wrapper around socketpair() for creating a local TCP connection.
+ *
+ * @returns IPRT status code.
+ * @param phServer Where to return the first native socket.
+ * @param phClient Where to return the second native socket.
+ */
+static int rtSocketCreateNativeTcpPair(RTSOCKETNATIVE *phServer, RTSOCKETNATIVE *phClient)
+{
+#ifdef RT_OS_WINDOWS
+ /*
+ * Initialize WinSock and make sure we got the necessary APIs.
+ */
+ int rc = rtSocketInitWinsock();
+ if (RT_FAILURE(rc))
+ return rc;
+ AssertReturn(g_pfnsocket, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfnclosesocket, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfnsetsockopt, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfnbind, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfngetsockname, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfnlisten, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfnaccept, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfnconnect, VERR_NET_NOT_UNSUPPORTED);
+
+ /*
+ * Create the "server" listen socket and the "client" socket.
+ */
+ RTSOCKETNATIVE hListener = g_pfnsocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (hListener == NIL_RTSOCKETNATIVE)
+ return rtSocketError();
+ RTSOCKETNATIVE hClient = g_pfnsocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (hClient != NIL_RTSOCKETNATIVE)
+ {
+
+ /*
+ * We let WinSock choose a port number when we bind.
+ */
+ union
+ {
+ struct sockaddr_in Ip;
+ struct sockaddr Generic;
+ } uAddr;
+ RT_ZERO(uAddr);
+ uAddr.Ip.sin_family = AF_INET;
+ uAddr.Ip.sin_addr.s_addr = RT_H2N_U32_C(INADDR_LOOPBACK);
+ //uAddr.Ip.sin_port = 0;
+ int fReuse = 1;
+ rc = g_pfnsetsockopt(hListener, SOL_SOCKET, SO_REUSEADDR, (const char *)&fReuse, sizeof(fReuse));
+ if (rc == 0)
+ {
+ rc = g_pfnbind(hListener, &uAddr.Generic, sizeof(uAddr.Ip));
+ if (rc == 0)
+ {
+ /*
+ * Get the address the client should connect to. According to the docs,
+ * we cannot assume that getsockname sets the IP and family.
+ */
+ RT_ZERO(uAddr);
+ int cbAddr = sizeof(uAddr.Ip);
+ rc = g_pfngetsockname(hListener, &uAddr.Generic, &cbAddr);
+ if (rc == 0)
+ {
+ uAddr.Ip.sin_family = AF_INET;
+ uAddr.Ip.sin_addr.s_addr = RT_H2N_U32_C(INADDR_LOOPBACK);
+
+ /*
+ * Listen, connect and accept.
+ */
+ rc = g_pfnlisten(hListener, 1 /*cBacklog*/);
+ if (rc == 0)
+ {
+ rc = g_pfnconnect(hClient, &uAddr.Generic, sizeof(uAddr.Ip));
+ if (rc == 0)
+ {
+ RTSOCKETNATIVE hServer = g_pfnaccept(hListener, NULL, NULL);
+ if (hServer != NIL_RTSOCKETNATIVE)
+ {
+ g_pfnclosesocket(hListener);
+
+ /*
+ * Done!
+ */
+ *phServer = hServer;
+ *phClient = hClient;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ }
+ }
+ }
+ rc = rtSocketError();
+ g_pfnclosesocket(hClient);
+ }
+ else
+ rc = rtSocketError();
+ g_pfnclosesocket(hListener);
+ return rc;
+
+#else
+ /*
+ * Got socket pair, so use it.
+ * Note! This isn't TCP per se, but it should fool the users.
+ */
+ int aSockets[2] = { -1, -1 };
+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, aSockets) == 0)
+ {
+ *phServer = aSockets[0];
+ *phClient = aSockets[1];
+ return VINF_SUCCESS;
+ }
+ return rtSocketError();
+#endif
+}
+
+
+/**
+ * Worker for RTTcpCreatePair.
+ *
+ * @returns IPRT status code.
+ * @param phServer Where to return the "server" side of the pair.
+ * @param phClient Where to return the "client" side of the pair.
+ * @note There is no server or client side, but we gotta call it something.
+ */
+DECLHIDDEN(int) rtSocketCreateTcpPair(RTSOCKET *phServer, RTSOCKET *phClient)
+{
+ RTSOCKETNATIVE hServer = NIL_RTSOCKETNATIVE;
+ RTSOCKETNATIVE hClient = NIL_RTSOCKETNATIVE;
+ int rc = rtSocketCreateNativeTcpPair(&hServer, &hClient);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtSocketCreateForNative(phServer, hServer, false /*fLeaveOpen*/);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtSocketCreateForNative(phClient, hClient, false /*fLeaveOpen*/);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ RTSocketRelease(*phServer);
+ }
+ else
+ {
+#ifdef RT_OS_WINDOWS
+ g_pfnclosesocket(hServer);
+#else
+ close(hServer);
+#endif
+ }
+#ifdef RT_OS_WINDOWS
+ g_pfnclosesocket(hClient);
+#else
+ close(hClient);
+#endif
+ }
+
+ *phServer = NIL_RTSOCKET;
+ *phClient = NIL_RTSOCKET;
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTSocketRetain(RTSOCKET hSocket)
+{
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, UINT32_MAX);
+ return RTMemPoolRetain(pThis);
+}
+
+
+/**
+ * Worker for RTSocketRelease and RTSocketClose.
+ *
+ * @returns IPRT status code.
+ * @param pThis The socket handle instance data.
+ * @param fDestroy Whether we're reaching ref count zero.
+ */
+static int rtSocketCloseIt(RTSOCKETINT *pThis, bool fDestroy)
+{
+ /*
+ * Invalidate the handle structure on destroy.
+ */
+ if (fDestroy)
+ {
+ Assert(ASMAtomicReadU32(&pThis->u32Magic) == RTSOCKET_MAGIC);
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSOCKET_MAGIC_DEAD);
+ }
+
+ int rc = VINF_SUCCESS;
+ if (ASMAtomicCmpXchgBool(&pThis->fClosed, true, false))
+ {
+#ifdef RT_OS_WINDOWS
+ /*
+ * Poke the polling thread if active and give it a small chance to stop.
+ */
+ if ( pThis->fPollFallback
+ && pThis->hPollFallbackThread != NIL_RTTHREAD)
+ {
+ ASMAtomicWriteBool(&pThis->fPollFallbackShutdown, true);
+ rtSocketPokePollFallbackThread(pThis);
+ int rc2 = RTThreadWait(pThis->hPollFallbackThread, RT_MS_1SEC, NULL);
+ if (RT_SUCCESS(rc2))
+ pThis->hPollFallbackThread = NIL_RTTHREAD;
+ }
+#endif
+
+ /*
+ * Close the native handle.
+ */
+ RTSOCKETNATIVE hNative = pThis->hNative;
+ if (hNative != NIL_RTSOCKETNATIVE)
+ {
+ pThis->hNative = NIL_RTSOCKETNATIVE;
+
+ if (!pThis->fLeaveOpen)
+ {
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnclosesocket, VERR_NET_NOT_UNSUPPORTED);
+ if (g_pfnclosesocket(hNative))
+#else
+ if (close(hNative))
+#endif
+ {
+ rc = rtSocketError();
+#ifdef RT_OS_WINDOWS
+ AssertMsgFailed(("closesocket(%p) -> %Rrc\n", (uintptr_t)hNative, rc));
+#else
+ AssertMsgFailed(("close(%d) -> %Rrc\n", hNative, rc));
+#endif
+ }
+ }
+ }
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * Windows specific polling cleanup.
+ */
+ WSAEVENT hEvent = pThis->hEvent;
+ if (hEvent != WSA_INVALID_EVENT)
+ {
+ pThis->hEvent = WSA_INVALID_EVENT;
+ if (!pThis->fPollFallback)
+ {
+ Assert(g_pfnWSACloseEvent);
+ if (g_pfnWSACloseEvent)
+ g_pfnWSACloseEvent(hEvent);
+ }
+ else
+ CloseHandle(hEvent);
+ }
+
+ if (pThis->fPollFallback)
+ {
+ if (pThis->hPollFallbackNotifyW != NIL_RTSOCKETNATIVE)
+ {
+ g_pfnclosesocket(pThis->hPollFallbackNotifyW);
+ pThis->hPollFallbackNotifyW = NIL_RTSOCKETNATIVE;
+ }
+
+ if (pThis->hPollFallbackThread != NIL_RTTHREAD)
+ {
+ int rc2 = RTThreadWait(pThis->hPollFallbackThread, RT_MS_1MIN / 2, NULL);
+ AssertRC(rc2);
+ pThis->hPollFallbackThread = NIL_RTTHREAD;
+ }
+
+ if (pThis->hPollFallbackNotifyR != NIL_RTSOCKETNATIVE)
+ {
+ g_pfnclosesocket(pThis->hPollFallbackNotifyR);
+ pThis->hPollFallbackNotifyR = NIL_RTSOCKETNATIVE;
+ }
+ }
+#endif
+ }
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTSocketRelease(RTSOCKET hSocket)
+{
+ RTSOCKETINT *pThis = hSocket;
+ if (pThis == NIL_RTSOCKET)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, UINT32_MAX);
+
+ /* get the refcount without killing it... */
+ uint32_t cRefs = RTMemPoolRefCount(pThis);
+ AssertReturn(cRefs != UINT32_MAX, UINT32_MAX);
+ if (cRefs == 1)
+ rtSocketCloseIt(pThis, true);
+
+ return RTMemPoolRelease(RTMEMPOOL_DEFAULT, pThis);
+}
+
+
+RTDECL(int) RTSocketClose(RTSOCKET hSocket)
+{
+ RTSOCKETINT *pThis = hSocket;
+ if (pThis == NIL_RTSOCKET)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+
+ uint32_t cRefs = RTMemPoolRefCount(pThis);
+ AssertReturn(cRefs != UINT32_MAX, UINT32_MAX);
+
+ int rc = rtSocketCloseIt(pThis, cRefs == 1);
+
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pThis);
+ return rc;
+}
+
+
+RTDECL(RTHCUINTPTR) RTSocketToNative(RTSOCKET hSocket)
+{
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, RTHCUINTPTR_MAX);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, RTHCUINTPTR_MAX);
+ return (RTHCUINTPTR)pThis->hNative;
+}
+
+
+RTDECL(int) RTSocketSetInheritance(RTSOCKET hSocket, bool fInheritable)
+{
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRefCount(pThis) >= (pThis->cUsers ? 2U : 1U), VERR_CALLER_NO_REFERENCE);
+
+#ifndef RT_OS_WINDOWS
+ if (fcntl(pThis->hNative, F_SETFD, fInheritable ? 0 : FD_CLOEXEC) < 0)
+ return RTErrConvertFromErrno(errno);
+ return VINF_SUCCESS;
+#else
+ /* Windows is more complicated as sockets are complicated wrt inheritance
+ (see stackoverflow for details). In general, though we cannot hope to
+ make a socket really non-inheritable before vista as other layers in
+ the winsock maze may have additional handles associated with the socket. */
+ if (g_pfnGetHandleInformation)
+ {
+ /* Check if the handle is already in what seems to be the right state
+ before we try doing anything. */
+ DWORD fFlags;
+ if (g_pfnGetHandleInformation((HANDLE)pThis->hNative, &fFlags))
+ {
+ if (RT_BOOL(fFlags & HANDLE_FLAG_INHERIT) == fInheritable)
+ return VINF_SUCCESS;
+ }
+ }
+
+ if (!g_pfnSetHandleInformation)
+ return VERR_NET_NOT_UNSUPPORTED;
+
+ if (!g_pfnSetHandleInformation((HANDLE)pThis->hNative, HANDLE_FLAG_INHERIT, fInheritable ? HANDLE_FLAG_INHERIT : 0))
+ return RTErrConvertFromWin32(GetLastError());
+ /** @todo Need we do something related to WS_SIO_ASSOCIATE_HANDLE or
+ * WS_SIO_TRANSLATE_HANDLE? Or what other handles could be associated
+ * with the socket? that we need to modify? */
+
+ return VINF_SUCCESS;
+#endif
+}
+
+
+static bool rtSocketIsIPv4Numerical(const char *pszAddress, PRTNETADDRIPV4 pAddr)
+{
+
+ /* Empty address resolves to the INADDR_ANY address (good for bind). */
+ if (!pszAddress || !*pszAddress)
+ {
+ pAddr->u = INADDR_ANY;
+ return true;
+ }
+
+ /* Four quads? */
+ char *psz = (char *)pszAddress;
+ for (int i = 0; i < 4; i++)
+ {
+ uint8_t u8;
+ int rc = RTStrToUInt8Ex(psz, &psz, 0, &u8);
+ if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
+ return false;
+ if (*psz != (i < 3 ? '.' : '\0'))
+ return false;
+ psz++;
+
+ pAddr->au8[i] = u8; /* big endian */
+ }
+
+ return true;
+}
+
+RTDECL(int) RTSocketParseInetAddress(const char *pszAddress, unsigned uPort, PRTNETADDR pAddr)
+{
+ int rc;
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(uPort > 0, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pszAddress, VERR_INVALID_POINTER);
+
+ /*
+ * Resolve the address. Pretty crude at the moment, but we have to make
+ * sure to not ask the NT 4 gethostbyname about an IPv4 address as it may
+ * give a wrong answer.
+ */
+ /** @todo this only supports IPv4, and IPv6 support needs to be added.
+ * It probably needs to be converted to getaddrinfo(). */
+ RTNETADDRIPV4 IPv4Quad;
+ if (rtSocketIsIPv4Numerical(pszAddress, &IPv4Quad))
+ {
+ Log3(("rtSocketIsIPv4Numerical: %s -> %#x (%RTnaipv4)\n", pszAddress, IPv4Quad.u, IPv4Quad));
+ RT_ZERO(*pAddr);
+ pAddr->enmType = RTNETADDRTYPE_IPV4;
+ pAddr->uPort = uPort;
+ pAddr->uAddr.IPv4 = IPv4Quad;
+ return VINF_SUCCESS;
+ }
+
+#ifdef RT_OS_WINDOWS
+ /* Initialize WinSock and check version before we call gethostbyname. */
+ if (!g_pfngethostbyname)
+ return VERR_NET_NOT_UNSUPPORTED;
+
+ int rc2 = rtSocketInitWinsock();
+ if (RT_FAILURE(rc2))
+ return rc2;
+
+# define gethostbyname g_pfngethostbyname
+#endif
+
+ struct hostent *pHostEnt;
+ pHostEnt = gethostbyname(pszAddress);
+ if (!pHostEnt)
+ {
+ rc = rtSocketResolverError();
+ AssertMsg(rc == VERR_NET_HOST_NOT_FOUND,
+ ("Could not resolve '%s', rc=%Rrc\n", pszAddress, rc));
+ return rc;
+ }
+
+ if (pHostEnt->h_addrtype == AF_INET)
+ {
+ RT_ZERO(*pAddr);
+ pAddr->enmType = RTNETADDRTYPE_IPV4;
+ pAddr->uPort = uPort;
+ pAddr->uAddr.IPv4.u = ((struct in_addr *)pHostEnt->h_addr)->s_addr;
+ Log3(("gethostbyname: %s -> %#x (%RTnaipv4)\n", pszAddress, pAddr->uAddr.IPv4.u, pAddr->uAddr.IPv4));
+ }
+ else
+ return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
+
+#ifdef RT_OS_WINDOWS
+# undef gethostbyname
+#endif
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * New function to allow both ipv4 and ipv6 addresses to be resolved.
+ * Breaks compatibility with windows before 2000.
+ */
+RTDECL(int) RTSocketQueryAddressStr(const char *pszHost, char *pszResult, size_t *pcbResult, PRTNETADDRTYPE penmAddrType)
+{
+ AssertPtrReturn(pszHost, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbResult, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(penmAddrType, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pszResult, VERR_INVALID_POINTER);
+
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) /** @todo dynamically resolve the APIs not present in NT4! */
+ return VERR_NOT_SUPPORTED;
+
+#else
+ int rc;
+ if (*pcbResult < 16)
+ return VERR_NET_ADDRESS_NOT_AVAILABLE;
+
+ /* Setup the hint. */
+ struct addrinfo grHints;
+ RT_ZERO(grHints);
+ grHints.ai_socktype = 0;
+ grHints.ai_flags = 0;
+ grHints.ai_protocol = 0;
+ grHints.ai_family = AF_UNSPEC;
+ if (penmAddrType)
+ {
+ switch (*penmAddrType)
+ {
+ case RTNETADDRTYPE_INVALID:
+ /*grHints.ai_family = AF_UNSPEC;*/
+ break;
+ case RTNETADDRTYPE_IPV4:
+ grHints.ai_family = AF_INET;
+ break;
+ case RTNETADDRTYPE_IPV6:
+ grHints.ai_family = AF_INET6;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+ }
+
+# ifdef RT_OS_WINDOWS
+ /*
+ * Winsock2 init
+ */
+ if ( !g_pfngetaddrinfo
+ || !g_pfnfreeaddrinfo)
+ return VERR_NET_NOT_UNSUPPORTED;
+
+ int rc2 = rtSocketInitWinsock();
+ if (RT_FAILURE(rc2))
+ return rc2;
+
+# define getaddrinfo g_pfngetaddrinfo
+# define freeaddrinfo g_pfnfreeaddrinfo
+# endif
+
+ /** @todo r=bird: getaddrinfo and freeaddrinfo breaks the additions on NT4. */
+ struct addrinfo *pgrResults = NULL;
+ rc = getaddrinfo(pszHost, "", &grHints, &pgrResults);
+ if (rc != 0)
+ return VERR_NET_ADDRESS_NOT_AVAILABLE;
+
+ // return data
+ // on multiple matches return only the first one
+
+ if (!pgrResults)
+ return VERR_NET_ADDRESS_NOT_AVAILABLE;
+
+ struct addrinfo const *pgrResult = pgrResults->ai_next;
+ if (!pgrResult)
+ {
+ freeaddrinfo(pgrResults);
+ return VERR_NET_ADDRESS_NOT_AVAILABLE;
+ }
+
+ RTNETADDRTYPE enmAddrType = RTNETADDRTYPE_INVALID;
+ size_t cchIpAddress;
+ char szIpAddress[48];
+ if (pgrResult->ai_family == AF_INET)
+ {
+ struct sockaddr_in const *pgrSa = (struct sockaddr_in const *)pgrResult->ai_addr;
+ cchIpAddress = RTStrPrintf(szIpAddress, sizeof(szIpAddress),
+ "%RTnaipv4", pgrSa->sin_addr.s_addr);
+ Assert(cchIpAddress >= 7 && cchIpAddress < sizeof(szIpAddress) - 1);
+ enmAddrType = RTNETADDRTYPE_IPV4;
+ rc = VINF_SUCCESS;
+ }
+ else if (pgrResult->ai_family == AF_INET6)
+ {
+ struct sockaddr_in6 const *pgrSa6 = (struct sockaddr_in6 const *)pgrResult->ai_addr;
+ cchIpAddress = RTStrPrintf(szIpAddress, sizeof(szIpAddress),
+ "%RTnaipv6", (PRTNETADDRIPV6)&pgrSa6->sin6_addr);
+ enmAddrType = RTNETADDRTYPE_IPV6;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = VERR_NET_ADDRESS_NOT_AVAILABLE;
+ szIpAddress[0] = '\0';
+ cchIpAddress = 0;
+ }
+ freeaddrinfo(pgrResults);
+
+ /*
+ * Copy out the result.
+ */
+ size_t const cbResult = *pcbResult;
+ *pcbResult = cchIpAddress + 1;
+ if (cchIpAddress < cbResult)
+ memcpy(pszResult, szIpAddress, cchIpAddress + 1);
+ else
+ {
+ RT_BZERO(pszResult, cbResult);
+ if (RT_SUCCESS(rc))
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ if (penmAddrType && RT_SUCCESS(rc))
+ *penmAddrType = enmAddrType;
+ return rc;
+
+# ifdef RT_OS_WINDOWS
+# undef getaddrinfo
+# undef freeaddrinfo
+# endif
+#endif /* !RT_OS_OS2 */
+}
+
+
+RTDECL(int) RTSocketRead(RTSOCKET hSocket, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(cbBuffer > 0, VERR_INVALID_PARAMETER);
+ AssertPtr(pvBuffer);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnrecv, VERR_NET_NOT_UNSUPPORTED);
+# define recv g_pfnrecv
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc = rtSocketSwitchBlockingMode(pThis, true /* fBlocking */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Read loop.
+ * If pcbRead is NULL we have to fill the entire buffer!
+ */
+ size_t cbRead = 0;
+ size_t cbToRead = cbBuffer;
+ for (;;)
+ {
+ rtSocketErrorReset();
+#ifdef RTSOCKET_MAX_READ
+ int cbNow = cbToRead >= RTSOCKET_MAX_READ ? RTSOCKET_MAX_READ : (int)cbToRead;
+#else
+ size_t cbNow = cbToRead;
+#endif
+ ssize_t cbBytesRead = recv(pThis->hNative, (char *)pvBuffer + cbRead, cbNow, MSG_NOSIGNAL);
+ if (cbBytesRead <= 0)
+ {
+ rc = rtSocketError();
+ Assert(RT_FAILURE_NP(rc) || cbBytesRead == 0);
+ if (RT_SUCCESS_NP(rc))
+ {
+ if (!pcbRead)
+ rc = VERR_NET_SHUTDOWN;
+ else
+ {
+ *pcbRead = 0;
+ rc = VINF_SUCCESS;
+ }
+ }
+ break;
+ }
+ if (pcbRead)
+ {
+ /* return partial data */
+ *pcbRead = cbBytesRead;
+ break;
+ }
+
+ /* read more? */
+ cbRead += cbBytesRead;
+ if (cbRead == cbBuffer)
+ break;
+
+ /* next */
+ cbToRead = cbBuffer - cbRead;
+ }
+
+ rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef recv
+#endif
+ return rc;
+}
+
+
+RTDECL(int) RTSocketReadFrom(RTSOCKET hSocket, void *pvBuffer, size_t cbBuffer, size_t *pcbRead, PRTNETADDR pSrcAddr)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(cbBuffer > 0, VERR_INVALID_PARAMETER);
+ AssertPtr(pvBuffer);
+ AssertPtr(pcbRead);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnrecvfrom, VERR_NET_NOT_UNSUPPORTED);
+# define recvfrom g_pfnrecvfrom
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc = rtSocketSwitchBlockingMode(pThis, true /* fBlocking */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Read data.
+ */
+ size_t cbRead = 0;
+ size_t cbToRead = cbBuffer;
+ rtSocketErrorReset();
+ RTSOCKADDRUNION u;
+#ifdef RTSOCKET_MAX_READ
+ int cbNow = cbToRead >= RTSOCKET_MAX_READ ? RTSOCKET_MAX_READ : (int)cbToRead;
+ int cbAddr = sizeof(u);
+#else
+ size_t cbNow = cbToRead;
+ socklen_t cbAddr = sizeof(u);
+#endif
+ ssize_t cbBytesRead = recvfrom(pThis->hNative, (char *)pvBuffer + cbRead, cbNow, MSG_NOSIGNAL, &u.Addr, &cbAddr);
+ if (cbBytesRead <= 0)
+ {
+ rc = rtSocketError();
+ Assert(RT_FAILURE_NP(rc) || cbBytesRead == 0);
+ if (RT_SUCCESS_NP(rc))
+ {
+ *pcbRead = 0;
+ rc = VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ if (pSrcAddr)
+ rc = rtSocketNetAddrFromAddr(&u, cbAddr, pSrcAddr);
+ *pcbRead = cbBytesRead;
+ }
+
+ rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef recvfrom
+#endif
+ return rc;
+}
+
+
+RTDECL(int) RTSocketWrite(RTSOCKET hSocket, const void *pvBuffer, size_t cbBuffer)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnsend, VERR_NET_NOT_UNSUPPORTED);
+# define send g_pfnsend
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc = rtSocketSwitchBlockingMode(pThis, true /* fBlocking */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Try write all at once.
+ */
+#ifdef RTSOCKET_MAX_WRITE
+ int cbNow = cbBuffer >= RTSOCKET_MAX_WRITE ? RTSOCKET_MAX_WRITE : (int)cbBuffer;
+#else
+ size_t cbNow = cbBuffer >= SSIZE_MAX ? SSIZE_MAX : cbBuffer;
+#endif
+ ssize_t cbWritten = send(pThis->hNative, (const char *)pvBuffer, cbNow, MSG_NOSIGNAL);
+ if (RT_LIKELY((size_t)cbWritten == cbBuffer && cbWritten >= 0))
+ rc = VINF_SUCCESS;
+ else if (cbWritten < 0)
+ rc = rtSocketError();
+ else
+ {
+ /*
+ * Unfinished business, write the remainder of the request. Must ignore
+ * VERR_INTERRUPTED here if we've managed to send something.
+ */
+ size_t cbSentSoFar = 0;
+ for (;;)
+ {
+ /* advance */
+ cbBuffer -= (size_t)cbWritten;
+ if (!cbBuffer)
+ break;
+ cbSentSoFar += (size_t)cbWritten;
+ pvBuffer = (char const *)pvBuffer + cbWritten;
+
+ /* send */
+#ifdef RTSOCKET_MAX_WRITE
+ cbNow = cbBuffer >= RTSOCKET_MAX_WRITE ? RTSOCKET_MAX_WRITE : (int)cbBuffer;
+#else
+ cbNow = cbBuffer >= SSIZE_MAX ? SSIZE_MAX : cbBuffer;
+#endif
+ cbWritten = send(pThis->hNative, (const char *)pvBuffer, cbNow, MSG_NOSIGNAL);
+ if (cbWritten >= 0)
+ AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%zu cbBuffer=%zu rtSocketError()=%d\n",
+ cbWritten, cbBuffer, rtSocketError()));
+ else
+ {
+ rc = rtSocketError();
+ if (rc != VERR_INTERNAL_ERROR || cbSentSoFar == 0)
+ break;
+ cbWritten = 0;
+ rc = VINF_SUCCESS;
+ }
+ }
+ }
+
+ rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef send
+#endif
+ return rc;
+}
+
+
+RTDECL(int) RTSocketWriteTo(RTSOCKET hSocket, const void *pvBuffer, size_t cbBuffer, PCRTNETADDR pAddr)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnsendto, VERR_NET_NOT_UNSUPPORTED);
+# define sendto g_pfnsendto
+#endif
+
+ /* no locking since UDP reads may be done concurrently to writes, and
+ * this is the normal use case of this code. */
+
+ int rc = rtSocketSwitchBlockingMode(pThis, true /* fBlocking */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Figure out destination address. */
+ struct sockaddr *pSA = NULL;
+#ifdef RT_OS_WINDOWS
+ int cbSA = 0;
+#else
+ socklen_t cbSA = 0;
+#endif
+ RTSOCKADDRUNION u;
+ if (pAddr)
+ {
+ rc = rtSocketAddrFromNetAddr(pAddr, &u, sizeof(u), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ pSA = &u.Addr;
+ cbSA = sizeof(u);
+ }
+
+ /*
+ * Must write all at once, otherwise it is a failure.
+ */
+#ifdef RT_OS_WINDOWS
+ int cbNow = cbBuffer >= RTSOCKET_MAX_WRITE ? RTSOCKET_MAX_WRITE : (int)cbBuffer;
+#else
+ size_t cbNow = cbBuffer >= SSIZE_MAX ? SSIZE_MAX : cbBuffer;
+#endif
+ ssize_t cbWritten = sendto(pThis->hNative, (const char *)pvBuffer, cbNow, MSG_NOSIGNAL, pSA, cbSA);
+ if (RT_LIKELY((size_t)cbWritten == cbBuffer && cbWritten >= 0))
+ rc = VINF_SUCCESS;
+ else if (cbWritten < 0)
+ rc = rtSocketError();
+ else
+ rc = VERR_TOO_MUCH_DATA;
+
+ /// @todo rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef sendto
+#endif
+ return rc;
+}
+
+
+RTDECL(int) RTSocketWriteToNB(RTSOCKET hSocket, const void *pvBuffer, size_t cbBuffer, PCRTNETADDR pAddr)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnsendto, VERR_NET_NOT_UNSUPPORTED);
+# define sendto g_pfnsendto
+#endif
+
+ /* no locking since UDP reads may be done concurrently to writes, and
+ * this is the normal use case of this code. */
+
+ int rc = rtSocketSwitchBlockingMode(pThis, false /* fBlocking */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Figure out destination address. */
+ struct sockaddr *pSA = NULL;
+#ifdef RT_OS_WINDOWS
+ int cbSA = 0;
+#else
+ socklen_t cbSA = 0;
+#endif
+ RTSOCKADDRUNION u;
+ if (pAddr)
+ {
+ rc = rtSocketAddrFromNetAddr(pAddr, &u, sizeof(u), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ pSA = &u.Addr;
+ cbSA = sizeof(u);
+ }
+
+ /*
+ * Must write all at once, otherwise it is a failure.
+ */
+#ifdef RT_OS_WINDOWS
+ int cbNow = cbBuffer >= RTSOCKET_MAX_WRITE ? RTSOCKET_MAX_WRITE : (int)cbBuffer;
+#else
+ size_t cbNow = cbBuffer >= SSIZE_MAX ? SSIZE_MAX : cbBuffer;
+#endif
+ ssize_t cbWritten = sendto(pThis->hNative, (const char *)pvBuffer, cbNow, MSG_NOSIGNAL, pSA, cbSA);
+ if (RT_LIKELY((size_t)cbWritten == cbBuffer && cbWritten >= 0))
+ rc = VINF_SUCCESS;
+ else if (cbWritten < 0)
+ rc = rtSocketError();
+ else
+ rc = VERR_TOO_MUCH_DATA;
+
+ /// @todo rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef sendto
+#endif
+ return rc;
+}
+
+
+RTDECL(int) RTSocketSgWrite(RTSOCKET hSocket, PCRTSGBUF pSgBuf)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pSgBuf, VERR_INVALID_PARAMETER);
+ AssertReturn(pSgBuf->cSegs > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc = rtSocketSwitchBlockingMode(pThis, true /* fBlocking */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Construct message descriptor (translate pSgBuf) and send it.
+ */
+ rc = VERR_NO_TMP_MEMORY;
+#ifdef RT_OS_WINDOWS
+ if (g_pfnWSASend)
+ {
+ AssertCompileSize(WSABUF, sizeof(RTSGSEG));
+ AssertCompileMemberSize(WSABUF, buf, RT_SIZEOFMEMB(RTSGSEG, pvSeg));
+
+ LPWSABUF paMsg = (LPWSABUF)RTMemTmpAllocZ(pSgBuf->cSegs * sizeof(WSABUF));
+ if (paMsg)
+ {
+ for (unsigned i = 0; i < pSgBuf->cSegs; i++)
+ {
+ paMsg[i].buf = (char *)pSgBuf->paSegs[i].pvSeg;
+ paMsg[i].len = (u_long)pSgBuf->paSegs[i].cbSeg;
+ }
+
+ DWORD dwSent;
+ int hrc = g_pfnWSASend(pThis->hNative, paMsg, pSgBuf->cSegs, &dwSent, MSG_NOSIGNAL, NULL, NULL);
+ if (!hrc)
+ rc = VINF_SUCCESS;
+ /** @todo check for incomplete writes */
+ else
+ rc = rtSocketError();
+
+ RTMemTmpFree(paMsg);
+ }
+ }
+ else if (g_pfnsend)
+ {
+ rc = VINF_SUCCESS;
+ for (uint32_t iSeg = 0; iSeg < pSgBuf->cSegs; iSeg++)
+ {
+ uint8_t const *pbSeg = (uint8_t const *)pSgBuf->paSegs[iSeg].pvSeg;
+ size_t cbSeg = pSgBuf->paSegs[iSeg].cbSeg;
+ int cbNow;
+ ssize_t cbWritten;
+ for (;;)
+ {
+ cbNow = cbSeg >= RTSOCKET_MAX_WRITE ? RTSOCKET_MAX_WRITE : (int)cbSeg;
+ cbWritten = g_pfnsend(pThis->hNative, (const char *)pbSeg, cbNow, MSG_NOSIGNAL);
+ if ((size_t)cbWritten >= cbSeg || cbWritten < 0)
+ break;
+ pbSeg += cbWritten;
+ cbSeg -= cbWritten;
+ }
+ if (cbWritten < 0)
+ {
+ rc = rtSocketError();
+ break;
+ }
+ }
+ }
+ else
+ rc = VERR_NET_NOT_UNSUPPORTED;
+
+#else /* !RT_OS_WINDOWS */
+ AssertCompileSize(struct iovec, sizeof(RTSGSEG));
+ AssertCompileMemberSize(struct iovec, iov_base, RT_SIZEOFMEMB(RTSGSEG, pvSeg));
+ AssertCompileMemberSize(struct iovec, iov_len, RT_SIZEOFMEMB(RTSGSEG, cbSeg));
+
+ struct iovec *paMsg = (struct iovec *)RTMemTmpAllocZ(pSgBuf->cSegs * sizeof(struct iovec));
+ if (paMsg)
+ {
+ for (unsigned i = 0; i < pSgBuf->cSegs; i++)
+ {
+ paMsg[i].iov_base = pSgBuf->paSegs[i].pvSeg;
+ paMsg[i].iov_len = pSgBuf->paSegs[i].cbSeg;
+ }
+
+ struct msghdr msgHdr;
+ RT_ZERO(msgHdr);
+ msgHdr.msg_iov = paMsg;
+ msgHdr.msg_iovlen = pSgBuf->cSegs;
+ ssize_t cbWritten = sendmsg(pThis->hNative, &msgHdr, MSG_NOSIGNAL);
+ if (RT_LIKELY(cbWritten >= 0))
+ rc = VINF_SUCCESS;
+/** @todo check for incomplete writes */
+ else
+ rc = rtSocketError();
+
+ RTMemTmpFree(paMsg);
+ }
+#endif /* !RT_OS_WINDOWS */
+
+ rtSocketUnlock(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSocketSgWriteL(RTSOCKET hSocket, size_t cSegs, ...)
+{
+ va_list va;
+ va_start(va, cSegs);
+ int rc = RTSocketSgWriteLV(hSocket, cSegs, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTSocketSgWriteLV(RTSOCKET hSocket, size_t cSegs, va_list va)
+{
+ /*
+ * Set up a S/G segment array + buffer on the stack and pass it
+ * on to RTSocketSgWrite.
+ */
+ Assert(cSegs <= 16);
+ PRTSGSEG paSegs = (PRTSGSEG)alloca(cSegs * sizeof(RTSGSEG));
+ AssertReturn(paSegs, VERR_NO_TMP_MEMORY);
+ for (size_t i = 0; i < cSegs; i++)
+ {
+ paSegs[i].pvSeg = va_arg(va, void *);
+ paSegs[i].cbSeg = va_arg(va, size_t);
+ }
+
+ RTSGBUF SgBuf;
+ RTSgBufInit(&SgBuf, paSegs, cSegs);
+ return RTSocketSgWrite(hSocket, &SgBuf);
+}
+
+
+RTDECL(int) RTSocketReadNB(RTSOCKET hSocket, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(cbBuffer > 0, VERR_INVALID_PARAMETER);
+ AssertPtr(pvBuffer);
+ AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnrecv, VERR_NET_NOT_UNSUPPORTED);
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc = rtSocketSwitchBlockingMode(pThis, false /* fBlocking */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rtSocketErrorReset();
+#ifdef RTSOCKET_MAX_READ
+ int cbNow = cbBuffer >= RTSOCKET_MAX_WRITE ? RTSOCKET_MAX_WRITE : (int)cbBuffer;
+#else
+ size_t cbNow = cbBuffer;
+#endif
+
+#ifdef RT_OS_WINDOWS
+ int cbRead = g_pfnrecv(pThis->hNative, (char *)pvBuffer, cbNow, MSG_NOSIGNAL);
+ if (cbRead >= 0)
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = rtSocketError();
+ if (rc == VERR_TRY_AGAIN)
+ {
+ *pcbRead = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ }
+
+#else
+ ssize_t cbRead = recv(pThis->hNative, pvBuffer, cbNow, MSG_NOSIGNAL);
+ if (cbRead >= 0)
+ *pcbRead = cbRead;
+ else if ( errno == EAGAIN
+# ifdef EWOULDBLOCK
+# if EWOULDBLOCK != EAGAIN
+ || errno == EWOULDBLOCK
+# endif
+# endif
+ )
+ {
+ *pcbRead = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = rtSocketError();
+#endif
+
+ rtSocketUnlock(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSocketWriteNB(RTSOCKET hSocket, const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnsend, VERR_NET_NOT_UNSUPPORTED);
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc = rtSocketSwitchBlockingMode(pThis, false /* fBlocking */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rtSocketErrorReset();
+#ifdef RT_OS_WINDOWS
+# ifdef RTSOCKET_MAX_WRITE
+ int cbNow = cbBuffer >= RTSOCKET_MAX_WRITE ? RTSOCKET_MAX_WRITE : (int)cbBuffer;
+# else
+ size_t cbNow = cbBuffer;
+# endif
+ int cbWritten = g_pfnsend(pThis->hNative, (const char *)pvBuffer, cbNow, MSG_NOSIGNAL);
+ if (cbWritten >= 0)
+ {
+ *pcbWritten = cbWritten;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = rtSocketError();
+ if (rc == VERR_TRY_AGAIN)
+ {
+ *pcbWritten = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ }
+#else
+ ssize_t cbWritten = send(pThis->hNative, pvBuffer, cbBuffer, MSG_NOSIGNAL);
+ if (cbWritten >= 0)
+ *pcbWritten = cbWritten;
+ else if ( errno == EAGAIN
+# ifdef EWOULDBLOCK
+# if EWOULDBLOCK != EAGAIN
+ || errno == EWOULDBLOCK
+# endif
+# endif
+ )
+ {
+ *pcbWritten = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = rtSocketError();
+#endif
+
+ rtSocketUnlock(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSocketSgWriteNB(RTSOCKET hSocket, PCRTSGBUF pSgBuf, size_t *pcbWritten)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pSgBuf, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER);
+ AssertReturn(pSgBuf->cSegs > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc = rtSocketSwitchBlockingMode(pThis, false /* fBlocking */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ unsigned cSegsToSend = 0;
+ rc = VERR_NO_TMP_MEMORY;
+#ifdef RT_OS_WINDOWS
+ if (g_pfnWSASend)
+ {
+ LPWSABUF paMsg = NULL;
+ RTSgBufMapToNative(paMsg, pSgBuf, WSABUF, buf, char *, len, u_long, cSegsToSend);
+ if (paMsg)
+ {
+ DWORD dwSent = 0;
+ int hrc = g_pfnWSASend(pThis->hNative, paMsg, cSegsToSend, &dwSent, MSG_NOSIGNAL, NULL, NULL);
+ if (!hrc)
+ rc = VINF_SUCCESS;
+ else
+ rc = rtSocketError();
+
+ *pcbWritten = dwSent;
+
+ RTMemTmpFree(paMsg);
+ }
+ }
+ else if (g_pfnsend)
+ {
+ size_t cbWrittenTotal = 0;
+ rc = VINF_SUCCESS;
+ for (uint32_t iSeg = 0; iSeg < pSgBuf->cSegs; iSeg++)
+ {
+ uint8_t const *pbSeg = (uint8_t const *)pSgBuf->paSegs[iSeg].pvSeg;
+ size_t cbSeg = pSgBuf->paSegs[iSeg].cbSeg;
+ int cbNow;
+ ssize_t cbWritten;
+ for (;;)
+ {
+ cbNow = cbSeg >= RTSOCKET_MAX_WRITE ? RTSOCKET_MAX_WRITE : (int)cbSeg;
+ cbWritten = g_pfnsend(pThis->hNative, (const char *)pbSeg, cbNow, MSG_NOSIGNAL);
+ if ((size_t)cbWritten >= cbSeg || cbWritten < 0)
+ break;
+ cbWrittenTotal += cbWrittenTotal;
+ pbSeg += cbWritten;
+ cbSeg -= cbWritten;
+ }
+ if (cbWritten < 0)
+ {
+ rc = rtSocketError();
+ break;
+ }
+ if (cbWritten != cbNow)
+ break;
+ }
+ *pcbWritten = cbWrittenTotal;
+ }
+ else
+ rc = VERR_NET_NOT_UNSUPPORTED;
+
+#else /* !RT_OS_WINDOWS */
+ struct iovec *paMsg = NULL;
+
+ RTSgBufMapToNative(paMsg, pSgBuf, struct iovec, iov_base, void *, iov_len, size_t, cSegsToSend);
+ if (paMsg)
+ {
+ struct msghdr msgHdr;
+ RT_ZERO(msgHdr);
+ msgHdr.msg_iov = paMsg;
+ msgHdr.msg_iovlen = cSegsToSend;
+ ssize_t cbWritten = sendmsg(pThis->hNative, &msgHdr, MSG_NOSIGNAL);
+ if (RT_LIKELY(cbWritten >= 0))
+ {
+ rc = VINF_SUCCESS;
+ *pcbWritten = cbWritten;
+ }
+ else
+ rc = rtSocketError();
+
+ RTMemTmpFree(paMsg);
+ }
+#endif /* !RT_OS_WINDOWS */
+
+ rtSocketUnlock(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSocketSgWriteLNB(RTSOCKET hSocket, size_t cSegs, size_t *pcbWritten, ...)
+{
+ va_list va;
+ va_start(va, pcbWritten);
+ int rc = RTSocketSgWriteLVNB(hSocket, cSegs, pcbWritten, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(int) RTSocketSgWriteLVNB(RTSOCKET hSocket, size_t cSegs, size_t *pcbWritten, va_list va)
+{
+ /*
+ * Set up a S/G segment array + buffer on the stack and pass it
+ * on to RTSocketSgWrite.
+ */
+ Assert(cSegs <= 16);
+ PRTSGSEG paSegs = (PRTSGSEG)alloca(cSegs * sizeof(RTSGSEG));
+ AssertReturn(paSegs, VERR_NO_TMP_MEMORY);
+ for (size_t i = 0; i < cSegs; i++)
+ {
+ paSegs[i].pvSeg = va_arg(va, void *);
+ paSegs[i].cbSeg = va_arg(va, size_t);
+ }
+
+ RTSGBUF SgBuf;
+ RTSgBufInit(&SgBuf, paSegs, cSegs);
+ return RTSocketSgWriteNB(hSocket, &SgBuf, pcbWritten);
+}
+
+
+RTDECL(int) RTSocketSelectOne(RTSOCKET hSocket, RTMSINTERVAL cMillies)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRefCount(pThis) >= (pThis->cUsers ? 2U : 1U), VERR_CALLER_NO_REFERENCE);
+ int const fdMax = (int)pThis->hNative + 1;
+ AssertReturn((RTSOCKETNATIVE)(fdMax - 1) == pThis->hNative, VERR_INTERNAL_ERROR_5);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnselect, VERR_NET_NOT_UNSUPPORTED);
+# define select g_pfnselect
+#endif
+
+ /*
+ * Set up the file descriptor sets and do the select.
+ */
+ fd_set fdsetR;
+ FD_ZERO(&fdsetR);
+ FD_SET(pThis->hNative, &fdsetR);
+
+ fd_set fdsetE = fdsetR;
+
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = select(fdMax, &fdsetR, NULL, &fdsetE, NULL);
+ else
+ {
+ struct timeval timeout;
+ timeout.tv_sec = cMillies / 1000;
+ timeout.tv_usec = (cMillies % 1000) * 1000;
+ rc = select(fdMax, &fdsetR, NULL, &fdsetE, &timeout);
+ }
+ if (rc > 0)
+ rc = VINF_SUCCESS;
+ else if (rc == 0)
+ rc = VERR_TIMEOUT;
+ else
+ rc = rtSocketError();
+
+#ifdef RT_OS_WINDOWS
+# undef select
+#endif
+ return rc;
+}
+
+
+/**
+ * Internal worker for RTSocketSelectOneEx and rtSocketPollCheck (fallback)
+ *
+ * @returns IPRT status code
+ * @param pThis The socket (valid).
+ * @param fEvents The events to select for.
+ * @param pfEvents Where to return the events.
+ * @param cMillies How long to select for, in milliseconds.
+ */
+static int rtSocketSelectOneEx(RTSOCKET pThis, uint32_t fEvents, uint32_t *pfEvents, RTMSINTERVAL cMillies)
+{
+ RTSOCKETNATIVE hNative = pThis->hNative;
+ if (hNative == NIL_RTSOCKETNATIVE)
+ {
+ /* Socket is already closed? Possible we raced someone calling rtSocketCloseIt.
+ Should we return a different status code? */
+ *pfEvents = RTSOCKET_EVT_ERROR;
+ return VINF_SUCCESS;
+ }
+
+ int const fdMax = (int)hNative + 1;
+ AssertReturn((RTSOCKETNATIVE)(fdMax - 1) == hNative, VERR_INTERNAL_ERROR_5);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnselect, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfn__WSAFDIsSet, VERR_NET_NOT_UNSUPPORTED);
+# define select g_pfnselect
+# define __WSAFDIsSet g_pfn__WSAFDIsSet
+#endif
+
+ *pfEvents = 0;
+
+ /*
+ * Set up the file descriptor sets and do the select.
+ */
+ fd_set fdsetR;
+ fd_set fdsetW;
+ fd_set fdsetE;
+ FD_ZERO(&fdsetR);
+ FD_ZERO(&fdsetW);
+ FD_ZERO(&fdsetE);
+
+ if (fEvents & RTSOCKET_EVT_READ)
+ FD_SET(hNative, &fdsetR);
+ if (fEvents & RTSOCKET_EVT_WRITE)
+ FD_SET(hNative, &fdsetW);
+ if (fEvents & RTSOCKET_EVT_ERROR)
+ FD_SET(hNative, &fdsetE);
+
+ int rc;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rc = select(fdMax, &fdsetR, &fdsetW, &fdsetE, NULL);
+ else
+ {
+ struct timeval timeout;
+ timeout.tv_sec = cMillies / 1000;
+ timeout.tv_usec = (cMillies % 1000) * 1000;
+ rc = select(fdMax, &fdsetR, &fdsetW, &fdsetE, &timeout);
+ }
+ if (rc > 0)
+ {
+ if (pThis->hNative == hNative)
+ {
+ if (FD_ISSET(hNative, &fdsetR))
+ *pfEvents |= RTSOCKET_EVT_READ;
+ if (FD_ISSET(hNative, &fdsetW))
+ *pfEvents |= RTSOCKET_EVT_WRITE;
+ if (FD_ISSET(hNative, &fdsetE))
+ *pfEvents |= RTSOCKET_EVT_ERROR;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ /* Socket was closed while we waited (rtSocketCloseIt). Different status code? */
+ *pfEvents = RTSOCKET_EVT_ERROR;
+ rc = VINF_SUCCESS;
+ }
+ }
+ else if (rc == 0)
+ rc = VERR_TIMEOUT;
+ else
+ rc = rtSocketError();
+
+#ifdef RT_OS_WINDOWS
+# undef select
+# undef __WSAFDIsSet
+#endif
+ return rc;
+}
+
+
+RTDECL(int) RTSocketSelectOneEx(RTSOCKET hSocket, uint32_t fEvents, uint32_t *pfEvents, RTMSINTERVAL cMillies)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pfEvents, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fEvents & ~RTSOCKET_EVT_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(RTMemPoolRefCount(pThis) >= (pThis->cUsers ? 2U : 1U), VERR_CALLER_NO_REFERENCE);
+
+ return rtSocketSelectOneEx(pThis, fEvents, pfEvents, cMillies);
+}
+
+
+RTDECL(int) RTSocketShutdown(RTSOCKET hSocket, bool fRead, bool fWrite)
+{
+ /*
+ * Validate input, don't lock it because we might want to interrupt a call
+ * active on a different thread.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRefCount(pThis) >= (pThis->cUsers ? 2U : 1U), VERR_CALLER_NO_REFERENCE);
+ AssertReturn(fRead || fWrite, VERR_INVALID_PARAMETER);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnshutdown, VERR_NET_NOT_UNSUPPORTED);
+# define shutdown g_pfnshutdown
+#endif
+
+ /*
+ * Do the job.
+ */
+ int rc = VINF_SUCCESS;
+ int fHow;
+ if (fRead && fWrite)
+ fHow = SHUT_RDWR;
+ else if (fRead)
+ fHow = SHUT_RD;
+ else
+ fHow = SHUT_WR;
+ if (shutdown(pThis->hNative, fHow) == -1)
+ rc = rtSocketError();
+
+#ifdef RT_OS_WINDOWS
+# undef shutdown
+#endif
+ return rc;
+}
+
+
+RTDECL(int) RTSocketGetLocalAddress(RTSOCKET hSocket, PRTNETADDR pAddr)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRefCount(pThis) >= (pThis->cUsers ? 2U : 1U), VERR_CALLER_NO_REFERENCE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfngetsockname, VERR_NET_NOT_UNSUPPORTED);
+# define getsockname g_pfngetsockname
+#endif
+
+ /*
+ * Get the address and convert it.
+ */
+ int rc;
+ RTSOCKADDRUNION u;
+#ifdef RT_OS_WINDOWS
+ int cbAddr = sizeof(u);
+#else
+ socklen_t cbAddr = sizeof(u);
+#endif
+ RT_ZERO(u);
+ if (getsockname(pThis->hNative, &u.Addr, &cbAddr) == 0)
+ rc = rtSocketNetAddrFromAddr(&u, cbAddr, pAddr);
+ else
+ rc = rtSocketError();
+
+#ifdef RT_OS_WINDOWS
+# undef getsockname
+#endif
+ return rc;
+}
+
+
+RTDECL(int) RTSocketGetPeerAddress(RTSOCKET hSocket, PRTNETADDR pAddr)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRefCount(pThis) >= (pThis->cUsers ? 2U : 1U), VERR_CALLER_NO_REFERENCE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfngetpeername, VERR_NET_NOT_UNSUPPORTED);
+# define getpeername g_pfngetpeername
+#endif
+
+ /*
+ * Get the address and convert it.
+ */
+ int rc;
+ RTSOCKADDRUNION u;
+#ifdef RT_OS_WINDOWS
+ int cbAddr = sizeof(u);
+#else
+ socklen_t cbAddr = sizeof(u);
+#endif
+ RT_ZERO(u);
+ if (getpeername(pThis->hNative, &u.Addr, &cbAddr) == 0)
+ rc = rtSocketNetAddrFromAddr(&u, cbAddr, pAddr);
+ else
+ rc = rtSocketError();
+
+#ifdef RT_OS_WINDOWS
+# undef getpeername
+#endif
+ return rc;
+}
+
+
+
+/**
+ * Wrapper around bind.
+ *
+ * @returns IPRT status code.
+ * @param hSocket The socket handle.
+ * @param pAddr The address to bind to.
+ */
+DECLHIDDEN(int) rtSocketBind(RTSOCKET hSocket, PCRTNETADDR pAddr)
+{
+ RTSOCKADDRUNION u;
+ int cbAddr;
+ int rc = rtSocketAddrFromNetAddr(pAddr, &u, sizeof(u), &cbAddr);
+ if (RT_SUCCESS(rc))
+ rc = rtSocketBindRawAddr(hSocket, &u.Addr, cbAddr);
+ return rc;
+}
+
+
+/**
+ * Very thin wrapper around bind.
+ *
+ * @returns IPRT status code.
+ * @param hSocket The socket handle.
+ * @param pvAddr The address to bind to (struct sockaddr and
+ * friends).
+ * @param cbAddr The size of the address.
+ */
+DECLHIDDEN(int) rtSocketBindRawAddr(RTSOCKET hSocket, void const *pvAddr, size_t cbAddr)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvAddr, VERR_INVALID_POINTER);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnbind, VERR_NET_NOT_UNSUPPORTED);
+# define bind g_pfnbind
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc;
+ if (bind(pThis->hNative, (struct sockaddr const *)pvAddr, (int)cbAddr) == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = rtSocketError();
+
+ rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef bind
+#endif
+ return rc;
+}
+
+
+
+/**
+ * Wrapper around listen.
+ *
+ * @returns IPRT status code.
+ * @param hSocket The socket handle.
+ * @param cMaxPending The max number of pending connections.
+ */
+DECLHIDDEN(int) rtSocketListen(RTSOCKET hSocket, int cMaxPending)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnlisten, VERR_NET_NOT_UNSUPPORTED);
+# define listen g_pfnlisten
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc = VINF_SUCCESS;
+ if (listen(pThis->hNative, cMaxPending) != 0)
+ rc = rtSocketError();
+
+ rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef listen
+#endif
+ return rc;
+}
+
+
+/**
+ * Wrapper around accept.
+ *
+ * @returns IPRT status code.
+ * @param hSocket The socket handle.
+ * @param phClient Where to return the client socket handle on
+ * success.
+ * @param pAddr Where to return the client address.
+ * @param pcbAddr On input this gives the size buffer size of what
+ * @a pAddr point to. On return this contains the
+ * size of what's stored at @a pAddr.
+ */
+DECLHIDDEN(int) rtSocketAccept(RTSOCKET hSocket, PRTSOCKET phClient, struct sockaddr *pAddr, size_t *pcbAddr)
+{
+ /*
+ * Validate input.
+ * Only lock the socket temporarily while we get the native handle, so that
+ * we can safely shutdown and destroy the socket from a different thread.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnaccept, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfnclosesocket, VERR_NET_NOT_UNSUPPORTED);
+# define accept g_pfnaccept
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ /*
+ * Call accept().
+ */
+ rtSocketErrorReset();
+ int rc = VINF_SUCCESS;
+#ifdef RT_OS_WINDOWS
+ int cbAddr = (int)*pcbAddr;
+#else
+ socklen_t cbAddr = *pcbAddr;
+#endif
+ RTSOCKETNATIVE hNativeClient = accept(pThis->hNative, pAddr, &cbAddr);
+ if (hNativeClient != NIL_RTSOCKETNATIVE)
+ {
+ *pcbAddr = cbAddr;
+
+ /*
+ * Wrap the client socket.
+ */
+ rc = rtSocketCreateForNative(phClient, hNativeClient, false /*fLeaveOpen*/);
+ if (RT_FAILURE(rc))
+ {
+#ifdef RT_OS_WINDOWS
+ g_pfnclosesocket(hNativeClient);
+#else
+ close(hNativeClient);
+#endif
+ }
+ }
+ else
+ rc = rtSocketError();
+
+ rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef accept
+#endif
+ return rc;
+}
+
+
+/**
+ * Wrapper around connect.
+ *
+ * @returns IPRT status code.
+ * @param hSocket The socket handle.
+ * @param pAddr The socket address to connect to.
+ * @param cMillies Number of milliseconds to wait for the connect attempt to complete.
+ * Use RT_INDEFINITE_WAIT to wait for ever.
+ * Use RT_TCPCLIENTCONNECT_DEFAULT_WAIT to wait for the default time
+ * configured on the running system.
+ */
+DECLHIDDEN(int) rtSocketConnect(RTSOCKET hSocket, PCRTNETADDR pAddr, RTMSINTERVAL cMillies)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnconnect, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfnselect, VERR_NET_NOT_UNSUPPORTED);
+ AssertReturn(g_pfngetsockopt, VERR_NET_NOT_UNSUPPORTED);
+# define connect g_pfnconnect
+# define select g_pfnselect
+# define getsockopt g_pfngetsockopt
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ RTSOCKADDRUNION u;
+ int cbAddr;
+ int rc = rtSocketAddrFromNetAddr(pAddr, &u, sizeof(u), &cbAddr);
+ if (RT_SUCCESS(rc))
+ {
+ if (cMillies == RT_SOCKETCONNECT_DEFAULT_WAIT)
+ {
+ if (connect(pThis->hNative, &u.Addr, cbAddr) != 0)
+ rc = rtSocketError();
+ }
+ else
+ {
+ /*
+ * Switch the socket to nonblocking mode, initiate the connect
+ * and wait for the socket to become writable or until the timeout
+ * expires.
+ */
+ rc = rtSocketSwitchBlockingMode(pThis, false /* fBlocking */);
+ if (RT_SUCCESS(rc))
+ {
+ if (connect(pThis->hNative, &u.Addr, cbAddr) != 0)
+ {
+ rc = rtSocketError();
+ if (rc == VERR_TRY_AGAIN || rc == VERR_NET_IN_PROGRESS)
+ {
+ int rcSock = 0;
+ fd_set FdSetWriteable;
+ struct timeval TvTimeout;
+
+ TvTimeout.tv_sec = cMillies / RT_MS_1SEC;
+ TvTimeout.tv_usec = (cMillies % RT_MS_1SEC) * RT_US_1MS;
+
+ FD_ZERO(&FdSetWriteable);
+ FD_SET(pThis->hNative, &FdSetWriteable);
+ do
+ {
+ rcSock = select(pThis->hNative + 1, NULL, &FdSetWriteable, NULL,
+ cMillies == RT_INDEFINITE_WAIT || cMillies >= INT_MAX
+ ? NULL
+ : &TvTimeout);
+ if (rcSock > 0)
+ {
+ int iSockError = 0;
+ socklen_t cbSockOpt = sizeof(iSockError);
+ rcSock = getsockopt(pThis->hNative, SOL_SOCKET, SO_ERROR, (char *)&iSockError, &cbSockOpt);
+ if (rcSock == 0)
+ {
+ if (iSockError == 0)
+ rc = VINF_SUCCESS;
+ else
+ {
+#ifdef RT_OS_WINDOWS
+ rc = RTErrConvertFromWin32(iSockError);
+#else
+ rc = RTErrConvertFromErrno(iSockError);
+#endif
+ }
+ }
+ else
+ rc = rtSocketError();
+ }
+ else if (rcSock == 0)
+ rc = VERR_TIMEOUT;
+ else
+ rc = rtSocketError();
+ } while (rc == VERR_INTERRUPTED);
+ }
+ }
+
+ rtSocketSwitchBlockingMode(pThis, true /* fBlocking */);
+ }
+ }
+ }
+
+ rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef connect
+# undef select
+# undef getsockopt
+#endif
+ return rc;
+}
+
+
+/**
+ * Wrapper around connect, raw address, no timeout.
+ *
+ * @returns IPRT status code.
+ * @param hSocket The socket handle.
+ * @param pvAddr The raw socket address to connect to.
+ * @param cbAddr The size of the raw address.
+ */
+DECLHIDDEN(int) rtSocketConnectRaw(RTSOCKET hSocket, void const *pvAddr, size_t cbAddr)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnconnect, VERR_NET_NOT_UNSUPPORTED);
+# define connect g_pfnconnect
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc;
+ if (connect(pThis->hNative, (const struct sockaddr *)pvAddr, (int)cbAddr) == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = rtSocketError();
+
+ rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef connect
+#endif
+ return rc;
+}
+
+
+/**
+ * Wrapper around setsockopt.
+ *
+ * @returns IPRT status code.
+ * @param hSocket The socket handle.
+ * @param iLevel The protocol level, e.g. IPPORTO_TCP.
+ * @param iOption The option, e.g. TCP_NODELAY.
+ * @param pvValue The value buffer.
+ * @param cbValue The size of the value pointed to by pvValue.
+ */
+DECLHIDDEN(int) rtSocketSetOpt(RTSOCKET hSocket, int iLevel, int iOption, void const *pvValue, int cbValue)
+{
+ /*
+ * Validate input.
+ */
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(g_pfnsetsockopt, VERR_NET_NOT_UNSUPPORTED);
+# define setsockopt g_pfnsetsockopt
+#endif
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc = VINF_SUCCESS;
+ if (setsockopt(pThis->hNative, iLevel, iOption, (const char *)pvValue, cbValue) != 0)
+ rc = rtSocketError();
+
+ rtSocketUnlock(pThis);
+#ifdef RT_OS_WINDOWS
+# undef setsockopt
+#endif
+ return rc;
+}
+
+
+/**
+ * Internal RTPollSetAdd helper that returns the handle that should be added to
+ * the pollset.
+ *
+ * @returns Valid handle on success, INVALID_HANDLE_VALUE on failure.
+ * @param hSocket The socket handle.
+ * @param fEvents The events we're polling for.
+ * @param phNative Where to put the primary handle.
+ */
+DECLHIDDEN(int) rtSocketPollGetHandle(RTSOCKET hSocket, uint32_t fEvents, PRTHCINTPTR phNative)
+{
+ RTSOCKETINT *pThis = hSocket;
+ RT_NOREF_PV(fEvents);
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, VERR_INVALID_HANDLE);
+#ifdef RT_OS_WINDOWS
+ AssertReturn(rtSocketTryLock(pThis), VERR_CONCURRENT_ACCESS);
+
+ int rc = VINF_SUCCESS;
+ if (pThis->hEvent != WSA_INVALID_EVENT)
+ *phNative = (RTHCINTPTR)pThis->hEvent;
+ else if (g_pfnWSACreateEvent)
+ {
+ pThis->hEvent = g_pfnWSACreateEvent();
+ *phNative = (RTHCINTPTR)pThis->hEvent;
+ if (pThis->hEvent == WSA_INVALID_EVENT)
+ rc = rtSocketError();
+ }
+ else
+ {
+ AssertCompile(WSA_INVALID_EVENT == (WSAEVENT)NULL);
+ pThis->hEvent = CreateEventW(NULL, TRUE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pwszName*/);
+ *phNative = (RTHCINTPTR)pThis->hEvent;
+ if (pThis->hEvent == WSA_INVALID_EVENT)
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+
+ rtSocketUnlock(pThis);
+ return rc;
+
+#else /* !RT_OS_WINDOWS */
+ *phNative = (RTHCUINTPTR)pThis->hNative;
+ return VINF_SUCCESS;
+#endif /* !RT_OS_WINDOWS */
+}
+
+#ifdef RT_OS_WINDOWS
+
+/**
+ * Fallback poller thread.
+ *
+ * @returns VINF_SUCCESS.
+ * @param hSelf The thread handle.
+ * @param pvUser Socket instance data.
+ */
+static DECLCALLBACK(int) rtSocketPollFallbackThreadProc(RTTHREAD hSelf, void *pvUser)
+{
+ RTSOCKETINT *pThis = (RTSOCKETINT *)pvUser;
+ RT_NOREF(hSelf);
+# define __WSAFDIsSet g_pfn__WSAFDIsSet
+
+ /*
+ * The execution loop.
+ */
+ while (!ASMAtomicReadBool(&pThis->fPollFallbackShutdown))
+ {
+ /*
+ * Do the selecting (with a 15 second timeout because that seems like a good idea).
+ */
+ struct fd_set SetRead;
+ struct fd_set SetWrite;
+ struct fd_set SetXcpt;
+
+ FD_ZERO(&SetRead);
+ FD_ZERO(&SetWrite);
+ FD_ZERO(&SetXcpt);
+
+ FD_SET(pThis->hPollFallbackNotifyR, &SetRead);
+ FD_SET(pThis->hPollFallbackNotifyR, &SetXcpt);
+
+ bool fActive = ASMAtomicReadBool(&pThis->fPollFallbackActive);
+ uint32_t fEvents;
+ if (!fActive)
+ fEvents = 0;
+ else
+ {
+ fEvents = ASMAtomicReadU32(&pThis->fSubscribedEvts);
+ if (fEvents & RTPOLL_EVT_READ)
+ FD_SET(pThis->hNative, &SetRead);
+ if (fEvents & RTPOLL_EVT_WRITE)
+ FD_SET(pThis->hNative, &SetWrite);
+ if (fEvents & RTPOLL_EVT_ERROR)
+ FD_SET(pThis->hNative, &SetXcpt);
+ }
+
+ struct timeval Timeout;
+ Timeout.tv_sec = 15;
+ Timeout.tv_usec = 0;
+ int rc = g_pfnselect(INT_MAX /*ignored*/, &SetRead, &SetWrite, &SetXcpt, &Timeout);
+
+ /* Stop immediately if told to shut down. */
+ if (ASMAtomicReadBool(&pThis->fPollFallbackShutdown))
+ break;
+
+ /*
+ * Process the result.
+ */
+ if (rc > 0)
+ {
+ /* First the socket we're listening on. */
+ if ( fEvents
+ && ( FD_ISSET(pThis->hNative, &SetRead)
+ || FD_ISSET(pThis->hNative, &SetWrite)
+ || FD_ISSET(pThis->hNative, &SetXcpt)) )
+ {
+ ASMAtomicWriteBool(&pThis->fPollFallbackActive, false);
+ SetEvent(pThis->hEvent);
+ }
+
+ /* Then maintain the notification pipe. (We only read one byte here
+ because we're overly paranoid wrt socket switching to blocking mode.) */
+ if (FD_ISSET(pThis->hPollFallbackNotifyR, &SetRead))
+ {
+ char chIgnored;
+ g_pfnrecv(pThis->hPollFallbackNotifyR, &chIgnored, sizeof(chIgnored), MSG_NOSIGNAL);
+ }
+ }
+ else
+ AssertMsg(rc == 0, ("%Rrc\n", rtSocketError()));
+ }
+
+# undef __WSAFDIsSet
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Pokes the fallback thread, making sure it gets out of whatever it's stuck in.
+ *
+ * @param pThis The socket handle.
+ */
+static void rtSocketPokePollFallbackThread(RTSOCKETINT *pThis)
+{
+ Assert(pThis->fPollFallback);
+ if (pThis->hPollFallbackThread != NIL_RTTHREAD)
+ {
+ int cbWritten = g_pfnsend(pThis->hPollFallbackNotifyW, "!", 1, MSG_NOSIGNAL);
+ AssertMsg(cbWritten == 1, ("cbWritten=%d err=%Rrc\n", rtSocketError()));
+ RT_NOREF_PV(cbWritten);
+ }
+}
+
+
+/**
+ * Called by rtSocketPollStart to make the thread start selecting on the socket.
+ *
+ * @returns 0 on success, RTPOLL_EVT_ERROR on failure.
+ * @param pThis The socket handle.
+ */
+static uint32_t rtSocketPollFallbackStart(RTSOCKETINT *pThis)
+{
+ /*
+ * Reset the event and tell the thread to start selecting on the socket.
+ */
+ ResetEvent(pThis->hEvent);
+ ASMAtomicWriteBool(&pThis->fPollFallbackActive, true);
+
+ /*
+ * Wake up the thread the thread.
+ */
+ if (pThis->hPollFallbackThread != NIL_RTTHREAD)
+ rtSocketPokePollFallbackThread(pThis);
+ else
+ {
+ /*
+ * Not running, need to set it up and start it.
+ */
+ AssertLogRelReturn(pThis->hEvent != NULL && pThis->hEvent != INVALID_HANDLE_VALUE, RTPOLL_EVT_ERROR);
+
+ /* Create the notification socket pair. */
+ int rc;
+ if (pThis->hPollFallbackNotifyR == NIL_RTSOCKETNATIVE)
+ {
+ rc = rtSocketCreateNativeTcpPair(&pThis->hPollFallbackNotifyW, &pThis->hPollFallbackNotifyR);
+ AssertLogRelRCReturn(rc, RTPOLL_EVT_ERROR);
+
+ /* Make the read end non-blocking (not fatal). */
+ u_long fNonBlocking = 1;
+ rc = g_pfnioctlsocket(pThis->hPollFallbackNotifyR, FIONBIO, &fNonBlocking);
+ AssertLogRelMsg(rc == 0, ("rc=%#x %Rrc\n", rc, rtSocketError()));
+ }
+
+ /* Finally, start the thread. ASSUME we don't need too much stack. */
+ rc = RTThreadCreate(&pThis->hPollFallbackThread, rtSocketPollFallbackThreadProc, pThis,
+ _128K, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "sockpoll");
+ AssertLogRelRCReturn(rc, RTPOLL_EVT_ERROR);
+ }
+ return 0;
+}
+
+
+/**
+ * Undos the harm done by WSAEventSelect.
+ *
+ * @returns IPRT status code.
+ * @param pThis The socket handle.
+ */
+static int rtSocketPollClearEventAndRestoreBlocking(RTSOCKETINT *pThis)
+{
+ int rc = VINF_SUCCESS;
+ if (pThis->fSubscribedEvts)
+ {
+ if (!pThis->fPollFallback)
+ {
+ Assert(g_pfnWSAEventSelect && g_pfnioctlsocket);
+ if (g_pfnWSAEventSelect && g_pfnioctlsocket)
+ {
+ if (g_pfnWSAEventSelect(pThis->hNative, WSA_INVALID_EVENT, 0) == 0)
+ {
+ pThis->fSubscribedEvts = 0;
+
+ /*
+ * Switch back to blocking mode if that was the state before the
+ * operation.
+ */
+ if (pThis->fBlocking)
+ {
+ u_long fNonBlocking = 0;
+ int rc2 = g_pfnioctlsocket(pThis->hNative, FIONBIO, &fNonBlocking);
+ if (rc2 != 0)
+ {
+ rc = rtSocketError();
+ AssertMsgFailed(("%Rrc; rc2=%d\n", rc, rc2));
+ }
+ }
+ }
+ else
+ {
+ rc = rtSocketError();
+ AssertMsgFailed(("%Rrc\n", rc));
+ }
+ }
+ else
+ {
+ Assert(pThis->fPollFallback);
+ rc = VINF_SUCCESS;
+ }
+ }
+ /*
+ * Just clear the event mask as we never started waiting if we get here.
+ */
+ else
+ ASMAtomicWriteU32(&pThis->fSubscribedEvts, 0);
+ }
+ return rc;
+}
+
+
+/**
+ * Updates the mask of events we're subscribing to.
+ *
+ * @returns IPRT status code.
+ * @param pThis The socket handle.
+ * @param fEvents The events we want to subscribe to.
+ */
+static int rtSocketPollUpdateEvents(RTSOCKETINT *pThis, uint32_t fEvents)
+{
+ if (!pThis->fPollFallback)
+ {
+ LONG fNetworkEvents = 0;
+ if (fEvents & RTPOLL_EVT_READ)
+ fNetworkEvents |= FD_READ;
+ if (fEvents & RTPOLL_EVT_WRITE)
+ fNetworkEvents |= FD_WRITE;
+ if (fEvents & RTPOLL_EVT_ERROR)
+ fNetworkEvents |= FD_CLOSE;
+ LogFlowFunc(("fNetworkEvents=%#x\n", fNetworkEvents));
+
+ if (g_pfnWSAEventSelect(pThis->hNative, pThis->hEvent, fNetworkEvents) == 0)
+ {
+ pThis->fSubscribedEvts = fEvents;
+ return VINF_SUCCESS;
+ }
+
+ int rc = rtSocketError();
+ AssertMsgFailed(("fNetworkEvents=%#x rc=%Rrc\n", fNetworkEvents, rtSocketError()));
+ return rc;
+ }
+
+ /*
+ * Update the events we're waiting for. Caller will poke/start the thread. later
+ */
+ ASMAtomicWriteU32(&pThis->fSubscribedEvts, fEvents);
+ return VINF_SUCCESS;
+}
+
+#endif /* RT_OS_WINDOWS */
+
+
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+
+/**
+ * Checks for pending events.
+ *
+ * @returns Event mask or 0.
+ * @param pThis The socket handle.
+ * @param fEvents The desired events.
+ */
+static uint32_t rtSocketPollCheck(RTSOCKETINT *pThis, uint32_t fEvents)
+{
+ uint32_t fRetEvents = 0;
+
+ LogFlowFunc(("pThis=%#p fEvents=%#x\n", pThis, fEvents));
+
+# ifdef RT_OS_WINDOWS
+ /* Make sure WSAEnumNetworkEvents returns what we want. */
+ int rc = VINF_SUCCESS;
+ if ((pThis->fSubscribedEvts & fEvents) != fEvents)
+ rc = rtSocketPollUpdateEvents(pThis, pThis->fSubscribedEvts | fEvents);
+
+ if (!pThis->fPollFallback)
+ {
+ /* Atomically get pending events and reset the event semaphore. */
+ Assert(g_pfnWSAEnumNetworkEvents);
+ WSANETWORKEVENTS NetEvts;
+ RT_ZERO(NetEvts);
+ if (g_pfnWSAEnumNetworkEvents(pThis->hNative, pThis->hEvent, &NetEvts) == 0)
+ {
+ if ( (NetEvts.lNetworkEvents & FD_READ)
+ && NetEvts.iErrorCode[FD_READ_BIT] == 0)
+ fRetEvents |= RTPOLL_EVT_READ;
+
+ if ( (NetEvts.lNetworkEvents & FD_WRITE)
+ && NetEvts.iErrorCode[FD_WRITE_BIT] == 0)
+ fRetEvents |= RTPOLL_EVT_WRITE;
+
+ if (NetEvts.lNetworkEvents & FD_CLOSE)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ else
+ for (uint32_t i = 0; i < FD_MAX_EVENTS; i++)
+ if ( (NetEvts.lNetworkEvents & (1L << i))
+ && NetEvts.iErrorCode[i] != 0)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+
+ pThis->fEventsSaved = fRetEvents |= pThis->fEventsSaved;
+ fRetEvents &= fEvents | RTPOLL_EVT_ERROR;
+ }
+ else
+ rc = rtSocketError();
+ }
+
+ /* Fall back on select if we hit an error above or is using fallback polling. */
+ if (pThis->fPollFallback || RT_FAILURE(rc))
+ {
+ rc = rtSocketSelectOneEx(pThis, fEvents & RTPOLL_EVT_ERROR ? fEvents | RTPOLL_EVT_READ : fEvents, &fRetEvents, 0);
+ if (RT_SUCCESS(rc))
+ {
+ /* rtSocketSelectOneEx may return RTPOLL_EVT_READ on disconnect. Use
+ getpeername to fix this. */
+ if ((fRetEvents & (RTPOLL_EVT_READ | RTPOLL_EVT_ERROR)) == RTPOLL_EVT_READ)
+ {
+# if 0 /* doens't work */
+ rtSocketErrorReset();
+ char chIgn;
+ rc = g_pfnrecv(pThis->hNative, &chIgn, 0, MSG_NOSIGNAL);
+ rc = rtSocketError();
+ if (RT_FAILURE(rc))
+ fRetEvents |= RTPOLL_EVT_ERROR;
+
+ rc = g_pfnsend(pThis->hNative, &chIgn, 0, MSG_NOSIGNAL);
+ rc = rtSocketError();
+ if (RT_FAILURE(rc))
+ fRetEvents |= RTPOLL_EVT_ERROR;
+
+ RTSOCKADDRUNION u;
+ int cbAddr = sizeof(u);
+ if (g_pfngetpeername(pThis->hNative, &u.Addr, &cbAddr) == SOCKET_ERROR)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+# endif
+ /* If no bytes are available, assume error condition. */
+ u_long cbAvail = 0;
+ rc = g_pfnioctlsocket(pThis->hNative, FIONREAD, &cbAvail);
+ if (rc == 0 && cbAvail == 0)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ }
+ fRetEvents &= fEvents | RTPOLL_EVT_ERROR;
+ }
+ else if (rc == VERR_TIMEOUT)
+ fRetEvents = 0;
+ else
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ }
+
+# else /* RT_OS_OS2 */
+ int aFds[4] = { pThis->hNative, pThis->hNative, pThis->hNative, -1 };
+ int rc = os2_select(aFds, 1, 1, 1, 0);
+ if (rc > 0)
+ {
+ if (aFds[0] == pThis->hNative)
+ fRetEvents |= RTPOLL_EVT_READ;
+ if (aFds[1] == pThis->hNative)
+ fRetEvents |= RTPOLL_EVT_WRITE;
+ if (aFds[2] == pThis->hNative)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ fRetEvents &= fEvents;
+ }
+# endif /* RT_OS_OS2 */
+
+ LogFlowFunc(("fRetEvents=%#x\n", fRetEvents));
+ return fRetEvents;
+}
+
+
+/**
+ * Internal RTPoll helper that polls the socket handle and, if @a fNoWait is
+ * clear, starts whatever actions we've got running during the poll call.
+ *
+ * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
+ * Event mask (in @a fEvents) and no actions if the handle is ready
+ * already.
+ * UINT32_MAX (asserted) if the socket handle is busy in I/O or a
+ * different poll set.
+ *
+ * @param hSocket The socket handle.
+ * @param hPollSet The poll set handle (for access checks).
+ * @param fEvents The events we're polling for.
+ * @param fFinalEntry Set if this is the final entry for this handle
+ * in this poll set. This can be used for dealing
+ * with duplicate entries.
+ * @param fNoWait Set if it's a zero-wait poll call. Clear if
+ * we'll wait for an event to occur.
+ *
+ * @remarks There is a potential race wrt duplicate handles when @a fNoWait is
+ * @c true, we don't currently care about that oddity...
+ */
+DECLHIDDEN(uint32_t) rtSocketPollStart(RTSOCKET hSocket, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
+{
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, UINT32_MAX);
+ /** @todo This isn't quite sane. Replace by critsect and open up concurrent
+ * reads and writes! */
+ if (rtSocketTryLock(pThis))
+ pThis->hPollSet = hPollSet;
+ else
+ {
+ AssertReturn(pThis->hPollSet == hPollSet, UINT32_MAX);
+ ASMAtomicIncU32(&pThis->cUsers);
+ }
+
+ /* (rtSocketPollCheck will reset the event object). */
+# ifdef RT_OS_WINDOWS
+ uint32_t fRetEvents = pThis->fEventsSaved;
+ pThis->fEventsSaved = 0; /* Reset */
+ fRetEvents |= rtSocketPollCheck(pThis, fEvents);
+
+ if ( !fRetEvents
+ && !fNoWait)
+ {
+ pThis->fPollEvts |= fEvents;
+ if (fFinalEntry)
+ {
+ if (pThis->fSubscribedEvts != pThis->fPollEvts)
+ {
+ /** @todo seems like there might be a call to many here and that fPollEvts is
+ * totally unnecessary... (bird) */
+ int rc = rtSocketPollUpdateEvents(pThis, pThis->fPollEvts);
+ if (RT_FAILURE(rc))
+ {
+ pThis->fPollEvts = 0;
+ fRetEvents = UINT32_MAX;
+ }
+ }
+
+ /* Make sure we don't block when there are events pending relevant to an earlier poll set entry. */
+ if (pThis->fEventsSaved && !pThis->fPollFallback && g_pfnWSASetEvent && fRetEvents == 0)
+ g_pfnWSASetEvent(pThis->hEvent);
+ }
+ }
+# else
+ uint32_t fRetEvents = rtSocketPollCheck(pThis, fEvents);
+# endif
+
+ if (fRetEvents || fNoWait)
+ {
+ if (pThis->cUsers == 1)
+ {
+# ifdef RT_OS_WINDOWS
+ pThis->fEventsSaved &= RTPOLL_EVT_ERROR;
+ pThis->fHarvestedEvents = false;
+ rtSocketPollClearEventAndRestoreBlocking(pThis);
+# endif
+ pThis->hPollSet = NIL_RTPOLLSET;
+ }
+# ifdef RT_OS_WINDOWS
+ else
+ pThis->fHarvestedEvents = true;
+# endif
+ ASMAtomicDecU32(&pThis->cUsers);
+ }
+# ifdef RT_OS_WINDOWS
+ /*
+ * Kick the poller thread on if this is the final entry and we're in
+ * winsock 1.x fallback mode.
+ */
+ else if (pThis->fPollFallback && fFinalEntry)
+ fRetEvents = rtSocketPollFallbackStart(pThis);
+# endif
+
+ return fRetEvents;
+}
+
+
+/**
+ * Called after a WaitForMultipleObjects returned in order to check for pending
+ * events and stop whatever actions that rtSocketPollStart() initiated.
+ *
+ * @returns Event mask or 0.
+ *
+ * @param hSocket The socket handle.
+ * @param fEvents The events we're polling for.
+ * @param fFinalEntry Set if this is the final entry for this handle
+ * in this poll set. This can be used for dealing
+ * with duplicate entries. Only keep in mind that
+ * this method is called in reverse order, so the
+ * first call will have this set (when the entire
+ * set was processed).
+ * @param fHarvestEvents Set if we should check for pending events.
+ */
+DECLHIDDEN(uint32_t) rtSocketPollDone(RTSOCKET hSocket, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
+{
+ RTSOCKETINT *pThis = hSocket;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTSOCKET_MAGIC, 0);
+ Assert(pThis->cUsers > 0);
+ Assert(pThis->hPollSet != NIL_RTPOLLSET);
+ RT_NOREF_PV(fFinalEntry);
+
+# ifdef RT_OS_WINDOWS
+ /*
+ * Deactivate the poll thread if we're in winsock 1.x fallback poll mode.
+ */
+ if ( pThis->fPollFallback
+ && pThis->hPollFallbackThread != NIL_RTTHREAD)
+ {
+ ASMAtomicWriteU32(&pThis->fSubscribedEvts, 0);
+ if (ASMAtomicXchgBool(&pThis->fPollFallbackActive, false))
+ rtSocketPokePollFallbackThread(pThis);
+ }
+# endif
+
+ /*
+ * Harvest events and clear the event mask for the next round of polling.
+ */
+ uint32_t fRetEvents;
+# ifdef RT_OS_WINDOWS
+ if (!pThis->fPollFallback)
+ {
+ if (!pThis->fHarvestedEvents)
+ {
+ fRetEvents = rtSocketPollCheck(pThis, fEvents);
+ pThis->fHarvestedEvents = true;
+ }
+ else
+ fRetEvents = pThis->fEventsSaved;
+ if (fHarvestEvents)
+ fRetEvents &= fEvents;
+ else
+ fRetEvents = 0;
+ pThis->fPollEvts = 0;
+ }
+ else
+# endif
+ {
+ if (fHarvestEvents)
+ fRetEvents = rtSocketPollCheck(pThis, fEvents);
+ else
+ fRetEvents = 0;
+ }
+
+ /*
+ * Make the socket blocking again and unlock the handle.
+ */
+ if (pThis->cUsers == 1)
+ {
+# ifdef RT_OS_WINDOWS
+ pThis->fEventsSaved &= RTPOLL_EVT_ERROR;
+ pThis->fHarvestedEvents = false;
+ rtSocketPollClearEventAndRestoreBlocking(pThis);
+# endif
+ pThis->hPollSet = NIL_RTPOLLSET;
+ }
+ ASMAtomicDecU32(&pThis->cUsers);
+ return fRetEvents;
+}
+
+#endif /* RT_OS_WINDOWS || RT_OS_OS2 */
+
diff --git a/src/VBox/Runtime/r3/solaris/Makefile.kup b/src/VBox/Runtime/r3/solaris/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/Makefile.kup
diff --git a/src/VBox/Runtime/r3/solaris/RTFileQuerySectorSize-solaris.cpp b/src/VBox/Runtime/r3/solaris/RTFileQuerySectorSize-solaris.cpp
new file mode 100644
index 00000000..4620b71c
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/RTFileQuerySectorSize-solaris.cpp
@@ -0,0 +1,90 @@
+/* $Id: RTFileQuerySectorSize-solaris.cpp $ */
+/** @file
+ * IPRT - RTFileQuerySectorSize, Solaris.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#include <errno.h>
+#include <stropts.h>
+#include <sys/types.h>
+#include <sys/dkio.h>
+#include <sys/vtoc.h>
+#include <sys/stat.h>
+
+
+
+RTDECL(int) RTFileQuerySectorSize(RTFILE hFile, uint32_t *pcbSector)
+{
+ AssertPtrReturn(pcbSector, VERR_INVALID_PARAMETER);
+
+ int rc;
+ int const fd = (int)RTFileToNative(hFile);
+ struct stat DevStat = { 0 };
+ if (!fstat(fd, &DevStat))
+ {
+ if (S_ISBLK(DevStat.st_mode) || S_ISCHR(DevStat.st_mode))
+ {
+ struct dk_minfo MediaInfo;
+ if (!ioctl(fd, DKIOCGMEDIAINFO, &MediaInfo))
+ {
+ AssertReturn(MediaInfo.dki_lbsize > 0, VERR_INVALID_FUNCTION);
+ *pcbSector = MediaInfo.dki_lbsize;
+ return VINF_SUCCESS;
+ }
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("ioctl failed: errno=%d / %Rrc\n", errno, rc));
+ }
+ else
+ {
+ AssertMsgFailed(("not a block or character device.\n"));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ AssertMsgFailed(("fstat failed: errno=%d / %Rrc\n", errno, rc));
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/solaris/RTSystemFirmware-solaris.cpp b/src/VBox/Runtime/r3/solaris/RTSystemFirmware-solaris.cpp
new file mode 100644
index 00000000..d986e9f2
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/RTSystemFirmware-solaris.cpp
@@ -0,0 +1,78 @@
+/* $Id: RTSystemFirmware-solaris.cpp $ */
+/** @file
+ * IPRT - System firmware information for Solaris.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/system.h>
+#include <iprt/errcore.h>
+
+#include <libdevinfo.h>
+
+
+RTDECL(int) RTSystemQueryFirmwareType(PRTSYSFWTYPE penmFirmwareType)
+{
+ di_node_t di_node;
+ di_node = di_init("/", DINFOSUBTREE|DINFOPROP);
+ if (di_node == DI_NODE_NIL)
+ {
+ *penmFirmwareType = RTSYSFWTYPE_INVALID;
+ return VERR_NOT_SUPPORTED;
+ }
+
+ int64_t *efiprop;
+ int rc = di_prop_lookup_int64(DDI_DEV_T_ANY, di_node, "efi-systab", &efiprop);
+ if (rc == -1)
+ *penmFirmwareType = RTSYSFWTYPE_BIOS;
+ else
+ *penmFirmwareType = RTSYSFWTYPE_UEFI;
+
+ di_fini(di_node);
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSystemQueryFirmwareType);
+
+
+RTDECL(int) RTSystemQueryFirmwareBoolean(RTSYSFWBOOL enmBoolean, bool *pfValue)
+{
+ RT_NOREF(enmBoolean, pfValue);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTSystemQueryFirmwareBoolean);
+
diff --git a/src/VBox/Runtime/r3/solaris/RTSystemQueryDmiString-solaris.cpp b/src/VBox/Runtime/r3/solaris/RTSystemQueryDmiString-solaris.cpp
new file mode 100644
index 00000000..8e62a7a0
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/RTSystemQueryDmiString-solaris.cpp
@@ -0,0 +1,117 @@
+/* $Id: RTSystemQueryDmiString-solaris.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryDmiString, solaris ring-3.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include <smbios.h>
+#include <errno.h>
+
+
+RTDECL(int) RTSystemQueryDmiString(RTSYSDMISTR enmString, char *pszBuf, size_t cbBuf)
+{
+ AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbBuf > 0, VERR_INVALID_PARAMETER);
+ *pszBuf = '\0';
+ AssertReturn(enmString > RTSYSDMISTR_INVALID && enmString < RTSYSDMISTR_END, VERR_INVALID_PARAMETER);
+
+ int rc = VERR_NOT_SUPPORTED;
+ int err = 0;
+ smbios_hdl_t *pSMB = smbios_open(NULL /* default fd */, SMB_VERSION, 0 /* flags */, &err);
+ if (pSMB)
+ {
+ smbios_system_t hSMBSys;
+ id_t hSMBId = smbios_info_system(pSMB, &hSMBSys);
+ if (hSMBId != SMB_ERR)
+ {
+ /* Don't need the common bits for the product UUID. */
+ if (enmString == RTSYSDMISTR_PRODUCT_UUID)
+ {
+ static char const s_szHex[17] = "0123456789ABCDEF";
+ char szData[64];
+ char *pszData = szData;
+ unsigned cchUuid = RT_MIN(hSMBSys.smbs_uuidlen, sizeof(szData) - 1);
+ for (unsigned i = 0; i < cchUuid; i++)
+ {
+ *pszData++ = s_szHex[hSMBSys.smbs_uuid[i] >> 4];
+ *pszData++ = s_szHex[hSMBSys.smbs_uuid[i] & 0xf];
+ if (i == 3 || i == 5 || i == 7 || i == 9)
+ *pszData++ = '-';
+ }
+ *pszData = '\0';
+ rc = RTStrCopy(pszBuf, cbBuf, szData);
+ smbios_close(pSMB);
+ return rc;
+ }
+
+ smbios_info_t hSMBInfo;
+ id_t hSMBInfoId = smbios_info_common(pSMB, hSMBId, &hSMBInfo);
+ if (hSMBInfoId != SMB_ERR)
+ {
+ switch (enmString)
+ {
+ case RTSYSDMISTR_PRODUCT_NAME: rc = RTStrCopy(pszBuf, cbBuf, hSMBInfo.smbi_product); break;
+ case RTSYSDMISTR_PRODUCT_VERSION: rc = RTStrCopy(pszBuf, cbBuf, hSMBInfo.smbi_version); break;
+ case RTSYSDMISTR_PRODUCT_SERIAL: rc = RTStrCopy(pszBuf, cbBuf, hSMBInfo.smbi_serial); break;
+ case RTSYSDMISTR_MANUFACTURER: rc = RTStrCopy(pszBuf, cbBuf, hSMBInfo.smbi_manufacturer); break;
+
+ default: /* make gcc happy */
+ rc = VERR_NOT_SUPPORTED;
+ }
+ smbios_close(pSMB);
+ return rc;
+ }
+ }
+
+ /* smbios_* error path. */
+ err = smbios_errno(pSMB);
+ smbios_close(pSMB);
+ }
+
+ /* Do some error conversion. */
+ if (err == EPERM || err == EACCES)
+ rc = VERR_ACCESS_DENIED;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/solaris/RTSystemShutdown-solaris.cpp b/src/VBox/Runtime/r3/solaris/RTSystemShutdown-solaris.cpp
new file mode 100644
index 00000000..163b608f
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/RTSystemShutdown-solaris.cpp
@@ -0,0 +1,112 @@
+/* $Id: RTSystemShutdown-solaris.cpp $ */
+/** @file
+ * IPRT - RTSystemShutdown, linux implementation.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTSystemShutdown(RTMSINTERVAL cMsDelay, uint32_t fFlags, const char *pszLogMsg)
+{
+ AssertPtrReturn(pszLogMsg, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTSYSTEM_SHUTDOWN_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Assemble the argument vector.
+ */
+ int iArg = 0;
+ const char *apszArgs[8];
+
+ RT_BZERO(apszArgs, sizeof(apszArgs));
+
+ apszArgs[iArg++] = "/usr/sbin/shutdown";
+ apszArgs[iArg++] = "-y"; /* Pre-answer confirmation question. */
+ apszArgs[iArg++] = "-i"; /* Change to the following state. */
+ switch (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK)
+ {
+ case RTSYSTEM_SHUTDOWN_HALT:
+ apszArgs[iArg++] = "0";
+ break;
+ case RTSYSTEM_SHUTDOWN_REBOOT:
+ apszArgs[iArg++] = "6";
+ break;
+ case RTSYSTEM_SHUTDOWN_POWER_OFF:
+ case RTSYSTEM_SHUTDOWN_POWER_OFF_HALT:
+ apszArgs[iArg++] = "5";
+ break;
+ }
+
+ apszArgs[iArg++] = "-g"; /* Grace period. */
+
+ char szWhen[80];
+ if (cMsDelay < 500)
+ strcpy(szWhen, "0");
+ else
+ RTStrPrintf(szWhen, sizeof(szWhen), "%u", (unsigned)((cMsDelay + 499) / 1000));
+ apszArgs[iArg++] = szWhen;
+
+ apszArgs[iArg++] = pszLogMsg;
+
+
+ /*
+ * Start the shutdown process and wait for it to complete.
+ */
+ RTPROCESS hProc;
+ int rc = RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProc);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ RTPROCSTATUS ProcStatus;
+ rc = RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus);
+ if (RT_SUCCESS(rc))
+ {
+ if ( ProcStatus.enmReason != RTPROCEXITREASON_NORMAL
+ || ProcStatus.iStatus != 0)
+ rc = VERR_SYS_SHUTDOWN_FAILED;
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/solaris/coredumper-solaris.cpp b/src/VBox/Runtime/r3/solaris/coredumper-solaris.cpp
new file mode 100644
index 00000000..d1a24b2c
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/coredumper-solaris.cpp
@@ -0,0 +1,2375 @@
+/* $Id: coredumper-solaris.cpp $ */
+/** @file
+ * IPRT - Custom Core Dumper, Solaris.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/coredumper.h>
+
+#include <iprt/asm.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include "coredumper-solaris.h"
+
+#ifdef RT_OS_SOLARIS
+# include <syslog.h>
+# include <signal.h>
+# include <stdlib.h>
+# include <unistd.h>
+# include <errno.h>
+# include <zone.h>
+# include <sys/proc.h>
+# include <sys/sysmacros.h>
+# include <sys/systeminfo.h>
+# include <sys/mman.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+# include <ucontext.h>
+#endif /* RT_OS_SOLARIS */
+
+#include <iprt/formats/elf.h>
+#include <iprt/formats/elf64.h>
+
+
+/*********************************************************************************************************************************
+* Globals *
+*********************************************************************************************************************************/
+static RTNATIVETHREAD volatile g_CoreDumpThread = NIL_RTNATIVETHREAD;
+static bool volatile g_fCoreDumpSignalSetup = false;
+static uint32_t volatile g_fCoreDumpFlags = 0;
+static char g_szCoreDumpDir[PATH_MAX] = { 0 };
+static char g_szCoreDumpFile[PATH_MAX] = { 0 };
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define CORELOG_NAME "CoreDumper: "
+#define CORELOG(a) Log(a)
+#define CORELOGRELSYS(a) \
+ do { \
+ rtCoreDumperSysLogWrapper a; \
+ } while (0)
+
+
+/**
+ * ELFNOTEHDR: ELF NOTE header.
+ */
+typedef struct ELFNOTEHDR
+{
+ Elf64_Nhdr Hdr; /* Header of NOTE section */
+ char achName[8]; /* Name of NOTE section */
+} ELFNOTEHDR;
+typedef ELFNOTEHDR *PELFNOTEHDR;
+
+/**
+ * Wrapper function to write IPRT format style string to the syslog.
+ *
+ * @param pszFormat Format string
+ */
+static void rtCoreDumperSysLogWrapper(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ char szBuf[1024];
+ RTStrPrintfV(szBuf, sizeof(szBuf), pszFormat, va);
+ va_end(va);
+ syslog(LOG_ERR, "%s", szBuf);
+}
+
+
+/**
+ * Determines endianness of the system. Just for completeness.
+ *
+ * @return Will return false if system is little endian, true otherwise.
+ */
+static bool IsBigEndian()
+{
+ const int i = 1;
+ char *p = (char *)&i;
+ if (p[0] == 1)
+ return false;
+ return true;
+}
+
+
+/**
+ * Reads from a file making sure an interruption doesn't cause a failure.
+ *
+ * @param fd Handle to the file to read.
+ * @param pv Where to store the read data.
+ * @param cbToRead Size of data to read.
+ *
+ * @return IPRT status code.
+ */
+static int ReadFileNoIntr(int fd, void *pv, size_t cbToRead)
+{
+ for (;;)
+ {
+ ssize_t cbRead = read(fd, pv, cbToRead);
+ if (cbRead < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return RTErrConvertFromErrno(errno);
+ }
+ if ((size_t)cbRead == cbToRead)
+ return VINF_SUCCESS;
+ if ((size_t)cbRead > cbToRead)
+ return VERR_INTERNAL_ERROR_3;
+ if (cbRead == 0)
+ return VERR_EOF;
+ pv = (uint8_t *)pv + cbRead;
+ cbToRead -= cbRead;
+ }
+}
+
+
+/**
+ * Writes to a file making sure an interruption doesn't cause a failure.
+ *
+ * @param fd Handle to the file to write to.
+ * @param pv Pointer to what to write.
+ * @param cbToWrite Size of data to write.
+ *
+ * @return IPRT status code.
+ */
+static int WriteFileNoIntr(int fd, const void *pv, size_t cbToWrite)
+{
+ for (;;)
+ {
+ ssize_t cbWritten = write(fd, pv, cbToWrite);
+ if (cbWritten < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return RTErrConvertFromErrno(errno);
+ }
+ if ((size_t)cbWritten == cbToWrite)
+ return VINF_SUCCESS;
+ if ((size_t)cbWritten > cbToWrite)
+ return VERR_INTERNAL_ERROR_2;
+ pv = (uint8_t const *)pv + cbWritten;
+ cbToWrite -= cbWritten;
+ }
+}
+
+
+/**
+ * Read from a given offset in the process' address space.
+ *
+ * @param pSolProc Pointer to the solaris process.
+ * @param off The offset to read from.
+ * @param pvBuf Where to read the data into.
+ * @param cbToRead Number of bytes to read.
+ *
+ * @return VINF_SUCCESS, if all the given bytes was read in, otherwise VERR_READ_ERROR.
+ */
+static ssize_t ProcReadAddrSpace(PRTSOLCOREPROCESS pSolProc, RTFOFF off, void *pvBuf, size_t cbToRead)
+{
+ for (;;)
+ {
+ ssize_t cbRead = pread(pSolProc->fdAs, pvBuf, cbToRead, off);
+ if (cbRead < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return RTErrConvertFromErrno(errno);
+ }
+ if ((size_t)cbRead == cbToRead)
+ return VINF_SUCCESS;
+ if ((size_t)cbRead > cbToRead)
+ return VERR_INTERNAL_ERROR_4;
+ if (cbRead == 0)
+ return VERR_EOF;
+
+ pvBuf = (uint8_t *)pvBuf + cbRead;
+ cbToRead -= cbRead;
+ off += cbRead;
+ }
+}
+
+
+/**
+ * Determines if the current process' architecture is suitable for dumping core.
+ *
+ * @param pSolProc Pointer to the solaris process.
+ *
+ * @return true if the architecture matches the current one.
+ */
+static inline bool IsProcessArchNative(PRTSOLCOREPROCESS pSolProc)
+{
+ psinfo_t *pProcInfo = (psinfo_t *)pSolProc->pvProcInfo;
+ return pProcInfo->pr_dmodel == PR_MODEL_NATIVE;
+}
+
+
+/**
+ * Helper function to get the size_t compatible file size from a file
+ * descriptor.
+ *
+ * @return The file size (in bytes).
+ * @param fd The file descriptor.
+ */
+static size_t GetFileSizeByFd(int fd)
+{
+ struct stat st;
+ if (fstat(fd, &st) == 0)
+ {
+ if (st.st_size <= 0)
+ return 0;
+ size_t cbFile = (size_t)st.st_size;
+ return (off_t)cbFile == st.st_size ? cbFile : ~(size_t)0;
+ }
+
+ CORELOGRELSYS((CORELOG_NAME "GetFileSizeByFd: fstat failed rc=%Rrc\n", RTErrConvertFromErrno(errno)));
+ return 0;
+}
+
+
+/**
+ * Helper function to get the size_t compatible size of a file given its path.
+ *
+ * @return The file size (in bytes).
+ * @param pszPath Pointer to the full path of the file.
+ */
+static size_t GetFileSizeByName(const char *pszPath)
+{
+ int fd = open(pszPath, O_RDONLY);
+ if (fd < 0)
+ {
+ CORELOGRELSYS((CORELOG_NAME "GetFileSizeByName: failed to open %s rc=%Rrc\n", pszPath, RTErrConvertFromErrno(errno)));
+ return 0;
+ }
+
+ size_t cb = GetFileSizeByFd(fd);
+ close(fd);
+ return cb;
+}
+
+
+/**
+ * Pre-compute and pre-allocate sufficient memory for dumping core.
+ * This is meant to be called once, as a single-large anonymously
+ * mapped memory area which will be used during the core dumping routines.
+ *
+ * @param pSolCore Pointer to the core object.
+ *
+ * @return IPRT status code.
+ */
+static int AllocMemoryArea(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore->pvCore == NULL, VERR_ALREADY_EXISTS);
+
+ static struct
+ {
+ const char *pszFilePath; /* Proc based path */
+ size_t cbHeader; /* Size of header */
+ size_t cbEntry; /* Size of each entry in file */
+ size_t cbAccounting; /* Size of each accounting entry per entry */
+ } const s_aPreAllocTable[] =
+ {
+ { "/proc/%d/psinfo", 0, 0, 0 },
+ { "/proc/%d/map", 0, sizeof(prmap_t), sizeof(RTSOLCOREMAPINFO) },
+ { "/proc/%d/auxv", 0, 0, 0 },
+ { "/proc/%d/lpsinfo", sizeof(prheader_t), sizeof(lwpsinfo_t), sizeof(RTSOLCORETHREADINFO) },
+ { "/proc/%d/lstatus", 0, 0, 0 },
+ { "/proc/%d/ldt", 0, 0, 0 },
+ { "/proc/%d/cred", sizeof(prcred_t), sizeof(gid_t), 0 },
+ { "/proc/%d/priv", sizeof(prpriv_t), sizeof(priv_chunk_t), 0 },
+ };
+
+ size_t cb = 0;
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aPreAllocTable); i++)
+ {
+ char szPath[PATH_MAX];
+ RTStrPrintf(szPath, sizeof(szPath), s_aPreAllocTable[i].pszFilePath, (int)pSolCore->SolProc.Process);
+ size_t cbFile = GetFileSizeByName(szPath);
+ cb += cbFile;
+ if ( cbFile > 0
+ && s_aPreAllocTable[i].cbEntry > 0)
+ {
+ cb += ((cbFile - s_aPreAllocTable[i].cbHeader) / s_aPreAllocTable[i].cbEntry)
+ * (s_aPreAllocTable[i].cbAccounting > 0 ? s_aPreAllocTable[i].cbAccounting : 1);
+ cb += s_aPreAllocTable[i].cbHeader;
+ }
+ }
+
+ /*
+ * Make room for our own mapping accountant entry which will also be included in the core.
+ */
+ cb += sizeof(RTSOLCOREMAPINFO);
+
+ /*
+ * Allocate the required space, plus some extra room.
+ */
+ cb += _128K;
+ void *pv = mmap(NULL, cb, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1 /* fd */, 0 /* offset */);
+ if (pv != MAP_FAILED)
+ {
+ CORELOG((CORELOG_NAME "AllocMemoryArea: memory area of %u bytes allocated.\n", cb));
+ pSolCore->pvCore = pv;
+ pSolCore->pvFree = pv;
+ pSolCore->cbCore = cb;
+ return VINF_SUCCESS;
+ }
+ CORELOGRELSYS((CORELOG_NAME "AllocMemoryArea: failed cb=%u\n", cb));
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Free memory area used by the core object.
+ *
+ * @param pSolCore Pointer to the core object.
+ */
+static void FreeMemoryArea(PRTSOLCORE pSolCore)
+{
+ AssertReturnVoid(pSolCore);
+ AssertReturnVoid(pSolCore->pvCore);
+ AssertReturnVoid(pSolCore->cbCore > 0);
+
+ munmap(pSolCore->pvCore, pSolCore->cbCore);
+ CORELOG((CORELOG_NAME "FreeMemoryArea: memory area of %u bytes freed.\n", pSolCore->cbCore));
+
+ pSolCore->pvCore = NULL;
+ pSolCore->pvFree= NULL;
+ pSolCore->cbCore = 0;
+}
+
+
+/**
+ * Get a chunk from the area of allocated memory.
+ *
+ * @param pSolCore Pointer to the core object.
+ * @param cb Size of requested chunk.
+ *
+ * @return Pointer to allocated memory, or NULL on failure.
+ */
+static void *GetMemoryChunk(PRTSOLCORE pSolCore, size_t cb)
+{
+ AssertReturn(pSolCore, NULL);
+ AssertReturn(pSolCore->pvCore, NULL);
+ AssertReturn(pSolCore->pvFree, NULL);
+
+ size_t cbAllocated = (char *)pSolCore->pvFree - (char *)pSolCore->pvCore;
+ if (cbAllocated < pSolCore->cbCore)
+ {
+ char *pb = (char *)pSolCore->pvFree;
+ pSolCore->pvFree = pb + cb;
+ return pb;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Reads the proc file's content into a newly allocated buffer.
+ *
+ * @param pSolCore Pointer to the core object.
+ * @param pszProcFileName Only the name of the file to read from
+ * (/proc/\<pid\> will be prepended)
+ * @param ppv Where to store the allocated buffer.
+ * @param pcb Where to store size of the buffer.
+ *
+ * @return IPRT status code. If the proc file is 0 bytes, VINF_SUCCESS is
+ * returned with pointed to values of @c ppv, @c pcb set to NULL and 0
+ * respectively.
+ */
+static int ProcReadFileInto(PRTSOLCORE pSolCore, const char *pszProcFileName, void **ppv, size_t *pcb)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ char szPath[PATH_MAX];
+ RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/%s", (int)pSolCore->SolProc.Process, pszProcFileName);
+ int rc = VINF_SUCCESS;
+ int fd = open(szPath, O_RDONLY);
+ if (fd >= 0)
+ {
+ *pcb = GetFileSizeByFd(fd);
+ if (*pcb > 0)
+ {
+ *ppv = GetMemoryChunk(pSolCore, *pcb);
+ if (*ppv)
+ rc = ReadFileNoIntr(fd, *ppv, *pcb);
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ *pcb = 0;
+ *ppv = NULL;
+ rc = VINF_SUCCESS;
+ }
+ close(fd);
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(fd);
+ CORELOGRELSYS((CORELOG_NAME "ProcReadFileInto: failed to open %s. rc=%Rrc\n", szPath, rc));
+ }
+ return rc;
+}
+
+
+/**
+ * Read process information (format psinfo_t) from /proc.
+ *
+ * @param pSolCore Pointer to the core object.
+ *
+ * @return IPRT status code.
+ */
+static int ProcReadInfo(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ return ProcReadFileInto(pSolCore, "psinfo", &pSolProc->pvProcInfo, &pSolProc->cbProcInfo);
+}
+
+
+/**
+ * Read process status (format pstatus_t) from /proc.
+ *
+ * @param pSolCore Pointer to the core object.
+ *
+ * @return IPRT status code.
+ */
+static int ProcReadStatus(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+
+ char szPath[PATH_MAX];
+ int rc = VINF_SUCCESS;
+
+ RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/status", (int)pSolProc->Process);
+ int fd = open(szPath, O_RDONLY);
+ if (fd >= 0)
+ {
+ size_t cbProcStatus = sizeof(pstatus_t);
+ AssertCompile(sizeof(pstatus_t) == sizeof(pSolProc->ProcStatus));
+ rc = ReadFileNoIntr(fd, &pSolProc->ProcStatus, cbProcStatus);
+ close(fd);
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(fd);
+ CORELOGRELSYS((CORELOG_NAME "ProcReadStatus: failed to open %s. rc=%Rrc\n", szPath, rc));
+ }
+ return rc;
+}
+
+
+/**
+ * Read process credential information (format prcred_t + array of guid_t)
+ *
+ * @return IPRT status code.
+ * @param pSolCore Pointer to the core object.
+ *
+ * @remarks Should not be called before successful call to @see AllocMemoryArea()
+ */
+static int ProcReadCred(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ return ProcReadFileInto(pSolCore, "cred", &pSolProc->pvCred, &pSolProc->cbCred);
+}
+
+
+/**
+ * Read process privilege information (format prpriv_t + array of priv_chunk_t)
+ *
+ * @return IPRT status code.
+ * @param pSolCore Pointer to the core object.
+ *
+ * @remarks Should not be called before successful call to @see AllocMemoryArea()
+ */
+static int ProcReadPriv(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ int rc = ProcReadFileInto(pSolCore, "priv", (void **)&pSolProc->pPriv, &pSolProc->cbPriv);
+ if (RT_FAILURE(rc))
+ return rc;
+ pSolProc->pcPrivImpl = getprivimplinfo();
+ if (!pSolProc->pcPrivImpl)
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadPriv: getprivimplinfo returned NULL.\n"));
+ return VERR_INVALID_STATE;
+ }
+ return rc;
+}
+
+
+/**
+ * Read process LDT information (format array of struct ssd) from /proc.
+ *
+ * @return IPRT status code.
+ * @param pSolCore Pointer to the core object.
+ *
+ * @remarks Should not be called before successful call to @see AllocMemoryArea()
+ */
+static int ProcReadLdt(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ return ProcReadFileInto(pSolCore, "ldt", &pSolProc->pvLdt, &pSolProc->cbLdt);
+}
+
+
+/**
+ * Read process auxiliary vectors (format auxv_t) for the process.
+ *
+ * @return IPRT status code.
+ * @param pSolCore Pointer to the core object.
+ *
+ * @remarks Should not be called before successful call to @see AllocMemoryArea()
+ */
+static int ProcReadAuxVecs(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ char szPath[PATH_MAX];
+ int rc = VINF_SUCCESS;
+ RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/auxv", (int)pSolProc->Process);
+ int fd = open(szPath, O_RDONLY);
+ if (fd < 0)
+ {
+ rc = RTErrConvertFromErrno(fd);
+ CORELOGRELSYS((CORELOG_NAME "ProcReadAuxVecs: failed to open %s rc=%Rrc\n", szPath, rc));
+ return rc;
+ }
+
+ size_t cbAuxFile = GetFileSizeByFd(fd);
+ if (cbAuxFile >= sizeof(auxv_t))
+ {
+ pSolProc->pAuxVecs = (auxv_t*)GetMemoryChunk(pSolCore, cbAuxFile + sizeof(auxv_t));
+ if (pSolProc->pAuxVecs)
+ {
+ rc = ReadFileNoIntr(fd, pSolProc->pAuxVecs, cbAuxFile);
+ if (RT_SUCCESS(rc))
+ {
+ /* Terminate list of vectors */
+ pSolProc->cAuxVecs = cbAuxFile / sizeof(auxv_t);
+ CORELOG((CORELOG_NAME "ProcReadAuxVecs: cbAuxFile=%u auxv_t size %d cAuxVecs=%u\n", cbAuxFile, sizeof(auxv_t),
+ pSolProc->cAuxVecs));
+ if (pSolProc->cAuxVecs > 0)
+ {
+ pSolProc->pAuxVecs[pSolProc->cAuxVecs].a_type = AT_NULL;
+ pSolProc->pAuxVecs[pSolProc->cAuxVecs].a_un.a_val = 0L;
+ close(fd);
+ return VINF_SUCCESS;
+ }
+
+ CORELOGRELSYS((CORELOG_NAME "ProcReadAuxVecs: Invalid vector count %u\n", pSolProc->cAuxVecs));
+ rc = VERR_READ_ERROR;
+ }
+ else
+ CORELOGRELSYS((CORELOG_NAME "ProcReadAuxVecs: ReadFileNoIntr failed. rc=%Rrc cbAuxFile=%u\n", rc, cbAuxFile));
+
+ pSolProc->pAuxVecs = NULL;
+ pSolProc->cAuxVecs = 0;
+ }
+ else
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadAuxVecs: no memory for %u bytes\n", cbAuxFile + sizeof(auxv_t)));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadAuxVecs: aux file too small %u, expecting %u or more\n", cbAuxFile, sizeof(auxv_t)));
+ rc = VERR_READ_ERROR;
+ }
+
+ close(fd);
+ return rc;
+}
+
+
+/*
+ * Find an element in the process' auxiliary vector.
+ */
+static long GetAuxVal(PRTSOLCOREPROCESS pSolProc, int Type)
+{
+ AssertReturn(pSolProc, -1);
+ if (pSolProc->pAuxVecs)
+ {
+ auxv_t *pAuxVec = pSolProc->pAuxVecs;
+ for (; pAuxVec->a_type != AT_NULL; pAuxVec++)
+ {
+ if (pAuxVec->a_type == Type)
+ return pAuxVec->a_un.a_val;
+ }
+ }
+ return -1;
+}
+
+
+/**
+ * Read the process mappings (format prmap_t array).
+ *
+ * @return IPRT status code.
+ * @param pSolCore Pointer to the core object.
+ *
+ * @remarks Should not be called before successful call to @see AllocMemoryArea()
+ */
+static int ProcReadMappings(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ char szPath[PATH_MAX];
+ int rc = VINF_SUCCESS;
+ RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/map", (int)pSolProc->Process);
+ int fdMap = open(szPath, O_RDONLY);
+ if (fdMap < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: failed to open %s. rc=%Rrc\n", szPath, rc));
+ return rc;
+ }
+
+ RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/as", (int)pSolProc->Process);
+ pSolProc->fdAs = open(szPath, O_RDONLY);
+ if (pSolProc->fdAs >= 0)
+ {
+ /*
+ * Allocate and read all the prmap_t objects from proc.
+ */
+ size_t cbMapFile = GetFileSizeByFd(fdMap);
+ if (cbMapFile >= sizeof(prmap_t))
+ {
+ prmap_t *pMap = (prmap_t*)GetMemoryChunk(pSolCore, cbMapFile);
+ if (pMap)
+ {
+ rc = ReadFileNoIntr(fdMap, pMap, cbMapFile);
+ if (RT_SUCCESS(rc))
+ {
+ pSolProc->cMappings = cbMapFile / sizeof(prmap_t);
+ if (pSolProc->cMappings > 0)
+ {
+ /*
+ * Allocate for each prmap_t object, a corresponding RTSOLCOREMAPINFO object.
+ */
+ pSolProc->pMapInfoHead = (PRTSOLCOREMAPINFO)GetMemoryChunk(pSolCore,
+ pSolProc->cMappings * sizeof(RTSOLCOREMAPINFO));
+ if (pSolProc->pMapInfoHead)
+ {
+ /*
+ * Associate the prmap_t with the mapping info object.
+ */
+ /*Assert(pSolProc->pMapInfoHead == NULL); - does not make sense */
+ PRTSOLCOREMAPINFO pCur = pSolProc->pMapInfoHead;
+ PRTSOLCOREMAPINFO pPrev = NULL;
+ for (uint64_t i = 0; i < pSolProc->cMappings; i++, pMap++, pCur++)
+ {
+ memcpy(&pCur->pMap, pMap, sizeof(pCur->pMap));
+ if (pPrev)
+ pPrev->pNext = pCur;
+
+ pCur->fError = 0;
+
+ /*
+ * Make sure we can read the mapping, otherwise mark them to be skipped.
+ */
+ char achBuf[PAGE_SIZE];
+ uint64_t k = 0;
+ while (k < pCur->pMap.pr_size)
+ {
+ size_t cb = RT_MIN(sizeof(achBuf), pCur->pMap.pr_size - k);
+ int rc2 = ProcReadAddrSpace(pSolProc, pCur->pMap.pr_vaddr + k, &achBuf, cb);
+ if (RT_FAILURE(rc2))
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: skipping mapping. vaddr=%#x rc=%Rrc\n",
+ pCur->pMap.pr_vaddr, rc2));
+
+ /*
+ * Instead of storing the actual mapping data which we failed to read, the core
+ * will contain an errno in place. So we adjust the prmap_t's size field too
+ * so the program header offsets match.
+ */
+ pCur->pMap.pr_size = RT_ALIGN_Z(sizeof(int), 8);
+ pCur->fError = errno;
+ if (pCur->fError == 0) /* huh!? somehow errno got reset? fake one! EFAULT is nice. */
+ pCur->fError = EFAULT;
+ break;
+ }
+ k += cb;
+ }
+
+ pPrev = pCur;
+ }
+ if (pPrev)
+ pPrev->pNext = NULL;
+
+ close(fdMap);
+ close(pSolProc->fdAs);
+ pSolProc->fdAs = -1;
+ CORELOG((CORELOG_NAME "ProcReadMappings: successfully read in %u mappings\n", pSolProc->cMappings));
+ return VINF_SUCCESS;
+ }
+
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: GetMemoryChunk failed %u\n",
+ pSolProc->cMappings * sizeof(RTSOLCOREMAPINFO)));
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: Invalid mapping count %u\n", pSolProc->cMappings));
+ rc = VERR_READ_ERROR;
+ }
+ }
+ else
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: FileReadNoIntr failed. rc=%Rrc cbMapFile=%u\n", rc,
+ cbMapFile));
+ }
+ }
+ else
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: GetMemoryChunk failed. cbMapFile=%u\n", cbMapFile));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ close(pSolProc->fdAs);
+ pSolProc->fdAs = -1;
+ }
+ else
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: failed to open %s. rc=%Rrc\n", szPath, rc));
+
+ close(fdMap);
+ return rc;
+}
+
+
+/**
+ * Reads the thread information for all threads in the process.
+ *
+ * @return IPRT status code.
+ * @param pSolCore Pointer to the core object.
+ *
+ * @remarks Should not be called before successful call to @see AllocMemoryArea()
+ */
+static int ProcReadThreads(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ AssertReturn(pSolProc->pCurThreadCtx, VERR_NO_DATA);
+
+ /*
+ * Read the information for threads.
+ * Format: prheader_t + array of lwpsinfo_t's.
+ */
+ size_t cbInfoHdrAndData;
+ void *pvInfoHdr = NULL;
+ int rc = ProcReadFileInto(pSolCore, "lpsinfo", &pvInfoHdr, &cbInfoHdrAndData);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Read the status of threads.
+ * Format: prheader_t + array of lwpstatus_t's.
+ */
+ void *pvStatusHdr = NULL;
+ size_t cbStatusHdrAndData;
+ rc = ProcReadFileInto(pSolCore, "lstatus", &pvStatusHdr, &cbStatusHdrAndData);
+ if (RT_SUCCESS(rc))
+ {
+ prheader_t *pInfoHdr = (prheader_t *)pvInfoHdr;
+ prheader_t *pStatusHdr = (prheader_t *)pvStatusHdr;
+ lwpstatus_t *pStatus = (lwpstatus_t *)((uintptr_t)pStatusHdr + sizeof(prheader_t));
+ lwpsinfo_t *pInfo = (lwpsinfo_t *)((uintptr_t)pInfoHdr + sizeof(prheader_t));
+ uint64_t cStatus = pStatusHdr->pr_nent;
+ uint64_t cInfo = pInfoHdr->pr_nent;
+
+ CORELOG((CORELOG_NAME "ProcReadThreads: read info(%u) status(%u), threads:cInfo=%u cStatus=%u\n", cbInfoHdrAndData,
+ cbStatusHdrAndData, cInfo, cStatus));
+
+ /*
+ * Minor sanity size check (remember sizeof lwpstatus_t & lwpsinfo_t is <= size in file per entry).
+ */
+ if ( (cbStatusHdrAndData - sizeof(prheader_t)) % pStatusHdr->pr_entsize == 0
+ && (cbInfoHdrAndData - sizeof(prheader_t)) % pInfoHdr->pr_entsize == 0)
+ {
+ /*
+ * Make sure we have a matching lstatus entry for an lpsinfo entry unless
+ * it is a zombie thread, in which case we will not have a matching lstatus entry.
+ */
+ for (; cInfo != 0; cInfo--)
+ {
+ if (pInfo->pr_sname != 'Z') /* zombie */
+ {
+ if ( cStatus == 0
+ || pStatus->pr_lwpid != pInfo->pr_lwpid)
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: cStatus = %u pStatuslwpid=%d infolwpid=%d\n", cStatus,
+ pStatus->pr_lwpid, pInfo->pr_lwpid));
+ rc = VERR_INVALID_STATE;
+ break;
+ }
+ pStatus = (lwpstatus_t *)((uintptr_t)pStatus + pStatusHdr->pr_entsize);
+ cStatus--;
+ }
+ pInfo = (lwpsinfo_t *)((uintptr_t)pInfo + pInfoHdr->pr_entsize);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * There can still be more lwpsinfo_t's than lwpstatus_t's, build the
+ * lists accordingly.
+ */
+ pStatus = (lwpstatus_t *)((uintptr_t)pStatusHdr + sizeof(prheader_t));
+ pInfo = (lwpsinfo_t *)((uintptr_t)pInfoHdr + sizeof(prheader_t));
+ cInfo = pInfoHdr->pr_nent;
+ cStatus = pInfoHdr->pr_nent;
+
+ size_t cbThreadInfo = RT_MAX(cStatus, cInfo) * sizeof(RTSOLCORETHREADINFO);
+ pSolProc->pThreadInfoHead = (PRTSOLCORETHREADINFO)GetMemoryChunk(pSolCore, cbThreadInfo);
+ if (pSolProc->pThreadInfoHead)
+ {
+ PRTSOLCORETHREADINFO pCur = pSolProc->pThreadInfoHead;
+ PRTSOLCORETHREADINFO pPrev = NULL;
+ for (uint64_t i = 0; i < cInfo; i++, pCur++)
+ {
+ pCur->Info = *pInfo;
+ if ( pInfo->pr_sname != 'Z'
+ && pInfo->pr_lwpid == pStatus->pr_lwpid)
+ {
+ /*
+ * Adjust the context of the dumping thread to reflect the context
+ * when the core dump got initiated before whatever signal caused it.
+ */
+ if ( pStatus /* noid droid */
+ && pStatus->pr_lwpid == (id_t)pSolProc->hCurThread)
+ {
+ AssertCompile(sizeof(pStatus->pr_reg) == sizeof(pSolProc->pCurThreadCtx->uc_mcontext.gregs));
+ AssertCompile(sizeof(pStatus->pr_fpreg) == sizeof(pSolProc->pCurThreadCtx->uc_mcontext.fpregs));
+ memcpy(&pStatus->pr_reg, &pSolProc->pCurThreadCtx->uc_mcontext.gregs, sizeof(pStatus->pr_reg));
+ memcpy(&pStatus->pr_fpreg, &pSolProc->pCurThreadCtx->uc_mcontext.fpregs, sizeof(pStatus->pr_fpreg));
+
+ AssertCompile(sizeof(pStatus->pr_lwphold) == sizeof(pSolProc->pCurThreadCtx->uc_sigmask));
+ memcpy(&pStatus->pr_lwphold, &pSolProc->pCurThreadCtx->uc_sigmask, sizeof(pStatus->pr_lwphold));
+ pStatus->pr_ustack = (uintptr_t)&pSolProc->pCurThreadCtx->uc_stack;
+
+ CORELOG((CORELOG_NAME "ProcReadThreads: patched dumper thread with pre-dump time context.\n"));
+ }
+
+ pCur->pStatus = pStatus;
+ pStatus = (lwpstatus_t *)((uintptr_t)pStatus + pStatusHdr->pr_entsize);
+ }
+ else
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: missing status for lwp %d\n", pInfo->pr_lwpid));
+ pCur->pStatus = NULL;
+ }
+
+ if (pPrev)
+ pPrev->pNext = pCur;
+ pPrev = pCur;
+ pInfo = (lwpsinfo_t *)((uintptr_t)pInfo + pInfoHdr->pr_entsize);
+ }
+ if (pPrev)
+ pPrev->pNext = NULL;
+
+ CORELOG((CORELOG_NAME "ProcReadThreads: successfully read %u threads.\n", cInfo));
+ pSolProc->cThreads = cInfo;
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: GetMemoryChunk failed for %u bytes\n", cbThreadInfo));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: Invalid state information for threads. rc=%Rrc\n", rc));
+ }
+ else
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: huh!? cbStatusHdrAndData=%u prheader_t=%u entsize=%u\n",
+ cbStatusHdrAndData, sizeof(prheader_t), pStatusHdr->pr_entsize));
+ CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: huh!? cbInfoHdrAndData=%u entsize=%u\n", cbInfoHdrAndData,
+ pStatusHdr->pr_entsize));
+ rc = VERR_INVALID_STATE;
+ }
+ }
+ else
+ CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: ReadFileNoIntr failed for \"lpsinfo\" rc=%Rrc\n", rc));
+ }
+ else
+ CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: ReadFileNoIntr failed for \"lstatus\" rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Reads miscellaneous information that is collected as part of a core file.
+ * This may include platform name, zone name and other OS-specific information.
+ *
+ * @param pSolCore Pointer to the core object.
+ *
+ * @return IPRT status code.
+ */
+static int ProcReadMiscInfo(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+
+#ifdef RT_OS_SOLARIS
+ /*
+ * Read the platform name, uname string and zone name.
+ */
+ int rc = sysinfo(SI_PLATFORM, pSolProc->szPlatform, sizeof(pSolProc->szPlatform));
+ if (rc == -1)
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMiscInfo: sysinfo failed. rc=%d errno=%d\n", rc, errno));
+ return VERR_GENERAL_FAILURE;
+ }
+ pSolProc->szPlatform[sizeof(pSolProc->szPlatform) - 1] = '\0';
+
+ rc = uname(&pSolProc->UtsName);
+ if (rc == -1)
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMiscInfo: uname failed. rc=%d errno=%d\n", rc, errno));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ /*
+ * See comment in GetOldProcessInfo() for why we need to verify the offset here.
+ * It's not perfect, but it should be fine unless they really mess up the structure
+ * layout in the future. See @bugref{8479}.
+ */
+ size_t const offZoneId = RT_UOFFSETOF(psinfo_t, pr_zoneid);
+ if (pSolProc->cbProcInfo < offZoneId)
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMiscInfo: psinfo size mismatch. cbProcInfo=%u expected >= %u\n",
+ pSolProc->cbProcInfo, offZoneId));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ psinfo_t *pProcInfo = (psinfo_t *)pSolProc->pvProcInfo;
+ rc = getzonenamebyid(pProcInfo->pr_zoneid, pSolProc->szZoneName, sizeof(pSolProc->szZoneName));
+ if (rc < 0)
+ {
+ CORELOGRELSYS((CORELOG_NAME "ProcReadMiscInfo: getzonenamebyid failed. rc=%d errno=%d zoneid=%d\n", rc, errno,
+ pProcInfo->pr_zoneid));
+ return VERR_GENERAL_FAILURE;
+ }
+ pSolProc->szZoneName[sizeof(pSolProc->szZoneName) - 1] = '\0';
+ rc = VINF_SUCCESS;
+
+#else
+# error Port Me!
+#endif
+ return rc;
+}
+
+
+/**
+ * On Solaris use the old-style procfs interfaces but the core file still should have this
+ * info. for backward and GDB compatibility, hence the need for this ugly function.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pSolCore Pointer to the core object.
+ * @param pInfo Pointer to the old prpsinfo_t structure to update.
+ */
+static int GetOldProcessInfo(PRTSOLCORE pSolCore, prpsinfo_t *pInfo)
+{
+ AssertReturn(pSolCore, VERR_INVALID_PARAMETER);
+ AssertReturn(pInfo, VERR_INVALID_PARAMETER);
+
+ /*
+ * The psinfo_t and the size of /proc/<pid>/psinfo varies both within the same Solaris system
+ * and across Solaris major versions. However, manual dumping of the structure and offsets shows
+ * that they changed the size of lwpsinfo_t and the size of the lwpsinfo_t::pr_filler.
+ *
+ * The proc psinfo file will be what gets dumped ultimately in the core file but we still need
+ * to read the fields to translate to the older process info structure here.
+ *
+ * See @bugref{8479}.
+ */
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+
+ size_t offLwp = RT_UOFFSETOF(psinfo_t, pr_lwp);
+ /* last member we care about in lwpsinfo_t is pr_bindpset which is also present on ancient Solaris version we use for
+ building the additions. Should be safe enough as we don't need/access members upto or beyond that point anyway. */
+ size_t offLastOnProc = RT_UOFFSETOF(lwpsinfo_t, pr_bindpset);
+ if (pSolProc->cbProcInfo >= offLwp + offLastOnProc)
+ {
+ psinfo_t *pSrc = (psinfo_t *)pSolProc->pvProcInfo;
+ memset(pInfo, 0, sizeof(prpsinfo_t));
+ pInfo->pr_state = pSrc->pr_lwp.pr_state;
+ pInfo->pr_zomb = (pInfo->pr_state == SZOMB);
+ RTStrCopy(pInfo->pr_clname, sizeof(pInfo->pr_clname), pSrc->pr_lwp.pr_clname);
+ RTStrCopy(pInfo->pr_fname, sizeof(pInfo->pr_fname), pSrc->pr_fname);
+ memcpy(&pInfo->pr_psargs, &pSrc->pr_psargs, sizeof(pInfo->pr_psargs));
+ pInfo->pr_nice = pSrc->pr_lwp.pr_nice;
+ pInfo->pr_flag = pSrc->pr_lwp.pr_flag;
+ pInfo->pr_uid = pSrc->pr_uid;
+ pInfo->pr_gid = pSrc->pr_gid;
+ pInfo->pr_pid = pSrc->pr_pid;
+ pInfo->pr_ppid = pSrc->pr_ppid;
+ pInfo->pr_pgrp = pSrc->pr_pgid;
+ pInfo->pr_sid = pSrc->pr_sid;
+ pInfo->pr_addr = (caddr_t)pSrc->pr_addr;
+ pInfo->pr_size = pSrc->pr_size;
+ pInfo->pr_rssize = pSrc->pr_rssize;
+ pInfo->pr_wchan = (caddr_t)pSrc->pr_lwp.pr_wchan;
+ pInfo->pr_start = pSrc->pr_start;
+ pInfo->pr_time = pSrc->pr_time;
+ pInfo->pr_pri = pSrc->pr_lwp.pr_pri;
+ pInfo->pr_oldpri = pSrc->pr_lwp.pr_oldpri;
+ pInfo->pr_cpu = pSrc->pr_lwp.pr_cpu;
+ pInfo->pr_ottydev = cmpdev(pSrc->pr_ttydev);
+ pInfo->pr_lttydev = pSrc->pr_ttydev;
+ pInfo->pr_syscall = pSrc->pr_lwp.pr_syscall;
+ pInfo->pr_ctime = pSrc->pr_ctime;
+ pInfo->pr_bysize = pSrc->pr_size * PAGESIZE;
+ pInfo->pr_byrssize = pSrc->pr_rssize * PAGESIZE;
+ pInfo->pr_argc = pSrc->pr_argc;
+ pInfo->pr_argv = (char **)pSrc->pr_argv;
+ pInfo->pr_envp = (char **)pSrc->pr_envp;
+ pInfo->pr_wstat = pSrc->pr_wstat;
+ pInfo->pr_pctcpu = pSrc->pr_pctcpu;
+ pInfo->pr_pctmem = pSrc->pr_pctmem;
+ pInfo->pr_euid = pSrc->pr_euid;
+ pInfo->pr_egid = pSrc->pr_egid;
+ pInfo->pr_aslwpid = 0;
+ pInfo->pr_dmodel = pSrc->pr_dmodel;
+
+ return VINF_SUCCESS;
+ }
+
+ CORELOGRELSYS((CORELOG_NAME "GetOldProcessInfo: Size/offset mismatch. offLwp=%u offLastOnProc=%u cbProcInfo=%u\n",
+ offLwp, offLastOnProc, pSolProc->cbProcInfo));
+ return VERR_MISMATCH;
+}
+
+
+/**
+ * On Solaris use the old-style procfs interfaces but the core file still should have this
+ * info. for backward and GDB compatibility, hence the need for this ugly function.
+ *
+ * @param pSolCore Pointer to the core object.
+ * @param pInfo Pointer to the thread info.
+ * @param pStatus Pointer to the thread status.
+ * @param pDst Pointer to the old-style status structure to update.
+ *
+ */
+static void GetOldProcessStatus(PRTSOLCORE pSolCore, lwpsinfo_t *pInfo, lwpstatus_t *pStatus, prstatus_t *pDst)
+{
+ AssertReturnVoid(pSolCore);
+ AssertReturnVoid(pInfo);
+ AssertReturnVoid(pStatus);
+ AssertReturnVoid(pDst);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ memset(pDst, 0, sizeof(prstatus_t));
+ if (pStatus->pr_flags & PR_STOPPED)
+ pDst->pr_flags = 0x0001;
+ if (pStatus->pr_flags & PR_ISTOP)
+ pDst->pr_flags = 0x0002;
+ if (pStatus->pr_flags & PR_DSTOP)
+ pDst->pr_flags = 0x0004;
+ if (pStatus->pr_flags & PR_ASLEEP)
+ pDst->pr_flags = 0x0008;
+ if (pStatus->pr_flags & PR_FORK)
+ pDst->pr_flags = 0x0010;
+ if (pStatus->pr_flags & PR_RLC)
+ pDst->pr_flags = 0x0020;
+ /* PR_PTRACE is never set */
+ if (pStatus->pr_flags & PR_PCINVAL)
+ pDst->pr_flags = 0x0080;
+ if (pStatus->pr_flags & PR_ISSYS)
+ pDst->pr_flags = 0x0100;
+ if (pStatus->pr_flags & PR_STEP)
+ pDst->pr_flags = 0x0200;
+ if (pStatus->pr_flags & PR_KLC)
+ pDst->pr_flags = 0x0400;
+ if (pStatus->pr_flags & PR_ASYNC)
+ pDst->pr_flags = 0x0800;
+ if (pStatus->pr_flags & PR_PTRACE)
+ pDst->pr_flags = 0x1000;
+ if (pStatus->pr_flags & PR_MSACCT)
+ pDst->pr_flags = 0x2000;
+ if (pStatus->pr_flags & PR_BPTADJ)
+ pDst->pr_flags = 0x4000;
+ if (pStatus->pr_flags & PR_ASLWP)
+ pDst->pr_flags = 0x8000;
+
+ pDst->pr_who = pStatus->pr_lwpid;
+ pDst->pr_why = pStatus->pr_why;
+ pDst->pr_what = pStatus->pr_what;
+ pDst->pr_info = pStatus->pr_info;
+ pDst->pr_cursig = pStatus->pr_cursig;
+ pDst->pr_sighold = pStatus->pr_lwphold;
+ pDst->pr_altstack = pStatus->pr_altstack;
+ pDst->pr_action = pStatus->pr_action;
+ pDst->pr_syscall = pStatus->pr_syscall;
+ pDst->pr_nsysarg = pStatus->pr_nsysarg;
+ pDst->pr_lwppend = pStatus->pr_lwppend;
+ pDst->pr_oldcontext = (ucontext_t *)pStatus->pr_oldcontext;
+ memcpy(pDst->pr_reg, pStatus->pr_reg, sizeof(pDst->pr_reg));
+ memcpy(pDst->pr_sysarg, pStatus->pr_sysarg, sizeof(pDst->pr_sysarg));
+ RTStrCopy(pDst->pr_clname, sizeof(pDst->pr_clname), pStatus->pr_clname);
+
+ pDst->pr_nlwp = pSolProc->ProcStatus.pr_nlwp;
+ pDst->pr_sigpend = pSolProc->ProcStatus.pr_sigpend;
+ pDst->pr_pid = pSolProc->ProcStatus.pr_pid;
+ pDst->pr_ppid = pSolProc->ProcStatus.pr_ppid;
+ pDst->pr_pgrp = pSolProc->ProcStatus.pr_pgid;
+ pDst->pr_sid = pSolProc->ProcStatus.pr_sid;
+ pDst->pr_utime = pSolProc->ProcStatus.pr_utime;
+ pDst->pr_stime = pSolProc->ProcStatus.pr_stime;
+ pDst->pr_cutime = pSolProc->ProcStatus.pr_cutime;
+ pDst->pr_cstime = pSolProc->ProcStatus.pr_cstime;
+ pDst->pr_brkbase = (caddr_t)pSolProc->ProcStatus.pr_brkbase;
+ pDst->pr_brksize = pSolProc->ProcStatus.pr_brksize;
+ pDst->pr_stkbase = (caddr_t)pSolProc->ProcStatus.pr_stkbase;
+ pDst->pr_stksize = pSolProc->ProcStatus.pr_stksize;
+
+ pDst->pr_processor = (short)pInfo->pr_onpro;
+ pDst->pr_bind = (short)pInfo->pr_bindpro;
+ pDst->pr_instr = pStatus->pr_instr;
+}
+
+
+/**
+ * Callback for rtCoreDumperForEachThread to suspend a thread.
+ *
+ * @param pSolCore Pointer to the core object.
+ * @param pvThreadInfo Opaque pointer to thread information.
+ *
+ * @return IPRT status code.
+ */
+static int suspendThread(PRTSOLCORE pSolCore, void *pvThreadInfo)
+{
+ AssertPtrReturn(pvThreadInfo, VERR_INVALID_POINTER);
+ NOREF(pSolCore);
+
+ lwpsinfo_t *pThreadInfo = (lwpsinfo_t *)pvThreadInfo;
+ CORELOG((CORELOG_NAME ":suspendThread %d\n", (lwpid_t)pThreadInfo->pr_lwpid));
+ if ((lwpid_t)pThreadInfo->pr_lwpid != pSolCore->SolProc.hCurThread)
+ _lwp_suspend(pThreadInfo->pr_lwpid);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Callback for rtCoreDumperForEachThread to resume a thread.
+ *
+ * @param pSolCore Pointer to the core object.
+ * @param pvThreadInfo Opaque pointer to thread information.
+ *
+ * @return IPRT status code.
+ */
+static int resumeThread(PRTSOLCORE pSolCore, void *pvThreadInfo)
+{
+ AssertPtrReturn(pvThreadInfo, VERR_INVALID_POINTER);
+ NOREF(pSolCore);
+
+ lwpsinfo_t *pThreadInfo = (lwpsinfo_t *)pvThreadInfo;
+ CORELOG((CORELOG_NAME ":resumeThread %d\n", (lwpid_t)pThreadInfo->pr_lwpid));
+ if ((lwpid_t)pThreadInfo->pr_lwpid != (lwpid_t)pSolCore->SolProc.hCurThread)
+ _lwp_continue(pThreadInfo->pr_lwpid);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Calls a thread worker function for all threads in the process as described by /proc
+ *
+ * @param pSolCore Pointer to the core object.
+ * @param pcThreads Number of threads read.
+ * @param pfnWorker Callback function for each thread.
+ *
+ * @return IPRT status code.
+ */
+static int rtCoreDumperForEachThread(PRTSOLCORE pSolCore, uint64_t *pcThreads, PFNRTSOLCORETHREADWORKER pfnWorker)
+{
+ AssertPtrReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+
+ /*
+ * Read the information for threads.
+ * Format: prheader_t + array of lwpsinfo_t's.
+ */
+ char szLpsInfoPath[PATH_MAX];
+ RTStrPrintf(szLpsInfoPath, sizeof(szLpsInfoPath), "/proc/%d/lpsinfo", (int)pSolProc->Process);
+
+ int rc = VINF_SUCCESS;
+ int fd = open(szLpsInfoPath, O_RDONLY);
+ if (fd >= 0)
+ {
+ size_t cbInfoHdrAndData = GetFileSizeByFd(fd);
+ void *pvInfoHdr = mmap(NULL, cbInfoHdrAndData, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
+ -1 /* fd */, 0 /* offset */);
+ if (pvInfoHdr != MAP_FAILED)
+ {
+ rc = ReadFileNoIntr(fd, pvInfoHdr, cbInfoHdrAndData);
+ if (RT_SUCCESS(rc))
+ {
+ prheader_t *pHeader = (prheader_t *)pvInfoHdr;
+ lwpsinfo_t *pThreadInfo = (lwpsinfo_t *)((uintptr_t)pvInfoHdr + sizeof(prheader_t));
+ for (long i = 0; i < pHeader->pr_nent; i++)
+ {
+ pfnWorker(pSolCore, pThreadInfo);
+ pThreadInfo = (lwpsinfo_t *)((uintptr_t)pThreadInfo + pHeader->pr_entsize);
+ }
+ if (pcThreads)
+ *pcThreads = pHeader->pr_nent;
+ }
+
+ munmap(pvInfoHdr, cbInfoHdrAndData);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ close(fd);
+ }
+ else
+ rc = RTErrConvertFromErrno(rc);
+
+ return rc;
+}
+
+
+/**
+ * Resume all threads of this process.
+ *
+ * @param pSolCore Pointer to the core object.
+ *
+ * @return IPRT status code..
+ */
+static int rtCoreDumperResumeThreads(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ uint64_t cThreads;
+ return rtCoreDumperForEachThread(pSolCore, &cThreads, resumeThread);
+}
+
+
+/**
+ * Stop all running threads of this process except the current one.
+ *
+ * @param pSolCore Pointer to the core object.
+ *
+ * @return IPRT status code.
+ */
+static int rtCoreDumperSuspendThreads(PRTSOLCORE pSolCore)
+{
+ AssertPtrReturn(pSolCore, VERR_INVALID_POINTER);
+
+ /*
+ * This function tries to ensures while we suspend threads, no newly spawned threads
+ * or a combination of spawning and terminating threads can cause any threads to be left running.
+ * The assumption here is that threads can only increase not decrease across iterations.
+ */
+ uint16_t cTries = 0;
+ uint64_t aThreads[4];
+ RT_ZERO(aThreads);
+ int rc = VERR_GENERAL_FAILURE;
+ void *pv = NULL;
+ size_t cb = 0;
+ for (cTries = 0; cTries < RT_ELEMENTS(aThreads); cTries++)
+ {
+ rc = rtCoreDumperForEachThread(pSolCore, &aThreads[cTries], suspendThread);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ if ( RT_SUCCESS(rc)
+ && aThreads[cTries - 1] != aThreads[cTries - 2])
+ {
+ CORELOGRELSYS((CORELOG_NAME "rtCoreDumperSuspendThreads: possible thread bomb!?\n"));
+ rc = VERR_TIMEOUT;
+ }
+ return rc;
+}
+
+
+/**
+ * Returns size of an ELF NOTE header given the size of data the NOTE section will contain.
+ *
+ * @param cb Size of the data.
+ *
+ * @return Size of data actually used for NOTE header and section.
+ */
+static inline size_t ElfNoteHeaderSize(size_t cb)
+{
+ return sizeof(ELFNOTEHDR) + RT_ALIGN_Z(cb, 4);
+}
+
+
+/**
+ * Write an ELF NOTE header into the core file.
+ *
+ * @param pSolCore Pointer to the core object.
+ * @param Type Type of this NOTE section.
+ * @param pcv Opaque pointer to the data, if NULL only computes size.
+ * @param cb Size of the data.
+ *
+ * @return IPRT status code.
+ */
+static int ElfWriteNoteHeader(PRTSOLCORE pSolCore, uint_t Type, const void *pcv, size_t cb)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+ AssertReturn(pcv, VERR_INVALID_POINTER);
+ AssertReturn(cb > 0, VERR_NO_DATA);
+ AssertReturn(pSolCore->pfnWriter, VERR_WRITE_ERROR);
+ AssertReturn(pSolCore->fdCoreFile >= 0, VERR_INVALID_HANDLE);
+
+ int rc = VERR_GENERAL_FAILURE;
+#ifdef RT_OS_SOLARIS
+ ELFNOTEHDR ElfNoteHdr;
+ RT_ZERO(ElfNoteHdr);
+ ElfNoteHdr.achName[0] = 'C';
+ ElfNoteHdr.achName[1] = 'O';
+ ElfNoteHdr.achName[2] = 'R';
+ ElfNoteHdr.achName[3] = 'E';
+
+ /*
+ * This is a known violation of the 64-bit ELF spec., see xTracker @bugref{5211}
+ * for the historic reasons as to the padding and 'namesz' anomalies.
+ */
+ static const char s_achPad[3] = { 0, 0, 0 };
+ size_t cbAlign = RT_ALIGN_Z(cb, 4);
+ ElfNoteHdr.Hdr.n_namesz = 5;
+ ElfNoteHdr.Hdr.n_type = Type;
+ ElfNoteHdr.Hdr.n_descsz = cbAlign;
+
+ /*
+ * Write note header and description.
+ */
+ rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &ElfNoteHdr, sizeof(ElfNoteHdr));
+ if (RT_SUCCESS(rc))
+ {
+ rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, pcv, cb);
+ if (RT_SUCCESS(rc))
+ {
+ if (cbAlign > cb)
+ rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, s_achPad, cbAlign - cb);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteNote: pfnWriter failed. Type=%d rc=%Rrc\n", Type, rc));
+#else
+#error Port Me!
+#endif
+ return rc;
+}
+
+
+/**
+ * Computes the size of NOTE section for the given core type.
+ * Solaris has two types of program header information (new and old).
+ *
+ * @param pSolCore Pointer to the core object.
+ * @param enmType Type of core file information required.
+ *
+ * @return Size of NOTE section.
+ */
+static size_t ElfNoteSectionSize(PRTSOLCORE pSolCore, RTSOLCORETYPE enmType)
+{
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ size_t cb = 0;
+ switch (enmType)
+ {
+ case enmOldEra:
+ {
+ cb += ElfNoteHeaderSize(sizeof(prpsinfo_t));
+ cb += ElfNoteHeaderSize(pSolProc->cAuxVecs * sizeof(auxv_t));
+ cb += ElfNoteHeaderSize(strlen(pSolProc->szPlatform));
+
+ PRTSOLCORETHREADINFO pThreadInfo = pSolProc->pThreadInfoHead;
+ while (pThreadInfo)
+ {
+ if (pThreadInfo->pStatus)
+ {
+ cb += ElfNoteHeaderSize(sizeof(prstatus_t));
+ cb += ElfNoteHeaderSize(sizeof(prfpregset_t));
+ }
+ pThreadInfo = pThreadInfo->pNext;
+ }
+
+ break;
+ }
+
+ case enmNewEra:
+ {
+ cb += ElfNoteHeaderSize(sizeof(psinfo_t));
+ cb += ElfNoteHeaderSize(sizeof(pstatus_t));
+ cb += ElfNoteHeaderSize(pSolProc->cAuxVecs * sizeof(auxv_t));
+ cb += ElfNoteHeaderSize(strlen(pSolProc->szPlatform) + 1);
+ cb += ElfNoteHeaderSize(sizeof(struct utsname));
+ cb += ElfNoteHeaderSize(sizeof(core_content_t));
+ cb += ElfNoteHeaderSize(pSolProc->cbCred);
+
+ if (pSolProc->pPriv)
+ cb += ElfNoteHeaderSize(PRIV_PRPRIV_SIZE(pSolProc->pPriv)); /* Ought to be same as cbPriv!? */
+
+ if (pSolProc->pcPrivImpl)
+ cb += ElfNoteHeaderSize(PRIV_IMPL_INFO_SIZE(pSolProc->pcPrivImpl));
+
+ cb += ElfNoteHeaderSize(strlen(pSolProc->szZoneName) + 1);
+ if (pSolProc->cbLdt > 0)
+ cb += ElfNoteHeaderSize(pSolProc->cbLdt);
+
+ PRTSOLCORETHREADINFO pThreadInfo = pSolProc->pThreadInfoHead;
+ while (pThreadInfo)
+ {
+ cb += ElfNoteHeaderSize(sizeof(lwpsinfo_t));
+ if (pThreadInfo->pStatus)
+ cb += ElfNoteHeaderSize(sizeof(lwpstatus_t));
+
+ pThreadInfo = pThreadInfo->pNext;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfNoteSectionSize: Unknown segment era %d\n", enmType));
+ break;
+ }
+ }
+
+ return cb;
+}
+
+
+/**
+ * Write the note section for the given era into the core file.
+ * Solaris has two types of program header information (new and old).
+ *
+ * @param pSolCore Pointer to the core object.
+ * @param enmType Type of core file information required.
+ *
+ * @return IPRT status code.
+ */
+static int ElfWriteNoteSection(PRTSOLCORE pSolCore, RTSOLCORETYPE enmType)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ int rc = VERR_GENERAL_FAILURE;
+
+#ifdef RT_OS_SOLARIS
+ typedef int (*PFNELFWRITENOTEHDR)(PRTSOLCORE pSolCore, uint_t, const void *pcv, size_t cb);
+ typedef struct
+ {
+ const char *pszType;
+ uint_t Type;
+ const void *pcv;
+ size_t cb;
+ } ELFWRITENOTE;
+
+ switch (enmType)
+ {
+ case enmOldEra:
+ {
+ ELFWRITENOTE aElfNotes[] =
+ {
+ { "NT_PRPSINFO", NT_PRPSINFO, &pSolProc->ProcInfoOld, sizeof(prpsinfo_t) },
+ { "NT_AUXV", NT_AUXV, pSolProc->pAuxVecs, pSolProc->cAuxVecs * sizeof(auxv_t) },
+ { "NT_PLATFORM", NT_PLATFORM, pSolProc->szPlatform, strlen(pSolProc->szPlatform) + 1 }
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(aElfNotes); i++)
+ {
+ rc = ElfWriteNoteHeader(pSolCore, aElfNotes[i].Type, aElfNotes[i].pcv, aElfNotes[i].cb);
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteNoteSection: ElfWriteNoteHeader failed for %s. rc=%Rrc\n",
+ aElfNotes[i].pszType, rc));
+ break;
+ }
+ }
+
+ /*
+ * Write old-style thread info., they contain nothing about zombies,
+ * so we just skip if there is no status information for them.
+ */
+ PRTSOLCORETHREADINFO pThreadInfo = pSolProc->pThreadInfoHead;
+ for (; pThreadInfo; pThreadInfo = pThreadInfo->pNext)
+ {
+ if (!pThreadInfo->pStatus)
+ continue;
+
+ prstatus_t OldProcessStatus;
+ GetOldProcessStatus(pSolCore, &pThreadInfo->Info, pThreadInfo->pStatus, &OldProcessStatus);
+ rc = ElfWriteNoteHeader(pSolCore, NT_PRSTATUS, &OldProcessStatus, sizeof(prstatus_t));
+ if (RT_SUCCESS(rc))
+ {
+ rc = ElfWriteNoteHeader(pSolCore, NT_PRFPREG, &pThreadInfo->pStatus->pr_fpreg, sizeof(prfpregset_t));
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteSegment: ElfWriteNote failed for NT_PRFPREF. rc=%Rrc\n", rc));
+ break;
+ }
+ }
+ else
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteSegment: ElfWriteNote failed for NT_PRSTATUS. rc=%Rrc\n", rc));
+ break;
+ }
+ }
+ break;
+ }
+
+ case enmNewEra:
+ {
+ ELFWRITENOTE aElfNotes[] =
+ {
+ { "NT_PSINFO", NT_PSINFO, pSolProc->pvProcInfo, pSolProc->cbProcInfo },
+ { "NT_PSTATUS", NT_PSTATUS, &pSolProc->ProcStatus, sizeof(pstatus_t) },
+ { "NT_AUXV", NT_AUXV, pSolProc->pAuxVecs, pSolProc->cAuxVecs * sizeof(auxv_t) },
+ { "NT_PLATFORM", NT_PLATFORM, pSolProc->szPlatform, strlen(pSolProc->szPlatform) + 1 },
+ { "NT_UTSNAME", NT_UTSNAME, &pSolProc->UtsName, sizeof(struct utsname) },
+ { "NT_CONTENT", NT_CONTENT, &pSolProc->CoreContent, sizeof(core_content_t) },
+ { "NT_PRCRED", NT_PRCRED, pSolProc->pvCred, pSolProc->cbCred },
+ { "NT_PRPRIV", NT_PRPRIV, pSolProc->pPriv, PRIV_PRPRIV_SIZE(pSolProc->pPriv) },
+ { "NT_PRPRIVINFO", NT_PRPRIVINFO, pSolProc->pcPrivImpl, PRIV_IMPL_INFO_SIZE(pSolProc->pcPrivImpl) },
+ { "NT_ZONENAME", NT_ZONENAME, pSolProc->szZoneName, strlen(pSolProc->szZoneName) + 1 }
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(aElfNotes); i++)
+ {
+ rc = ElfWriteNoteHeader(pSolCore, aElfNotes[i].Type, aElfNotes[i].pcv, aElfNotes[i].cb);
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteNoteSection: ElfWriteNoteHeader failed for %s. rc=%Rrc\n",
+ aElfNotes[i].pszType, rc));
+ break;
+ }
+ }
+
+ /*
+ * Write new-style thread info., missing lwpstatus_t indicates it's a zombie thread
+ * we only dump the lwpsinfo_t in that case.
+ */
+ PRTSOLCORETHREADINFO pThreadInfo = pSolProc->pThreadInfoHead;
+ for (; pThreadInfo; pThreadInfo = pThreadInfo->pNext)
+ {
+ rc = ElfWriteNoteHeader(pSolCore, NT_LWPSINFO, &pThreadInfo->Info, sizeof(lwpsinfo_t));
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteNoteSection: ElfWriteNoteHeader for NT_LWPSINFO failed. rc=%Rrc\n", rc));
+ break;
+ }
+
+ if (pThreadInfo->pStatus)
+ {
+ rc = ElfWriteNoteHeader(pSolCore, NT_LWPSTATUS, pThreadInfo->pStatus, sizeof(lwpstatus_t));
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteNoteSection: ElfWriteNoteHeader for NT_LWPSTATUS failed. rc=%Rrc\n",
+ rc));
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteNoteSection: Invalid type %d\n", enmType));
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+ }
+#else
+# error Port Me!
+#endif
+ return rc;
+}
+
+
+/**
+ * Write mappings into the core file.
+ *
+ * @param pSolCore Pointer to the core object.
+ *
+ * @return IPRT status code.
+ */
+static int ElfWriteMappings(PRTSOLCORE pSolCore)
+{
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+
+ int rc = VERR_GENERAL_FAILURE;
+ PRTSOLCOREMAPINFO pMapInfo = pSolProc->pMapInfoHead;
+ while (pMapInfo)
+ {
+ if (!pMapInfo->fError)
+ {
+ uint64_t k = 0;
+ char achBuf[PAGE_SIZE];
+ while (k < pMapInfo->pMap.pr_size)
+ {
+ size_t cb = RT_MIN(sizeof(achBuf), pMapInfo->pMap.pr_size - k);
+ int rc2 = ProcReadAddrSpace(pSolProc, pMapInfo->pMap.pr_vaddr + k, &achBuf, cb);
+ if (RT_FAILURE(rc2))
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteMappings: Failed to read mapping, can't recover. Bye. rc=%Rrc\n", rc));
+ return VERR_INVALID_STATE;
+ }
+
+ rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, achBuf, sizeof(achBuf));
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteMappings: pfnWriter failed. rc=%Rrc\n", rc));
+ return rc;
+ }
+ k += cb;
+ }
+ }
+ else
+ {
+ char achBuf[RT_ALIGN_Z(sizeof(int), 8)];
+ RT_ZERO(achBuf);
+ memcpy(achBuf, &pMapInfo->fError, sizeof(pMapInfo->fError));
+ if (sizeof(achBuf) != pMapInfo->pMap.pr_size)
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteMappings: Huh!? something is wrong!\n"));
+ rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &achBuf, sizeof(achBuf));
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteMappings: pfnWriter(2) failed. rc=%Rrc\n", rc));
+ return rc;
+ }
+ }
+
+ pMapInfo = pMapInfo->pNext;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Write program headers for all mappings into the core file.
+ *
+ * @param pSolCore Pointer to the core object.
+ *
+ * @return IPRT status code.
+ */
+static int ElfWriteMappingHeaders(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ Elf_Phdr ProgHdr;
+ RT_ZERO(ProgHdr);
+ ProgHdr.p_type = PT_LOAD;
+
+ int rc = VERR_GENERAL_FAILURE;
+ PRTSOLCOREMAPINFO pMapInfo = pSolProc->pMapInfoHead;
+ while (pMapInfo)
+ {
+ ProgHdr.p_vaddr = pMapInfo->pMap.pr_vaddr; /* Virtual address of this mapping in the process address space */
+ ProgHdr.p_offset = pSolCore->offWrite; /* Where this mapping is located in the core file */
+ ProgHdr.p_memsz = pMapInfo->pMap.pr_size; /* Size of the memory image of the mapping */
+ ProgHdr.p_filesz = pMapInfo->pMap.pr_size; /* Size of the file image of the mapping */
+
+ ProgHdr.p_flags = 0; /* Reset fields in a loop when needed! */
+ if (pMapInfo->pMap.pr_mflags & MA_READ)
+ ProgHdr.p_flags |= PF_R;
+ if (pMapInfo->pMap.pr_mflags & MA_WRITE)
+ ProgHdr.p_flags |= PF_W;
+ if (pMapInfo->pMap.pr_mflags & MA_EXEC)
+ ProgHdr.p_flags |= PF_X;
+
+ if (pMapInfo->fError)
+ ProgHdr.p_flags |= PF_SUNW_FAILURE;
+
+ rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &ProgHdr, sizeof(ProgHdr));
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "ElfWriteMappingHeaders: pfnWriter failed. rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ pSolCore->offWrite += ProgHdr.p_filesz;
+ pMapInfo = pMapInfo->pNext;
+ }
+ return rc;
+}
+
+/**
+ * Inner worker for rtCoreDumperWriteCore, which purpose is to
+ * squash cleanup gotos.
+ */
+static int rtCoreDumperWriteCoreDoIt(PRTSOLCORE pSolCore, PFNRTCOREWRITER pfnWriter,
+ PRTSOLCOREPROCESS pSolProc)
+{
+ pSolCore->offWrite = 0;
+ uint32_t cProgHdrs = pSolProc->cMappings + 2; /* two PT_NOTE program headers (old, new style) */
+
+ /*
+ * Write the ELF header.
+ */
+ Elf_Ehdr ElfHdr;
+ RT_ZERO(ElfHdr);
+ ElfHdr.e_ident[EI_MAG0] = ELFMAG0;
+ ElfHdr.e_ident[EI_MAG1] = ELFMAG1;
+ ElfHdr.e_ident[EI_MAG2] = ELFMAG2;
+ ElfHdr.e_ident[EI_MAG3] = ELFMAG3;
+ ElfHdr.e_ident[EI_DATA] = IsBigEndian() ? ELFDATA2MSB : ELFDATA2LSB;
+ ElfHdr.e_type = ET_CORE;
+ ElfHdr.e_version = EV_CURRENT;
+#ifdef RT_ARCH_AMD64
+ ElfHdr.e_machine = EM_AMD64;
+ ElfHdr.e_ident[EI_CLASS] = ELFCLASS64;
+#else
+ ElfHdr.e_machine = EM_386;
+ ElfHdr.e_ident[EI_CLASS] = ELFCLASS32;
+#endif
+ if (cProgHdrs >= PN_XNUM)
+ ElfHdr.e_phnum = PN_XNUM;
+ else
+ ElfHdr.e_phnum = cProgHdrs;
+ ElfHdr.e_ehsize = sizeof(ElfHdr);
+ ElfHdr.e_phoff = sizeof(ElfHdr);
+ ElfHdr.e_phentsize = sizeof(Elf_Phdr);
+ ElfHdr.e_shentsize = sizeof(Elf_Shdr);
+ int rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &ElfHdr, sizeof(ElfHdr));
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "WriteCore: pfnWriter failed writing ELF header. rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Setup program header.
+ */
+ Elf_Phdr ProgHdr;
+ RT_ZERO(ProgHdr);
+ ProgHdr.p_type = PT_NOTE;
+ ProgHdr.p_flags = PF_R;
+
+ /*
+ * Write old-style NOTE program header.
+ */
+ pSolCore->offWrite += sizeof(ElfHdr) + cProgHdrs * sizeof(ProgHdr);
+ ProgHdr.p_offset = pSolCore->offWrite;
+ ProgHdr.p_filesz = ElfNoteSectionSize(pSolCore, enmOldEra);
+ rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &ProgHdr, sizeof(ProgHdr));
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "WriteCore: pfnWriter failed writing old-style ELF program Header. rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Write new-style NOTE program header.
+ */
+ pSolCore->offWrite += ProgHdr.p_filesz;
+ ProgHdr.p_offset = pSolCore->offWrite;
+ ProgHdr.p_filesz = ElfNoteSectionSize(pSolCore, enmNewEra);
+ rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &ProgHdr, sizeof(ProgHdr));
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "WriteCore: pfnWriter failed writing new-style ELF program header. rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Write program headers per mapping.
+ */
+ pSolCore->offWrite += ProgHdr.p_filesz;
+ rc = ElfWriteMappingHeaders(pSolCore);
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "Write: ElfWriteMappings failed. rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Write old-style note section.
+ */
+ rc = ElfWriteNoteSection(pSolCore, enmOldEra);
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "WriteCore: ElfWriteNoteSection old-style failed. rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Write new-style section.
+ */
+ rc = ElfWriteNoteSection(pSolCore, enmNewEra);
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "WriteCore: ElfWriteNoteSection new-style failed. rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Write all mappings.
+ */
+ rc = ElfWriteMappings(pSolCore);
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "WriteCore: ElfWriteMappings failed. rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Write a prepared core file using a user-passed in writer function, requires all threads
+ * to be in suspended state (i.e. called after CreateCore).
+ *
+ * @return IPRT status code.
+ * @param pSolCore Pointer to the core object.
+ * @param pfnWriter Pointer to the writer function to override default writer (NULL uses default).
+ *
+ * @remarks Resumes all suspended threads, unless it's an invalid core. This
+ * function must be called only -after- rtCoreDumperCreateCore().
+ */
+static int rtCoreDumperWriteCore(PRTSOLCORE pSolCore, PFNRTCOREWRITER pfnWriter)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+
+ if (!pSolCore->fIsValid)
+ return VERR_INVALID_STATE;
+
+ if (pfnWriter)
+ pSolCore->pfnWriter = pfnWriter;
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ char szPath[PATH_MAX];
+ int rc;
+
+ /*
+ * Open the process address space file.
+ */
+ RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/as", (int)pSolProc->Process);
+ int fd = open(szPath, O_RDONLY);
+ if (fd >= 0)
+ {
+ pSolProc->fdAs = fd;
+
+ /*
+ * Create the core file.
+ */
+ fd = open(pSolCore->szCorePath, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR);
+ if (fd >= 0)
+ {
+ pSolCore->fdCoreFile = fd;
+
+ /*
+ * Do the actual writing.
+ */
+ rc = rtCoreDumperWriteCoreDoIt(pSolCore, pfnWriter, pSolProc);
+
+ close(pSolCore->fdCoreFile);
+ pSolCore->fdCoreFile = -1;
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(fd);
+ CORELOGRELSYS((CORELOG_NAME "WriteCore: failed to open %s. rc=%Rrc\n", pSolCore->szCorePath, rc));
+ }
+ close(pSolProc->fdAs);
+ pSolProc->fdAs = -1;
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(fd);
+ CORELOGRELSYS((CORELOG_NAME "WriteCore: Failed to open address space, %s. rc=%Rrc\n", szPath, rc));
+ }
+
+ rtCoreDumperResumeThreads(pSolCore);
+ return rc;
+}
+
+
+/**
+ * Takes a process snapshot into a passed-in core object. It has the side-effect of halting
+ * all threads which can lead to things like spurious wakeups of threads (if and when threads
+ * are ultimately resumed en-masse) already suspended while calling this function.
+ *
+ * @return IPRT status code.
+ * @param pSolCore Pointer to a core object.
+ * @param pContext Pointer to the caller context thread.
+ * @param pszCoreFilePath Path to the core file. If NULL is passed, the global
+ * path specified in RTCoreDumperSetup() would be used.
+ *
+ * @remarks Halts all threads.
+ */
+static int rtCoreDumperCreateCore(PRTSOLCORE pSolCore, ucontext_t *pContext, const char *pszCoreFilePath)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+ AssertReturn(pContext, VERR_INVALID_POINTER);
+
+ /*
+ * Initialize core structures.
+ */
+ memset(pSolCore, 0, sizeof(RTSOLCORE));
+ pSolCore->pfnReader = &ReadFileNoIntr;
+ pSolCore->pfnWriter = &WriteFileNoIntr;
+ pSolCore->fIsValid = false;
+ pSolCore->fdCoreFile = -1;
+
+ PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc;
+ pSolProc->Process = RTProcSelf();
+ pSolProc->hCurThread = _lwp_self(); /* thr_self() */
+ pSolProc->fdAs = -1;
+ pSolProc->pCurThreadCtx = pContext;
+ pSolProc->CoreContent = CC_CONTENT_DEFAULT;
+
+ RTProcGetExecutablePath(pSolProc->szExecPath, sizeof(pSolProc->szExecPath)); /* this gets full path not just name */
+ pSolProc->pszExecName = RTPathFilename(pSolProc->szExecPath);
+
+ /*
+ * If a path has been specified, use it. Otherwise use the global path.
+ */
+ if (!pszCoreFilePath)
+ {
+ /*
+ * If no output directory is specified, use current directory.
+ */
+ if (g_szCoreDumpDir[0] == '\0')
+ g_szCoreDumpDir[0] = '.';
+
+ if (g_szCoreDumpFile[0] == '\0')
+ {
+ /* We cannot call RTPathAbs*() as they call getcwd() which calls malloc. */
+ RTStrPrintf(pSolCore->szCorePath, sizeof(pSolCore->szCorePath), "%s/core.vb.%s.%d",
+ g_szCoreDumpDir, pSolProc->pszExecName, (int)pSolProc->Process);
+ }
+ else
+ RTStrPrintf(pSolCore->szCorePath, sizeof(pSolCore->szCorePath), "%s/core.vb.%s", g_szCoreDumpDir, g_szCoreDumpFile);
+ }
+ else
+ RTStrCopy(pSolCore->szCorePath, sizeof(pSolCore->szCorePath), pszCoreFilePath);
+
+ CORELOG((CORELOG_NAME "CreateCore: Taking Core %s from Thread %d\n", pSolCore->szCorePath, (int)pSolProc->hCurThread));
+
+ /*
+ * Quiesce the process.
+ */
+ int rc = rtCoreDumperSuspendThreads(pSolCore);
+ if (RT_SUCCESS(rc))
+ {
+ rc = AllocMemoryArea(pSolCore);
+ if (RT_SUCCESS(rc))
+ {
+ rc = ProcReadInfo(pSolCore);
+ if (RT_SUCCESS(rc))
+ {
+ rc = GetOldProcessInfo(pSolCore, &pSolProc->ProcInfoOld);
+ if (RT_SUCCESS(rc))
+ {
+ if (IsProcessArchNative(pSolProc))
+ {
+ /*
+ * Read process status, information such as number of active LWPs will be
+ * invalid since we just quiesced the process.
+ */
+ rc = ProcReadStatus(pSolCore);
+ if (RT_SUCCESS(rc))
+ {
+ struct COREACCUMULATOR
+ {
+ const char *pszName;
+ PFNRTSOLCOREACCUMULATOR pfnAcc;
+ bool fOptional;
+ } aAccumulators[] =
+ {
+ { "ProcReadLdt", &ProcReadLdt, false },
+ { "ProcReadCred", &ProcReadCred, false },
+ { "ProcReadPriv", &ProcReadPriv, false },
+ { "ProcReadAuxVecs", &ProcReadAuxVecs, false },
+ { "ProcReadMappings", &ProcReadMappings, false },
+ { "ProcReadThreads", &ProcReadThreads, false },
+ { "ProcReadMiscInfo", &ProcReadMiscInfo, false }
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(aAccumulators); i++)
+ {
+ rc = aAccumulators[i].pfnAcc(pSolCore);
+ if (RT_FAILURE(rc))
+ {
+ CORELOGRELSYS((CORELOG_NAME "CreateCore: %s failed. rc=%Rrc\n", aAccumulators[i].pszName, rc));
+ if (!aAccumulators[i].fOptional)
+ break;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pSolCore->fIsValid = true;
+ return VINF_SUCCESS;
+ }
+
+ FreeMemoryArea(pSolCore);
+ }
+ else
+ CORELOGRELSYS((CORELOG_NAME "CreateCore: ProcReadStatus failed. rc=%Rrc\n", rc));
+ }
+ else
+ {
+ CORELOGRELSYS((CORELOG_NAME "CreateCore: IsProcessArchNative failed.\n"));
+ rc = VERR_BAD_EXE_FORMAT;
+ }
+ }
+ else
+ CORELOGRELSYS((CORELOG_NAME "CreateCore: GetOldProcessInfo failed. rc=%Rrc\n", rc));
+ }
+ else
+ CORELOGRELSYS((CORELOG_NAME "CreateCore: ProcReadInfo failed. rc=%Rrc\n", rc));
+ }
+ else
+ CORELOGRELSYS((CORELOG_NAME "CreateCore: AllocMemoryArea failed. rc=%Rrc\n", rc));
+
+ /*
+ * Resume threads on failure.
+ */
+ rtCoreDumperResumeThreads(pSolCore);
+ }
+ else
+ CORELOG((CORELOG_NAME "CreateCore: SuspendAllThreads failed. Thread bomb!?! rc=%Rrc\n", rc));
+
+ return rc;
+}
+
+
+/**
+ * Destroy an existing core object.
+ *
+ * @param pSolCore Pointer to the core object.
+ *
+ * @return IPRT status code.
+ */
+static int rtCoreDumperDestroyCore(PRTSOLCORE pSolCore)
+{
+ AssertReturn(pSolCore, VERR_INVALID_POINTER);
+ if (!pSolCore->fIsValid)
+ return VERR_INVALID_STATE;
+
+ FreeMemoryArea(pSolCore);
+ pSolCore->fIsValid = false;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Takes a core dump.
+ *
+ * @param pContext The context of the caller.
+ * @param pszOutputFile Path of the core file. If NULL is passed, the
+ * global path passed in RTCoreDumperSetup will
+ * be used.
+ * @returns IPRT status code.
+ */
+static int rtCoreDumperTakeDump(ucontext_t *pContext, const char *pszOutputFile)
+{
+ if (!pContext)
+ {
+ CORELOGRELSYS((CORELOG_NAME "TakeDump: Missing context.\n"));
+ return VERR_INVALID_POINTER;
+ }
+
+ /*
+ * Take a snapshot, then dump core to disk, all threads except this one are halted
+ * from before taking the snapshot until writing the core is completely finished.
+ * Any errors would resume all threads if they were halted.
+ */
+ RTSOLCORE SolCore;
+ RT_ZERO(SolCore);
+ int rc = rtCoreDumperCreateCore(&SolCore, pContext, pszOutputFile);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtCoreDumperWriteCore(&SolCore, &WriteFileNoIntr);
+ if (RT_SUCCESS(rc))
+ CORELOGRELSYS((CORELOG_NAME "Core dumped in %s\n", SolCore.szCorePath));
+ else
+ CORELOGRELSYS((CORELOG_NAME "TakeDump: WriteCore failed. szCorePath=%s rc=%Rrc\n", SolCore.szCorePath, rc));
+
+ rtCoreDumperDestroyCore(&SolCore);
+ }
+ else
+ CORELOGRELSYS((CORELOG_NAME "TakeDump: CreateCore failed. rc=%Rrc\n", rc));
+
+ return rc;
+}
+
+
+/**
+ * The signal handler that will be invoked to take core dumps.
+ *
+ * @param Sig The signal that invoked us.
+ * @param pSigInfo The signal information.
+ * @param pvArg Opaque pointer to the caller context structure,
+ * this cannot be NULL.
+ */
+static void rtCoreDumperSignalHandler(int Sig, siginfo_t *pSigInfo, void *pvArg)
+{
+ CORELOG((CORELOG_NAME "SignalHandler Sig=%d pvArg=%p\n", Sig, pvArg));
+
+ RTNATIVETHREAD hCurNativeThread = RTThreadNativeSelf();
+ int rc = VERR_GENERAL_FAILURE;
+ bool fCallSystemDump = false;
+ bool fRc;
+ ASMAtomicCmpXchgHandle(&g_CoreDumpThread, hCurNativeThread, NIL_RTNATIVETHREAD, fRc);
+ if (fRc)
+ {
+ rc = rtCoreDumperTakeDump((ucontext_t *)pvArg, NULL /* Use Global Core filepath */);
+ ASMAtomicWriteHandle(&g_CoreDumpThread, NIL_RTNATIVETHREAD);
+
+ if (RT_FAILURE(rc))
+ CORELOGRELSYS((CORELOG_NAME "TakeDump failed! rc=%Rrc\n", rc));
+ }
+ else if (Sig == SIGSEGV || Sig == SIGBUS || Sig == SIGTRAP)
+ {
+ /*
+ * Core dumping is already in progress and we've somehow ended up being
+ * signalled again.
+ */
+ rc = VERR_INTERNAL_ERROR;
+
+ /*
+ * If our dumper has crashed. No point in waiting, trigger the system one.
+ * Wait only when the dumping thread is not the one generating this signal.
+ */
+ RTNATIVETHREAD hNativeDumperThread;
+ ASMAtomicReadHandle(&g_CoreDumpThread, &hNativeDumperThread);
+ if (hNativeDumperThread == RTThreadNativeSelf())
+ {
+ CORELOGRELSYS((CORELOG_NAME "SignalHandler: Core dumper (thread %u) crashed Sig=%d. Triggering system dump\n",
+ RTThreadSelf(), Sig));
+ fCallSystemDump = true;
+ }
+ else
+ {
+ /*
+ * Some other thread in the process is triggering a crash, wait a while
+ * to let our core dumper finish, on timeout trigger system dump.
+ */
+ CORELOGRELSYS((CORELOG_NAME "SignalHandler: Core dump already in progress! Waiting a while for completion Sig=%d.\n",
+ Sig));
+ int64_t iTimeout = 16000; /* timeout (ms) */
+ for (;;)
+ {
+ ASMAtomicReadHandle(&g_CoreDumpThread, &hNativeDumperThread);
+ if (hNativeDumperThread == NIL_RTNATIVETHREAD)
+ break;
+ RTThreadSleep(200);
+ iTimeout -= 200;
+ if (iTimeout <= 0)
+ break;
+ }
+ if (iTimeout <= 0)
+ {
+ fCallSystemDump = true;
+ CORELOGRELSYS((CORELOG_NAME "SignalHandler: Core dumper seems to be stuck. Signalling new signal %d\n", Sig));
+ }
+ }
+ }
+
+ if (Sig == SIGSEGV || Sig == SIGBUS || Sig == SIGTRAP)
+ {
+ /*
+ * Reset signal handlers, we're not a live core we will be blown away
+ * one way or another.
+ */
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGBUS, SIG_DFL);
+ signal(SIGTRAP, SIG_DFL);
+
+ /*
+ * Hard terminate the process if this is not a live dump without invoking
+ * the system core dumping behaviour.
+ */
+ if (RT_SUCCESS(rc))
+ raise(SIGKILL);
+
+ /*
+ * Something went wrong, fall back to the system core dumper.
+ */
+ if (fCallSystemDump)
+ abort();
+ }
+}
+
+
+RTDECL(int) RTCoreDumperTakeDump(const char *pszOutputFile, bool fLiveCore)
+{
+ ucontext_t Context;
+ int rc = getcontext(&Context);
+ if (!rc)
+ {
+ /*
+ * Block SIGSEGV and co. while we write the core.
+ */
+ sigset_t SigSet, OldSigSet;
+ sigemptyset(&SigSet);
+ sigaddset(&SigSet, SIGSEGV);
+ sigaddset(&SigSet, SIGBUS);
+ sigaddset(&SigSet, SIGTRAP);
+ sigaddset(&SigSet, SIGUSR2);
+ pthread_sigmask(SIG_BLOCK, &SigSet, &OldSigSet);
+ rc = rtCoreDumperTakeDump(&Context, pszOutputFile);
+ if (RT_FAILURE(rc))
+ CORELOGRELSYS(("RTCoreDumperTakeDump: rtCoreDumperTakeDump failed rc=%Rrc\n", rc));
+
+ if (!fLiveCore)
+ {
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGBUS, SIG_DFL);
+ signal(SIGTRAP, SIG_DFL);
+ if (RT_SUCCESS(rc))
+ raise(SIGKILL);
+ else
+ abort();
+ }
+ pthread_sigmask(SIG_SETMASK, &OldSigSet, NULL);
+ }
+ else
+ {
+ CORELOGRELSYS(("RTCoreDumperTakeDump: getcontext failed rc=%d.\n", rc));
+ rc = VERR_INVALID_CONTEXT;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTCoreDumperSetup(const char *pszOutputDir, uint32_t fFlags)
+{
+ /*
+ * Validate flags.
+ */
+ AssertReturn(fFlags, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~( RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP
+ | RTCOREDUMPER_FLAGS_LIVE_CORE)),
+ VERR_INVALID_PARAMETER);
+
+
+ /*
+ * Setup/change the core dump directory if specified.
+ */
+ RT_ZERO(g_szCoreDumpDir);
+ if (pszOutputDir)
+ {
+ if (!RTDirExists(pszOutputDir))
+ return VERR_NOT_A_DIRECTORY;
+ RTStrCopy(g_szCoreDumpDir, sizeof(g_szCoreDumpDir), pszOutputDir);
+ }
+
+ /*
+ * Install core dump signal handler only if the flags changed or if it's the first time.
+ */
+ if ( ASMAtomicReadBool(&g_fCoreDumpSignalSetup) == false
+ || ASMAtomicReadU32(&g_fCoreDumpFlags) != fFlags)
+ {
+ struct sigaction sigAct;
+ RT_ZERO(sigAct);
+ sigAct.sa_sigaction = &rtCoreDumperSignalHandler;
+
+ if ( (fFlags & RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP)
+ && !(g_fCoreDumpFlags & RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP))
+ {
+ sigemptyset(&sigAct.sa_mask);
+ sigAct.sa_flags = SA_RESTART | SA_SIGINFO | SA_NODEFER;
+ sigaction(SIGSEGV, &sigAct, NULL);
+ sigaction(SIGBUS, &sigAct, NULL);
+ sigaction(SIGTRAP, &sigAct, NULL);
+ }
+
+ if ( fFlags & RTCOREDUMPER_FLAGS_LIVE_CORE
+ && !(g_fCoreDumpFlags & RTCOREDUMPER_FLAGS_LIVE_CORE))
+ {
+ sigfillset(&sigAct.sa_mask); /* Block all signals while in it's signal handler */
+ sigAct.sa_flags = SA_RESTART | SA_SIGINFO;
+ sigaction(SIGUSR2, &sigAct, NULL);
+ }
+
+ ASMAtomicWriteU32(&g_fCoreDumpFlags, fFlags);
+ ASMAtomicWriteBool(&g_fCoreDumpSignalSetup, true);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTCoreDumperDisable(void)
+{
+ /*
+ * Remove core dump signal handler & reset variables.
+ */
+ if (ASMAtomicReadBool(&g_fCoreDumpSignalSetup) == true)
+ {
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGBUS, SIG_DFL);
+ signal(SIGTRAP, SIG_DFL);
+ signal(SIGUSR2, SIG_DFL);
+ ASMAtomicWriteBool(&g_fCoreDumpSignalSetup, false);
+ }
+
+ RT_ZERO(g_szCoreDumpDir);
+ RT_ZERO(g_szCoreDumpFile);
+ ASMAtomicWriteU32(&g_fCoreDumpFlags, 0);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/solaris/coredumper-solaris.h b/src/VBox/Runtime/r3/solaris/coredumper-solaris.h
new file mode 100644
index 00000000..4583eb4d
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/coredumper-solaris.h
@@ -0,0 +1,177 @@
+/* $Id: coredumper-solaris.h $ */
+/** @file
+ * IPRT - Custom Core Dumper, Solaris.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_solaris_coredumper_solaris_h
+#define IPRT_INCLUDED_SRC_r3_solaris_coredumper_solaris_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+#ifdef RT_OS_SOLARIS
+# if defined(RT_ARCH_X86) && _FILE_OFFSET_BITS==64
+/*
+ * Solaris' procfs cannot be used with large file environment in 32-bit.
+ */
+# undef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 32
+# include <procfs.h>
+# include <sys/procfs.h>
+# include <sys/old_procfs.h>
+# undef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 64
+#else
+# include <procfs.h>
+# include <sys/procfs.h>
+# include <sys/old_procfs.h>
+#endif
+# include <limits.h>
+# include <thread.h>
+# include <sys/auxv.h>
+# include <sys/lwp.h>
+# include <sys/zone.h>
+# include <sys/utsname.h>
+
+#ifdef RT_ARCH_AMD64
+# define _ELF64
+# undef _ELF32_COMPAT
+#endif
+# include <sys/corectl.h>
+#endif
+
+
+#ifdef RT_OS_SOLARIS
+/**
+ * Memory mapping descriptor employed by the solaris core dumper.
+ */
+typedef struct RTSOLCOREMAPINFO
+{
+ prmap_t pMap; /**< Proc description of this mapping */
+ int fError; /**< Any error reading this mapping (errno) */
+ struct RTSOLCOREMAPINFO *pNext; /**< Pointer to the next mapping */
+} RTSOLCOREMAPINFO;
+/** Pointer to a solaris memory mapping descriptor. */
+typedef RTSOLCOREMAPINFO *PRTSOLCOREMAPINFO;
+
+/**
+ * Whether this is an old or new style solaris core.
+ */
+typedef enum RTSOLCORETYPE
+{
+ enmOldEra = 0x01d, /**< old */
+ enmNewEra = 0x5c1f1 /**< sci-fi */
+} RTSOLCORETYPE;
+
+/**
+ * Per-Thread information employed by the solaris core dumper.
+ */
+typedef struct RTSOLCORETHREADINFO
+{
+ lwpsinfo_t Info; /**< Proc description of this thread */
+ lwpstatus_t *pStatus; /**< Proc description of this thread's status (can be NULL, zombie lwp) */
+ struct RTSOLCORETHREADINFO *pNext; /**< Pointer to the next thread */
+} RTSOLCORETHREADINFO;
+typedef RTSOLCORETHREADINFO *PRTSOLCORETHREADINFO;
+#endif
+
+
+/**
+ * Current (also the core target) process information.
+ */
+typedef struct RTSOLCOREPROCESS
+{
+ RTPROCESS Process; /**< The pid of the process */
+ char szExecPath[PATH_MAX]; /**< Path of the executable */
+ char *pszExecName; /**< Name of the executable file */
+#ifdef RT_OS_SOLARIS
+ void *pvProcInfo; /**< Process info. */
+ size_t cbProcInfo; /**< Size of the process info. */
+ prpsinfo_t ProcInfoOld; /**< Process info. Older version (for GDB compat.) */
+ pstatus_t ProcStatus; /**< Process status info. */
+ thread_t hCurThread; /**< The current thread */
+ ucontext_t *pCurThreadCtx; /**< Context info. of current thread before starting to dump */
+ int fdAs; /**< proc/pid/as file handle */
+ auxv_t *pAuxVecs; /**< Aux vector of process */
+ int cAuxVecs; /**< Number of aux vector entries */
+ PRTSOLCOREMAPINFO pMapInfoHead; /**< Pointer to the head of list of mappings */
+ uint32_t cMappings; /**< Number of mappings (count of pMapInfoHead list) */
+ PRTSOLCORETHREADINFO pThreadInfoHead; /**< Pointer to the head of list of threads */
+ uint64_t cThreads; /**< Number of threads (count of pThreadInfoHead list) */
+ char szPlatform[SYS_NMLN]; /**< Platform name */
+ char szZoneName[ZONENAME_MAX]; /**< Zone name */
+ struct utsname UtsName; /**< UTS name */
+ void *pvCred; /**< Process credential info. */
+ size_t cbCred; /**< Size of process credential info. */
+ void *pvLdt; /**< Process LDT info. */
+ size_t cbLdt; /**< Size of the LDT info. */
+ prpriv_t *pPriv; /**< Process privilege info. */
+ size_t cbPriv; /**< Size of process privilege info. */
+ const priv_impl_info_t *pcPrivImpl; /**< Process privilege implementation info. (opaque handle) */
+ core_content_t CoreContent; /**< What information goes in the core */
+#else
+# error Port Me!
+#endif
+
+} RTSOLCOREPROCESS;
+typedef RTSOLCOREPROCESS *PRTSOLCOREPROCESS;
+
+typedef int (*PFNRTCOREREADER)(int fdFile, void *pv, size_t cb);
+typedef int (*PFNRTCOREWRITER)(int fdhFile, const void *pcv, size_t cb);
+
+/**
+ * The solaris core file object.
+ */
+typedef struct RTSOLCORE
+{
+ char szCorePath[PATH_MAX]; /**< Path of the core file */
+ RTSOLCOREPROCESS SolProc; /**< Current process information */
+ void *pvCore; /**< Pointer to memory area during dumping */
+ size_t cbCore; /**< Size of memory area during dumping */
+ void *pvFree; /**< Pointer to base of free range in preallocated memory area */
+ bool fIsValid; /**< Whether core information has been fully collected */
+ PFNRTCOREREADER pfnReader; /**< Reader function */
+ PFNRTCOREWRITER pfnWriter; /**< Writer function */
+ int fdCoreFile; /**< Core file (used only while writing the core) */
+ RTFOFF offWrite; /**< Segment/section offset (used only while writing the core) */
+} RTSOLCORE;
+typedef RTSOLCORE *PRTSOLCORE;
+
+typedef int (*PFNRTSOLCOREACCUMULATOR)(PRTSOLCORE pSolCore);
+typedef int (*PFNRTSOLCORETHREADWORKER)(PRTSOLCORE pSolCore, void *pvThreadInfo);
+
+#endif /* !IPRT_INCLUDED_SRC_r3_solaris_coredumper_solaris_h */
+
diff --git a/src/VBox/Runtime/r3/solaris/fileaio-solaris.cpp b/src/VBox/Runtime/r3/solaris/fileaio-solaris.cpp
new file mode 100644
index 00000000..f129d4fc
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/fileaio-solaris.cpp
@@ -0,0 +1,574 @@
+/* $Id: fileaio-solaris.cpp $ */
+/** @file
+ * IPRT - File async I/O, native implementation for the Solaris host platform.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FILE
+#include <iprt/asm.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/fileaio.h"
+
+#include <port.h>
+#include <aio.h>
+#include <errno.h>
+#include <unistd.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Async I/O completion context state.
+ */
+typedef struct RTFILEAIOCTXINTERNAL
+{
+ /** Handle to the port. */
+ int iPort;
+ /** Current number of requests active on this context. */
+ volatile int32_t cRequests;
+ /** Flags given during creation. */
+ uint32_t fFlags;
+ /** Magic value (RTFILEAIOCTX_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOCTXINTERNAL;
+/** Pointer to an internal context structure. */
+typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
+
+/**
+ * Async I/O request state.
+ */
+typedef struct RTFILEAIOREQINTERNAL
+{
+ /** The aio control block. Must be the FIRST
+ * element. */
+ struct aiocb AioCB;
+ /** Current state the request is in. */
+ RTFILEAIOREQSTATE enmState;
+ /** Flag whether this is a flush request. */
+ bool fFlush;
+ /** Port notifier object to associate a request to a port. */
+ port_notify_t PortNotifier;
+ /** Opaque user data. */
+ void *pvUser;
+ /** Completion context we are assigned to. */
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ /** Magic value (RTFILEAIOREQ_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOREQINTERNAL;
+/** Pointer to an internal request structure. */
+typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The max number of events to get in one call. */
+#define AIO_MAXIMUM_REQUESTS_PER_CONTEXT 64
+/** Id for the wakeup event. */
+#define AIO_CONTEXT_WAKEUP_EVENT 1
+
+RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
+{
+ int rcBSD = 0;
+ AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
+
+ /* No limits known. */
+ pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS;
+ pAioLimits->cbBufferAlignment = 0;
+
+ return VINF_SUCCESS;
+}
+
+RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
+{
+ AssertPtrReturn(phReq, VERR_INVALID_POINTER);
+
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL));
+ if (RT_UNLIKELY(!pReqInt))
+ return VERR_NO_MEMORY;
+
+ /* Ininitialize static parts. */
+ pReqInt->AioCB.aio_sigevent.sigev_notify = SIGEV_PORT;
+ pReqInt->AioCB.aio_sigevent.sigev_value.sival_ptr = &pReqInt->PortNotifier;
+ pReqInt->PortNotifier.portnfy_user = pReqInt;
+ pReqInt->pCtxInt = NULL;
+ pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ *phReq = (RTFILEAIOREQ)pReqInt;
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
+{
+ /*
+ * Validate the handle and ignore nil.
+ */
+ if (hReq == NIL_RTFILEAIOREQ)
+ return VINF_SUCCESS;
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+
+ /*
+ * Trash the magic and free it.
+ */
+ ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
+ RTMemFree(pReqInt);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Worker setting up the request.
+ */
+DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
+ unsigned uTransferDirection,
+ RTFOFF off, void *pvBuf, size_t cbTransfer,
+ void *pvUser)
+{
+ /*
+ * Validate the input.
+ */
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ Assert(hFile != NIL_RTFILE);
+ AssertPtr(pvBuf);
+ Assert(off >= 0);
+ Assert(cbTransfer > 0);
+
+ pReqInt->AioCB.aio_lio_opcode = uTransferDirection;
+ pReqInt->AioCB.aio_fildes = RTFileToNative(hFile);
+ pReqInt->AioCB.aio_offset = off;
+ pReqInt->AioCB.aio_nbytes = cbTransfer;
+ pReqInt->AioCB.aio_buf = pvBuf;
+ pReqInt->fFlush = false;
+ pReqInt->pvUser = pvUser;
+ pReqInt->pCtxInt = NULL;
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void *pvBuf, size_t cbRead, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, LIO_READ,
+ off, pvBuf, cbRead, pvUser);
+}
+
+RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void const *pvBuf, size_t cbWrite, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, LIO_WRITE,
+ off, (void *)pvBuf, cbWrite, pvUser);
+}
+
+RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)hReq;
+
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ Assert(hFile != NIL_RTFILE);
+
+ pReqInt->fFlush = true;
+ pReqInt->AioCB.aio_fildes = RTFileToNative(hFile);
+ pReqInt->AioCB.aio_offset = 0;
+ pReqInt->AioCB.aio_nbytes = 0;
+ pReqInt->AioCB.aio_buf = NULL;
+ pReqInt->pvUser = pvUser;
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
+
+ return pReqInt->pvUser;
+}
+
+RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ int rcSolaris = aio_cancel(pReqInt->AioCB.aio_fildes, &pReqInt->AioCB);
+
+ if (rcSolaris == AIO_CANCELED)
+ {
+ /*
+ * Decrement request count because the request will never arrive at the
+ * completion port.
+ */
+ AssertMsg(RT_VALID_PTR(pReqInt->pCtxInt), ("Invalid state. Request was canceled but wasn't submitted\n"));
+
+ ASMAtomicDecS32(&pReqInt->pCtxInt->cRequests);
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ return VINF_SUCCESS;
+ }
+ else if (rcSolaris == AIO_ALLDONE)
+ return VERR_FILE_AIO_COMPLETED;
+ else if (rcSolaris == AIO_NOTCANCELED)
+ return VERR_FILE_AIO_IN_PROGRESS;
+ else
+ return RTErrConvertFromErrno(errno);
+}
+
+RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
+ AssertPtrNull(pcbTransfered);
+
+ int rcSol = aio_error(&pReqInt->AioCB);
+ Assert(rcSol != EINPROGRESS); /* Handled by our own state handling. */
+
+ if (rcSol == 0)
+ {
+ if (pcbTransfered)
+ *pcbTransfered = aio_return(&pReqInt->AioCB);
+ return VINF_SUCCESS;
+ }
+
+ /* An error occurred. */
+ return RTErrConvertFromErrno(rcSol);
+}
+
+RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax,
+ uint32_t fFlags)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
+ if (RT_UNLIKELY(!pCtxInt))
+ return VERR_NO_MEMORY;
+
+ /* Init the event handle. */
+ pCtxInt->iPort = port_create();
+ if (RT_LIKELY(pCtxInt->iPort > 0))
+ {
+ pCtxInt->fFlags = fFlags;
+ pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
+ *phAioCtx = (RTFILEAIOCTX)pCtxInt;
+ }
+ else
+ {
+ RTMemFree(pCtxInt);
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
+{
+ /* Validate the handle and ignore nil. */
+ if (hAioCtx == NIL_RTFILEAIOCTX)
+ return VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ /* Cannot destroy a busy context. */
+ if (RT_UNLIKELY(pCtxInt->cRequests))
+ return VERR_FILE_AIO_BUSY;
+
+ close(pCtxInt->iPort);
+ ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
+ RTMemFree(pCtxInt);
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
+{
+ return RTFILEAIO_UNLIMITED_REQS;
+}
+
+RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
+{
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
+{
+ /*
+ * Parameter validation.
+ */
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ size_t i = cReqs;
+
+ do
+ {
+ int rcSol = 0;
+ size_t cReqsSubmit = 0;
+ PRTFILEAIOREQINTERNAL pReqInt;
+
+ while(i-- > 0)
+ {
+ pReqInt = pahReqs[i];
+ if (RTFILEAIOREQ_IS_NOT_VALID(pReqInt))
+ {
+ /* Undo everything and stop submitting. */
+ for (size_t iUndo = 0; iUndo < i; iUndo++)
+ {
+ pReqInt = pahReqs[iUndo];
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+ pReqInt->pCtxInt = NULL;
+ }
+ rc = VERR_INVALID_HANDLE;
+ break;
+ }
+
+ pReqInt->PortNotifier.portnfy_port = pCtxInt->iPort;
+ pReqInt->pCtxInt = pCtxInt;
+ RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
+
+ if (pReqInt->fFlush)
+ break;
+
+ cReqsSubmit++;
+ }
+
+ if (cReqsSubmit)
+ {
+ rcSol = lio_listio(LIO_NOWAIT, (struct aiocb **)pahReqs, cReqsSubmit, NULL);
+ if (RT_UNLIKELY(rcSol < 0))
+ {
+ if (rcSol == EAGAIN)
+ rc = VERR_FILE_AIO_INSUFFICIENT_RESSOURCES;
+ else
+ rc = RTErrConvertFromErrno(errno);
+
+ /* Check which requests got actually submitted and which not. */
+ for (i = 0; i < cReqs; i++)
+ {
+ pReqInt = pahReqs[i];
+ rcSol = aio_error(&pReqInt->AioCB);
+ if (rcSol == EINVAL)
+ {
+ /* Was not submitted. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED);
+ pReqInt->pCtxInt = NULL;
+ }
+ else if (rcSol != EINPROGRESS)
+ {
+ /* The request encountered an error. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ }
+ }
+ break;
+ }
+
+ ASMAtomicAddS32(&pCtxInt->cRequests, cReqsSubmit);
+ cReqs -= cReqsSubmit;
+ pahReqs += cReqsSubmit;
+ }
+
+ if (cReqs)
+ {
+ pReqInt = pahReqs[0];
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+
+ /*
+ * If there are still requests left we have a flush request.
+ * lio_listio does not work with this requests so
+ * we have to use aio_fsync directly.
+ */
+ rcSol = aio_fsync(O_SYNC, &pReqInt->AioCB);
+ if (RT_UNLIKELY(rcSol < 0))
+ {
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+
+ ASMAtomicIncS32(&pCtxInt->cRequests);
+ cReqs--;
+ pahReqs++;
+ }
+ } while (cReqs);
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
+ PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
+{
+ int rc = VINF_SUCCESS;
+ int cRequestsCompleted = 0;
+
+ /*
+ * Validate the parameters, making sure to always set pcReqs.
+ */
+ AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
+ *pcReqs = 0; /* always set */
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
+
+ if ( RT_UNLIKELY(ASMAtomicReadS32(&pCtxInt->cRequests) == 0)
+ && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
+ return VERR_FILE_AIO_NO_REQUEST;
+
+ /*
+ * Convert the timeout if specified.
+ */
+ struct timespec *pTimeout = NULL;
+ struct timespec Timeout = {0,0};
+ uint64_t StartNanoTS = 0;
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ Timeout.tv_sec = cMillies / 1000;
+ Timeout.tv_nsec = cMillies % 1000 * 1000000;
+ pTimeout = &Timeout;
+ StartNanoTS = RTTimeNanoTS();
+ }
+
+ /* Wait for at least one. */
+ if (!cMinReqs)
+ cMinReqs = 1;
+
+ while ( cMinReqs
+ && RT_SUCCESS_NP(rc))
+ {
+ port_event_t aPortEvents[AIO_MAXIMUM_REQUESTS_PER_CONTEXT];
+ uint_t cRequests = cMinReqs;
+ int cRequestsToWait = RT_MIN(cReqs, AIO_MAXIMUM_REQUESTS_PER_CONTEXT);
+ int rcSol;
+ uint64_t StartTime;
+
+ rcSol = port_getn(pCtxInt->iPort, &aPortEvents[0], cRequestsToWait, &cRequests, pTimeout);
+
+ if (RT_UNLIKELY(rcSol < 0))
+ rc = RTErrConvertFromErrno(errno);
+
+ /* Process received events. */
+ for (uint_t i = 0; i < cRequests; i++)
+ {
+ if (aPortEvents[i].portev_source == PORT_SOURCE_ALERT)
+ {
+ Assert(aPortEvents[i].portev_events == AIO_CONTEXT_WAKEUP_EVENT);
+ rc = VERR_INTERRUPTED; /* We've got interrupted. */
+ /* Reset the port. */
+ port_alert(pCtxInt->iPort, PORT_ALERT_SET, 0, NULL);
+ }
+ else
+ {
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)aPortEvents[i].portev_user;
+ AssertPtr(pReqInt);
+ Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
+
+ /* A request has finished. */
+ pahReqs[cRequestsCompleted++] = pReqInt;
+
+ /* Mark the request as finished. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ }
+ }
+
+ /*
+ * Done Yet? If not advance and try again.
+ */
+ if (cRequests >= cMinReqs)
+ break;
+ cMinReqs -= cRequests;
+ cReqs -= cRequests;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ uint64_t NanoTS = RTTimeNanoTS();
+ uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
+
+ /* The syscall supposedly updates it, but we're paranoid. :-) */
+ if (cMilliesElapsed < cMillies)
+ {
+ Timeout.tv_sec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) / 1000;
+ Timeout.tv_nsec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) % 1000 * 1000000;
+ }
+ else
+ {
+ Timeout.tv_sec = 0;
+ Timeout.tv_nsec = 0;
+ }
+ }
+ }
+
+ /*
+ * Update the context state and set the return value.
+ */
+ *pcReqs = cRequestsCompleted;
+ ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ rc = port_alert(pCtxInt->iPort, PORT_ALERT_UPDATE, AIO_CONTEXT_WAKEUP_EVENT, NULL);
+ if (RT_UNLIKELY((rc < 0) && (errno != EBUSY)))
+ return RTErrConvertFromErrno(errno);
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/solaris/krnlmod-solaris.cpp b/src/VBox/Runtime/r3/solaris/krnlmod-solaris.cpp
new file mode 100644
index 00000000..bb02ac55
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/krnlmod-solaris.cpp
@@ -0,0 +1,358 @@
+/* $Id: krnlmod-solaris.cpp $ */
+/** @file
+ * IPRT - Kernel module, Linux.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYSTEM
+#include <iprt/krnlmod.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/dir.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/types.h>
+
+#include <iprt/stream.h>
+
+#include <sys/modctl.h>
+#include <errno.h>
+
+/**
+ * Internal kernel information record state.
+ */
+typedef struct RTKRNLMODINFOINT
+{
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** Load address of the kernel module. */
+ RTR0UINTPTR uLoadAddr;
+ /** Size of the kernel module. */
+ size_t cbKrnlMod;
+ /** Size of the name in characters including the zero terminator. */
+ size_t cchName;
+ /** Module name - variable in size. */
+ char achName[1];
+} RTKRNLMODINFOINT;
+/** Pointer to the internal kernel module information record. */
+typedef RTKRNLMODINFOINT *PRTKRNLMODINFOINT;
+/** Pointer to a const internal kernel module information record. */
+typedef const RTKRNLMODINFOINT *PCRTKRNLMODINFOINT;
+
+
+
+/**
+ * Destroy the given kernel module information record.
+ *
+ * @param pThis The record to destroy.
+ */
+static void rtKrnlModInfoDestroy(PRTKRNLMODINFOINT pThis)
+{
+ RTMemFree(pThis);
+}
+
+
+/**
+ * Creates a new kernel module information record for the given module.
+ *
+ * @returns IPRT status code.
+ * @param pModInfo The Solaris kernel module information.
+ * @param phKrnlModInfo Where to store the handle to the kernel module information record
+ * on success.
+ */
+static int rtKrnlModSolInfoCreate(struct modinfo *pModInfo, PRTKRNLMODINFO phKrnlModInfo)
+{
+ int rc = VINF_SUCCESS;
+ size_t cchName = strlen(&pModInfo->mi_name[0]) + 1;
+ PRTKRNLMODINFOINT pThis = (PRTKRNLMODINFOINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTKRNLMODINFOINT, achName[cchName]));
+ if (RT_LIKELY(pThis))
+ {
+ memcpy(&pThis->achName[0], &pModInfo->mi_name[0], cchName);
+ pThis->cchName = cchName;
+ pThis->cRefs = 1;
+ pThis->cbKrnlMod = pModInfo->mi_size;
+ pThis->uLoadAddr = (RTR0UINTPTR)pModInfo->mi_base;
+
+ *phKrnlModInfo = pThis;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTKrnlModQueryLoaded(const char *pszName, bool *pfLoaded)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfLoaded, VERR_INVALID_POINTER);
+
+ RTKRNLMODINFO hKrnlModInfo = NIL_RTKRNLMODINFO;
+ int rc = RTKrnlModLoadedQueryInfo(pszName, &hKrnlModInfo);
+ if (RT_SUCCESS(rc))
+ {
+ *pfLoaded = true;
+ RTKrnlModInfoRelease(hKrnlModInfo);
+ }
+ else if (rc == VERR_NOT_FOUND)
+ {
+ *pfLoaded = false;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTKrnlModLoadedQueryInfo(const char *pszName, PRTKRNLMODINFO phKrnlModInfo)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(phKrnlModInfo, VERR_INVALID_POINTER);
+
+ int rc = VERR_NOT_FOUND;
+ int iId = -1;
+ struct modinfo ModInfo;
+
+ ModInfo.mi_info = MI_INFO_ALL | MI_INFO_CNT;
+ ModInfo.mi_id = iId;
+ ModInfo.mi_nextid = iId;
+ do
+ {
+ int rcSol = modctl(MODINFO, iId, &ModInfo);
+ if (rcSol < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+
+ if (ModInfo.mi_id != -1)
+ {
+ ModInfo.mi_name[MODMAXNAMELEN - 1] = '\0'; /* Paranoia. */
+ if (!RTStrCmp(pszName, &ModInfo.mi_name[0]))
+ {
+ rc = rtKrnlModSolInfoCreate(&ModInfo, phKrnlModInfo);
+ break;
+ }
+ }
+
+ iId = ModInfo.mi_id;
+ } while (iId != -1);
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTKrnlModLoadedGetCount(void)
+{
+ uint32_t cKmodsLoaded = 0;
+ int iId = -1;
+ struct modinfo ModInfo;
+
+ ModInfo.mi_info = MI_INFO_ALL | MI_INFO_CNT;
+ ModInfo.mi_id = iId;
+ ModInfo.mi_nextid = iId;
+ do
+ {
+ int rcSol = modctl(MODINFO, iId, &ModInfo);
+ if (rcSol < 0)
+ break;
+
+ cKmodsLoaded++;
+
+ iId = ModInfo.mi_id;
+ } while (iId != -1);
+
+ return cKmodsLoaded;
+}
+
+
+RTDECL(int) RTKrnlModLoadedQueryInfoAll(PRTKRNLMODINFO pahKrnlModInfo, uint32_t cEntriesMax,
+ uint32_t *pcEntries)
+{
+ if (cEntriesMax > 0)
+ AssertPtrReturn(pahKrnlModInfo, VERR_INVALID_POINTER);
+
+ uint32_t cKmodsLoaded = RTKrnlModLoadedGetCount();
+ if (cEntriesMax < cKmodsLoaded)
+ {
+ if (*pcEntries)
+ *pcEntries = cKmodsLoaded;
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ int rc = VINF_SUCCESS;
+ int iId = -1;
+ unsigned idxKrnlModInfo = 0;
+ struct modinfo ModInfo;
+
+ ModInfo.mi_info = MI_INFO_ALL | MI_INFO_CNT;
+ ModInfo.mi_id = iId;
+ ModInfo.mi_nextid = iId;
+ do
+ {
+ int rcSol = modctl(MODINFO, iId, &ModInfo);
+ if (rcSol < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ if (rc == VERR_INVALID_PARAMETER && idxKrnlModInfo > 0)
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ ModInfo.mi_name[MODMAXNAMELEN - 1] = '\0'; /* Paranoia. */
+ rc = rtKrnlModSolInfoCreate(&ModInfo, &pahKrnlModInfo[idxKrnlModInfo]);
+ if (RT_SUCCESS(rc))
+ idxKrnlModInfo++;
+
+ iId = ModInfo.mi_id;
+ } while (iId != -1);
+
+ if (RT_FAILURE(rc))
+ {
+ /* Rollback */
+ while (idxKrnlModInfo-- > 0)
+ RTKrnlModInfoRelease(pahKrnlModInfo[idxKrnlModInfo]);
+ }
+ else if (pcEntries)
+ *pcEntries = idxKrnlModInfo;
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoRetain(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoRelease(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ if (!pThis)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0)
+ rtKrnlModInfoDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoGetRefCnt(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return 0;
+}
+
+
+RTDECL(const char *) RTKrnlModInfoGetName(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, NULL);
+
+ return &pThis->achName[0];
+}
+
+
+RTDECL(const char *) RTKrnlModInfoGetFilePath(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, NULL);
+
+ return NULL;
+}
+
+
+RTDECL(size_t) RTKrnlModInfoGetSize(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->cbKrnlMod;
+}
+
+
+RTDECL(RTR0UINTPTR) RTKrnlModInfoGetLoadAddr(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->uLoadAddr;
+}
+
+
+RTDECL(int) RTKrnlModInfoQueryRefModInfo(RTKRNLMODINFO hKrnlModInfo, uint32_t idx,
+ PRTKRNLMODINFO phKrnlModInfoRef)
+{
+ RT_NOREF3(hKrnlModInfo, idx, phKrnlModInfoRef);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTDECL(int) RTKrnlModLoadByName(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModLoadByPath(const char *pszPath)
+{
+ AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModUnloadByName(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
diff --git a/src/VBox/Runtime/r3/solaris/mp-solaris.cpp b/src/VBox/Runtime/r3/solaris/mp-solaris.cpp
new file mode 100644
index 00000000..b412efae
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/mp-solaris.cpp
@@ -0,0 +1,479 @@
+/* $Id: mp-solaris.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Solaris.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <kstat.h>
+#include <sys/processor.h>
+
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/log.h>
+#include <iprt/once.h>
+#include <iprt/string.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Initialization serializing (rtMpSolarisOnce). */
+static RTONCE g_MpSolarisOnce = RTONCE_INITIALIZER;
+/** Critical section serializing access to kstat. */
+static RTCRITSECT g_MpSolarisCritSect;
+/** The kstat handle. */
+static kstat_ctl_t *g_pKsCtl;
+/** Array pointing to the cpu_info instances. */
+static kstat_t **g_papCpuInfo;
+/** The number of entries in g_papCpuInfo */
+static RTCPUID g_capCpuInfo;
+/** Array of core ids. */
+static uint64_t *g_pu64CoreIds;
+/** Number of entries in g_pu64CoreIds. */
+static size_t g_cu64CoreIds;
+/** Number of cores in the system. */
+static size_t g_cCores;
+
+
+/**
+ * Helper for getting the core ID for a given CPU/strand/hyperthread.
+ *
+ * @returns The core ID.
+ * @param idCpu The CPU ID instance.
+ */
+static inline uint64_t rtMpSolarisGetCoreId(RTCPUID idCpu)
+{
+ kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(g_papCpuInfo[idCpu], (char *)"core_id");
+ Assert(pStat->data_type == KSTAT_DATA_LONG);
+ Assert(pStat->value.l >= 0);
+ AssertCompile(sizeof(uint64_t) >= sizeof(long)); /* Paranoia. */
+ return (uint64_t)pStat->value.l;
+}
+
+
+/**
+ * Populates 'g_pu64CoreIds' array with unique core identifiers in the system.
+ *
+ * @returns VBox status code.
+ */
+static int rtMpSolarisGetCoreIds(void)
+{
+ for (RTCPUID idCpu = 0; idCpu < g_capCpuInfo; idCpu++)
+ {
+ /*
+ * It is possible that the number of cores don't match the maximum number
+ * of cores possible on the system. Hence check if we have a valid cpu_info
+ * object. We don't want to break out if it's NULL, the array may be sparse
+ * in theory, see @bugref{8469}.
+ */
+ if (g_papCpuInfo[idCpu])
+ {
+ if (kstat_read(g_pKsCtl, g_papCpuInfo[idCpu], 0) != -1)
+ {
+ /* Strands/Hyperthreads share the same core ID. */
+ uint64_t u64CoreId = rtMpSolarisGetCoreId(idCpu);
+ bool fAddedCore = false;
+ for (RTCPUID i = 0; i < g_cCores; i++)
+ {
+ if (g_pu64CoreIds[i] == u64CoreId)
+ {
+ fAddedCore = true;
+ break;
+ }
+ }
+
+ if (!fAddedCore)
+ {
+ g_pu64CoreIds[g_cCores] = u64CoreId;
+ ++g_cCores;
+ }
+ }
+ else
+ return VERR_INTERNAL_ERROR_2;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Run once function that initializes the kstats we need here.
+ *
+ * @returns IPRT status code.
+ * @param pvUser Unused.
+ */
+static DECLCALLBACK(int) rtMpSolarisOnce(void *pvUser)
+{
+ int rc = VINF_SUCCESS;
+ NOREF(pvUser);
+
+ /*
+ * Open kstat and find the cpu_info entries for each of the CPUs.
+ */
+ g_pKsCtl = kstat_open();
+ if (g_pKsCtl)
+ {
+ g_capCpuInfo = RTMpGetCount();
+ if (RT_LIKELY(g_capCpuInfo > 0))
+ {
+ g_papCpuInfo = (kstat_t **)RTMemAllocZ(g_capCpuInfo * sizeof(kstat_t *));
+ if (g_papCpuInfo)
+ {
+ g_cu64CoreIds = g_capCpuInfo;
+ g_pu64CoreIds = (uint64_t *)RTMemAllocZ(g_cu64CoreIds * sizeof(uint64_t));
+ if (g_pu64CoreIds)
+ {
+ rc = RTCritSectInit(&g_MpSolarisCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ RTCPUID i = 0;
+ for (kstat_t *pKsp = g_pKsCtl->kc_chain; pKsp != NULL; pKsp = pKsp->ks_next)
+ {
+ if (!RTStrCmp(pKsp->ks_module, "cpu_info"))
+ {
+ AssertBreak(i < g_capCpuInfo);
+ g_papCpuInfo[i++] = pKsp;
+ /** @todo ks_instance == cpu_id (/usr/src/uts/common/os/cpu.c)? Check this and fix it ASAP. */
+ }
+ }
+
+ rc = rtMpSolarisGetCoreIds();
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ else
+ Log(("rtMpSolarisGetCoreIds failed. rc=%Rrc\n", rc));
+ }
+
+ RTMemFree(g_pu64CoreIds);
+ g_pu64CoreIds = NULL;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ /* bail out, we failed. */
+ RTMemFree(g_papCpuInfo);
+ g_papCpuInfo = NULL;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_CPU_IPE_1;
+ kstat_close(g_pKsCtl);
+ g_pKsCtl = NULL;
+ }
+ else
+ {
+ rc = RTErrConvertFromErrno(errno);
+ if (RT_SUCCESS(rc))
+ rc = VERR_INTERNAL_ERROR;
+ Log(("kstat_open() -> %d (%Rrc)\n", errno, rc));
+ }
+
+ return rc;
+}
+
+
+/**
+ * RTOnceEx() cleanup function.
+ *
+ * @param pvUser Unused.
+ * @param fLazyCleanUpOk Whether lazy cleanup is okay or not.
+ */
+static DECLCALLBACK(void) rtMpSolarisCleanUp(void *pvUser, bool fLazyCleanUpOk)
+{
+ if (g_pKsCtl)
+ kstat_close(g_pKsCtl);
+ RTMemFree(g_pu64CoreIds);
+ RTMemFree(g_papCpuInfo);
+}
+
+
+/**
+ * Worker for RTMpGetCurFrequency and RTMpGetMaxFrequency.
+ *
+ * @returns The desired frequency on success, 0 on failure.
+ *
+ * @param idCpu The CPU ID.
+ * @param pszStatName The cpu_info stat name.
+ */
+static uint64_t rtMpSolarisGetFrequency(RTCPUID idCpu, const char *pszStatName)
+{
+ uint64_t u64 = 0;
+ int rc = RTOnceEx(&g_MpSolarisOnce, rtMpSolarisOnce, rtMpSolarisCleanUp, NULL /* pvUser */);
+ if (RT_SUCCESS(rc))
+ {
+ if ( idCpu < g_capCpuInfo
+ && g_papCpuInfo[idCpu])
+ {
+ rc = RTCritSectEnter(&g_MpSolarisCritSect);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ if (kstat_read(g_pKsCtl, g_papCpuInfo[idCpu], 0) != -1)
+ {
+ /* Solaris really need to fix their APIs. Explicitly cast for now. */
+ kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(g_papCpuInfo[idCpu], (char*)pszStatName);
+ if (pStat)
+ {
+ Assert(pStat->data_type == KSTAT_DATA_UINT64 || pStat->data_type == KSTAT_DATA_LONG);
+ switch (pStat->data_type)
+ {
+ case KSTAT_DATA_UINT64: u64 = pStat->value.ui64; break; /* current_clock_Hz */
+ case KSTAT_DATA_INT32: u64 = pStat->value.i32; break; /* clock_MHz */
+
+ /* just in case... */
+ case KSTAT_DATA_UINT32: u64 = pStat->value.ui32; break;
+ case KSTAT_DATA_INT64: u64 = pStat->value.i64; break;
+ default:
+ AssertMsgFailed(("%d\n", pStat->data_type));
+ break;
+ }
+ }
+ else
+ Log(("kstat_data_lookup(%s) -> %d\n", pszStatName, errno));
+ }
+ else
+ Log(("kstat_read() -> %d\n", errno));
+ RTCritSectLeave(&g_MpSolarisCritSect);
+ }
+ }
+ else
+ Log(("invalid idCpu: %d (g_capCpuInfo=%d)\n", (int)idCpu, (int)g_capCpuInfo));
+ }
+
+ return u64;
+}
+
+
+RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
+{
+ return rtMpSolarisGetFrequency(idCpu, "current_clock_Hz") / 1000000;
+}
+
+
+RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
+{
+ return rtMpSolarisGetFrequency(idCpu, "clock_MHz");
+}
+
+
+#if defined(RT_ARCH_SPARC) || defined(RT_ARCH_SPARC64)
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ /** @todo implement RTMpCpuId on solaris/r3! */
+ return NIL_RTCPUID;
+}
+#endif
+
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS ? (int)idCpu : -1;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu < RTCPUSET_MAX_CPUS ? iCpu : NIL_RTCPUID;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return RTMpGetCount() - 1;
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ return idCpu != NIL_RTCPUID
+ && idCpu < (RTCPUID)RTMpGetCount();
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ int iStatus = p_online(idCpu, P_STATUS);
+ return iStatus == P_ONLINE
+ || iStatus == P_NOINTR;
+}
+
+
+RTDECL(bool) RTMpIsCpuPresent(RTCPUID idCpu)
+{
+ int iStatus = p_online(idCpu, P_STATUS);
+ return iStatus != -1;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ /*
+ * Solaris has sysconf.
+ */
+ int cCpus = sysconf(_SC_NPROCESSORS_MAX);
+ if (cCpus < 0)
+ cCpus = sysconf(_SC_NPROCESSORS_CONF);
+ return cCpus;
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCpuSetEmpty(pSet);
+ int idCpu = RTMpGetCount();
+ while (idCpu-- > 0)
+ RTCpuSetAdd(pSet, idCpu);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ /*
+ * Solaris has sysconf.
+ */
+ return sysconf(_SC_NPROCESSORS_ONLN);
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ RTCpuSetEmpty(pSet);
+ RTCPUID cCpus = RTMpGetCount();
+ for (RTCPUID idCpu = 0; idCpu < cCpus; idCpu++)
+ if (RTMpIsCpuOnline(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ return pSet;
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetPresentSet(PRTCPUSET pSet)
+{
+#ifdef RT_STRICT
+ RTCPUID cCpusPresent = 0;
+#endif
+ RTCpuSetEmpty(pSet);
+ RTCPUID cCpus = RTMpGetCount();
+ for (RTCPUID idCpu = 0; idCpu < cCpus; idCpu++)
+ if (RTMpIsCpuPresent(idCpu))
+ {
+ RTCpuSetAdd(pSet, idCpu);
+#ifdef RT_STRICT
+ cCpusPresent++;
+#endif
+ }
+ Assert(cCpusPresent == RTMpGetPresentCount());
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetPresentCount(void)
+{
+ /*
+ * Solaris has sysconf.
+ */
+ return sysconf(_SC_NPROCESSORS_CONF);
+}
+
+
+RTDECL(RTCPUID) RTMpGetPresentCoreCount(void)
+{
+ return RTMpGetCoreCount();
+}
+
+
+RTDECL(RTCPUID) RTMpGetCoreCount(void)
+{
+ int rc = RTOnceEx(&g_MpSolarisOnce, rtMpSolarisOnce, rtMpSolarisCleanUp, NULL /* pvUser */);
+ if (RT_SUCCESS(rc))
+ return g_cCores;
+
+ return 0;
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
+{
+ RTCPUID uOnlineCores = 0;
+ int rc = RTOnceEx(&g_MpSolarisOnce, rtMpSolarisOnce, rtMpSolarisCleanUp, NULL /* pvUser */);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectEnter(&g_MpSolarisCritSect);
+ AssertRC(rc);
+
+ /*
+ * For each core in the system, count how many are currently online.
+ */
+ for (RTCPUID j = 0; j < g_cCores; j++)
+ {
+ uint64_t u64CoreId = g_pu64CoreIds[j];
+ for (RTCPUID idCpu = 0; idCpu < g_capCpuInfo; idCpu++)
+ {
+ rc = kstat_read(g_pKsCtl, g_papCpuInfo[idCpu], 0);
+ AssertReturn(rc != -1, 0 /* rc */);
+ uint64_t u64ThreadCoreId = rtMpSolarisGetCoreId(idCpu);
+ if (u64ThreadCoreId == u64CoreId)
+ {
+ kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(g_papCpuInfo[idCpu], (char *)"state");
+ Assert(pStat->data_type == KSTAT_DATA_CHAR);
+ if( !RTStrNCmp(pStat->value.c, PS_ONLINE, sizeof(PS_ONLINE) - 1)
+ || !RTStrNCmp(pStat->value.c, PS_NOINTR, sizeof(PS_NOINTR) - 1))
+ {
+ uOnlineCores++;
+ break; /* Move to the next core. We have at least 1 hyperthread online in the current core. */
+ }
+ }
+ }
+ }
+
+ RTCritSectLeave(&g_MpSolarisCritSect);
+ }
+
+ return uOnlineCores;
+}
+
diff --git a/src/VBox/Runtime/r3/solaris/rtProcInitExePath-solaris.cpp b/src/VBox/Runtime/r3/solaris/rtProcInitExePath-solaris.cpp
new file mode 100644
index 00000000..0cf236b0
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/rtProcInitExePath-solaris.cpp
@@ -0,0 +1,81 @@
+/* $Id: rtProcInitExePath-solaris.cpp $ */
+/** @file
+ * IPRT - rtProcInitName, Solaris.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <unistd.h>
+#include <errno.h>
+
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include "internal/process.h"
+#include "internal/path.h"
+
+
+DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath)
+{
+ /*
+ * Read the /proc/<pid>/path/a.out link, convert to native and return it.
+ */
+ char szProcFile[80];
+ RTStrPrintf(szProcFile, sizeof(szProcFile), "/proc/%ld/path/a.out", (long)getpid());
+ int cchLink = readlink(szProcFile, pszPath, cchPath - 1);
+ if (cchLink > 0 && (size_t)cchLink <= cchPath - 1)
+ {
+ pszPath[cchLink] = '\0';
+
+ char const *pszTmp;
+ int rc = rtPathFromNative(&pszTmp, pszPath, NULL);
+ AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, cchLink, pszPath), rc);
+ if (pszTmp != pszPath)
+ {
+ rc = RTStrCopy(pszPath, cchPath, pszTmp);
+ rtPathFreeIprt(pszTmp, pszPath);
+ }
+ return rc;
+ }
+
+ int err = errno;
+ int rc = RTErrConvertFromErrno(err);
+ AssertMsgFailed(("rc=%Rrc err=%d cchLink=%d\n", rc, err, cchLink));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/solaris/systemmem-solaris.cpp b/src/VBox/Runtime/r3/solaris/systemmem-solaris.cpp
new file mode 100644
index 00000000..b083c4f0
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/systemmem-solaris.cpp
@@ -0,0 +1,182 @@
+/* $Id: systemmem-solaris.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryTotalRam, Solaris ring-3.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/once.h>
+#include <iprt/param.h>
+
+#include <errno.h>
+#include <kstat.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Initialize globals once. */
+static RTONCE g_rtSysMemSolInitOnce = RTONCE_INITIALIZER;
+/** Critical section serializing access to g_pKStatCtl and the other handles. */
+static RTCRITSECT g_rtSysMemSolCritSect;
+/** The kstate control handle. */
+static kstat_ctl_t *g_pKStatCtl = NULL;
+/** The unix.system_pages handle. */
+static kstat_t *g_pUnixSysPages = NULL;
+/** The zfs.archstats handle. */
+static kstat_t *g_pZfsArcStats = NULL;
+
+
+/** @callback_method_impl{FNRTONCE} */
+static DECLCALLBACK(int) rtSysMemSolInit(void *pvUser)
+{
+ int rc = RTCritSectInit(&g_rtSysMemSolCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ g_pKStatCtl = kstat_open();
+ if (g_pKStatCtl)
+ {
+ g_pUnixSysPages = kstat_lookup(g_pKStatCtl, (char *)"unix", 0, (char *)"system_pages");
+ if (g_pUnixSysPages)
+ {
+ g_pZfsArcStats = kstat_lookup(g_pKStatCtl, (char *)"zfs", 0, (char *)"arcstats"); /* allow NULL */
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromErrno(errno);
+ kstat_close(g_pKStatCtl);
+ g_pKStatCtl = NULL;
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ return rc;
+}
+
+
+/** @callback_method_impl{FNRTONCECLEANUP} */
+static DECLCALLBACK(void) rtSysMemSolCleanUp(void *pvUser, bool fLazyCleanUpOk)
+{
+ RTCritSectDelete(&g_rtSysMemSolCritSect);
+
+ kstat_close(g_pKStatCtl);
+ g_pKStatCtl = NULL;
+ g_pUnixSysPages = NULL;
+ g_pZfsArcStats = NULL;
+
+ NOREF(pvUser); NOREF(fLazyCleanUpOk);
+}
+
+
+
+RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ int rc = RTOnceEx(&g_rtSysMemSolInitOnce, rtSysMemSolInit, rtSysMemSolCleanUp, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectEnter(&g_rtSysMemSolCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (kstat_read(g_pKStatCtl, g_pUnixSysPages, NULL) != -1)
+ {
+ kstat_named_t *pData = (kstat_named_t *)kstat_data_lookup(g_pUnixSysPages, (char *)"physmem");
+ if (pData)
+ *pcb = (uint64_t)pData->value.ul * PAGE_SIZE;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ RTCritSectLeave(&g_rtSysMemSolCritSect);
+ }
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ int rc = RTOnceEx(&g_rtSysMemSolInitOnce, rtSysMemSolInit, rtSysMemSolCleanUp, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectEnter(&g_rtSysMemSolCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (kstat_read(g_pKStatCtl, g_pUnixSysPages, NULL) != -1)
+ {
+ kstat_named_t *pData = (kstat_named_t *)kstat_data_lookup(g_pUnixSysPages, (char *)"freemem");
+ if (pData)
+ {
+ *pcb = (uint64_t)pData->value.ul * PAGE_SIZE;
+
+ /*
+ * Adjust for ZFS greedyness if possible.
+ * (c_min is the target minimum size of the cache, it is not
+ * an absolute minimum.)
+ */
+ if ( g_pZfsArcStats
+ && kstat_read(g_pKStatCtl, g_pZfsArcStats, NULL) != -1)
+ {
+ kstat_named_t *pCurSize = (kstat_named_t *)kstat_data_lookup(g_pZfsArcStats, (char *)"size");
+ kstat_named_t *pMinSize = (kstat_named_t *)kstat_data_lookup(g_pZfsArcStats, (char *)"c_min");
+ if ( pCurSize
+ && pMinSize
+ && pCurSize->value.ul > pMinSize->value.ul)
+ {
+ *pcb += pCurSize->value.ul - pMinSize->value.ul;
+ }
+ }
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+
+ RTCritSectLeave(&g_rtSysMemSolCritSect);
+ }
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/solaris/thread-affinity-solaris.cpp b/src/VBox/Runtime/r3/solaris/thread-affinity-solaris.cpp
new file mode 100644
index 00000000..0e037aad
--- /dev/null
+++ b/src/VBox/Runtime/r3/solaris/thread-affinity-solaris.cpp
@@ -0,0 +1,104 @@
+/* $Id: thread-affinity-solaris.cpp $ */
+/** @file
+ * IPRT - Thread Affinity, Solaris ring-3 implementation.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+#include <iprt/mp.h>
+
+#include <sys/types.h>
+#include <sys/processor.h>
+#include <sys/procset.h>
+#include <unistd.h>
+#include <errno.h>
+
+
+/* Note! The current implementation can only bind to a single CPU. */
+
+
+RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet)
+{
+ int rc;
+ if (pCpuSet == NULL)
+ rc = processor_bind(P_LWPID, P_MYID, PBIND_NONE, NULL);
+ else
+ {
+ RTCPUSET PresentSet;
+ int cCpusInSet = RTCpuSetCount(pCpuSet);
+ if (cCpusInSet == 1)
+ {
+ unsigned iCpu = 0;
+ while ( iCpu < RTCPUSET_MAX_CPUS
+ && !RTCpuSetIsMemberByIndex(pCpuSet, iCpu))
+ iCpu++;
+ rc = processor_bind(P_LWPID, P_MYID, iCpu, NULL);
+ }
+ else if ( cCpusInSet == RTCPUSET_MAX_CPUS
+ || RTCpuSetIsEqual(pCpuSet, RTMpGetPresentSet(&PresentSet)))
+ rc = processor_bind(P_LWPID, P_MYID, PBIND_NONE, NULL);
+ else
+ return VERR_NOT_SUPPORTED;
+ }
+ if (!rc)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+}
+
+
+RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet)
+{
+ processorid_t iOldCpu;
+ int rc = processor_bind(P_LWPID, P_MYID, PBIND_QUERY, &iOldCpu);
+ if (rc)
+ return RTErrConvertFromErrno(errno);
+ if (iOldCpu == PBIND_NONE)
+ RTMpGetPresentSet(pCpuSet);
+ else
+ {
+ RTCpuSetEmpty(pCpuSet);
+ if (RTCpuSetAdd(pCpuSet, iOldCpu) != 0)
+ return VERR_INTERNAL_ERROR_5;
+ }
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/stream.cpp b/src/VBox/Runtime/r3/stream.cpp
new file mode 100644
index 00000000..ead09e53
--- /dev/null
+++ b/src/VBox/Runtime/r3/stream.cpp
@@ -0,0 +1,2698 @@
+/* $Id: stream.cpp $ */
+/** @file
+ * IPRT - I/O Stream.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def RTSTREAM_STANDALONE
+ * Standalone streams w/o depending on stdio.h, using our RTFile API for
+ * file/whatever access. */
+#if (defined(IPRT_NO_CRT) && defined(RT_OS_WINDOWS)) || defined(DOXYGEN_RUNNING)
+# define RTSTREAM_STANDALONE
+#endif
+
+#if defined(RT_OS_LINUX) /* PORTME: check for the _unlocked functions in stdio.h */
+# ifndef RTSTREAM_STANDALONE
+# define HAVE_FWRITE_UNLOCKED
+# endif
+#endif
+
+/** @def RTSTREAM_WITH_TEXT_MODE
+ * Indicates whether we need to support the 'text' mode files and convert
+ * CRLF to LF while reading and writing. */
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
+# define RTSTREAM_WITH_TEXT_MODE
+#endif
+
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/stream.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#ifndef HAVE_FWRITE_UNLOCKED
+# include <iprt/critsect.h>
+#endif
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+# include <iprt/file.h>
+#ifdef RTSTREAM_STANDALONE
+# include <iprt/list.h>
+#endif
+#include <iprt/mem.h>
+#ifdef RTSTREAM_STANDALONE
+# include <iprt/once.h>
+#endif
+#include <iprt/param.h>
+#include <iprt/string.h>
+
+#include "internal/alignmentchecks.h"
+#include "internal/magics.h"
+#if defined(IPRT_NO_CRT) || defined(IN_RT_STATIC)
+# include "internal/initterm.h"
+#endif
+
+#ifdef RTSTREAM_STANDALONE
+# ifdef _MSC_VER
+# define IPRT_COMPILER_VCC_WITH_C_INIT_TERM_SECTIONS
+# include "internal/compiler-vcc.h"
+# endif
+#else
+# include <stdio.h>
+# include <errno.h>
+# if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+# include <io.h>
+# include <fcntl.h>
+# endif
+#endif
+#ifdef RT_OS_WINDOWS
+# include <iprt/utf16.h>
+# include <iprt/win/windows.h>
+#elif !defined(RTSTREAM_STANDALONE)
+# include <termios.h>
+# include <unistd.h>
+# include <sys/ioctl.h>
+#endif
+
+#if defined(RT_OS_OS2) && !defined(RTSTREAM_STANDALONE)
+# define _O_TEXT O_TEXT
+# define _O_BINARY O_BINARY
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#ifdef RTSTREAM_STANDALONE
+/** The buffer direction. */
+typedef enum RTSTREAMBUFDIR
+{
+ RTSTREAMBUFDIR_NONE = 0,
+ RTSTREAMBUFDIR_READ,
+ RTSTREAMBUFDIR_WRITE
+} RTSTREAMBUFDIR;
+
+/** The buffer style. */
+typedef enum RTSTREAMBUFSTYLE
+{
+ RTSTREAMBUFSTYLE_UNBUFFERED = 0,
+ RTSTREAMBUFSTYLE_LINE,
+ RTSTREAMBUFSTYLE_FULL
+} RTSTREAMBUFSTYLE;
+
+#endif
+
+/**
+ * File stream.
+ */
+typedef struct RTSTREAM
+{
+ /** Magic value used to validate the stream. (RTSTREAM_MAGIC) */
+ uint32_t u32Magic;
+ /** File stream error. */
+ int32_t volatile i32Error;
+#ifndef RTSTREAM_STANDALONE
+ /** Pointer to the LIBC file stream. */
+ FILE *pFile;
+#else
+ /** Indicates which standard handle this is supposed to be.
+ * Set to RTHANDLESTD_INVALID if not one of the tree standard streams. */
+ RTHANDLESTD enmStdHandle;
+ /** The IPRT handle backing this stream.
+ * This is initialized lazily using enmStdHandle for the three standard
+ * streams. */
+ RTFILE hFile;
+ /** Buffer. */
+ char *pchBuf;
+ /** Buffer allocation size. */
+ size_t cbBufAlloc;
+ /** Offset of the first valid byte in the buffer. */
+ size_t offBufFirst;
+ /** Offset of the end of valid bytes in the buffer (exclusive). */
+ size_t offBufEnd;
+ /** The stream buffer direction. */
+ RTSTREAMBUFDIR enmBufDir;
+ /** The buffering style (unbuffered, line, full).
+ * @todo replace by RTSTRMBUFMODE. */
+ RTSTREAMBUFSTYLE enmBufStyle;
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ /** Bitmap running parallel to each char pchBuf, indicating where a '\\r'
+ * character have been removed during buffer filling. This is used to implement
+ * RTStrmTell in non-binary mode. */
+ uint32_t *pbmBuf;
+ /** Indicates that we've got a CR ('\\r') beyond the end of official buffer
+ * and need to check if there is a LF following it. This member is ignored
+ * in binary mode. */
+ bool fPendingCr;
+# endif
+#endif
+ /** Stream is using the current process code set. */
+ bool fCurrentCodeSet;
+ /** Whether the stream was opened in binary mode. */
+ bool fBinary;
+ /** Whether to recheck the stream mode before writing. */
+ bool fRecheckMode;
+#if !defined(HAVE_FWRITE_UNLOCKED) || defined(RTSTREAM_STANDALONE)
+ /** Critical section for serializing access to the stream. */
+ PRTCRITSECT pCritSect;
+#endif
+#ifdef RTSTREAM_STANDALONE
+ /** Entry in g_StreamList (for automatic flushing and closing at
+ * exit/unload). */
+ RTLISTNODE ListEntry;
+#endif
+} RTSTREAM;
+
+
+/**
+ * State for wrapped output (RTStrmWrappedPrintf, RTStrmWrappedPrintfV).
+ */
+typedef struct RTSTRMWRAPPEDSTATE
+{
+ PRTSTREAM pStream; /**< The output stream. */
+ uint32_t cchWidth; /**< The line width. */
+ uint32_t cchLine; /**< The current line length (valid chars in szLine). */
+ uint32_t cLines; /**< Number of lines written. */
+ uint32_t cchIndent; /**< The indent (determined from the first line). */
+ int rcStatus; /**< The output status. */
+ uint8_t cchHangingIndent; /**< Hanging indent (from fFlags). */
+ char szLine[0x1000+1]; /**< We must buffer output so we can do proper word splitting. */
+} RTSTRMWRAPPEDSTATE;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The standard input stream. */
+static RTSTREAM g_StdIn =
+{
+ /* .u32Magic = */ RTSTREAM_MAGIC,
+ /* .i32Error = */ 0,
+#ifndef RTSTREAM_STANDALONE
+ /* .pFile = */ stdin,
+#else
+ /* .enmStdHandle = */ RTHANDLESTD_INPUT,
+ /* .hFile = */ NIL_RTFILE,
+ /* .pchBuf = */ NULL,
+ /* .cbBufAlloc = */ 0,
+ /* .offBufFirst = */ 0,
+ /* .offBufEnd = */ 0,
+ /* .enmBufDir = */ RTSTREAMBUFDIR_NONE,
+ /* .enmBufStyle = */ RTSTREAMBUFSTYLE_UNBUFFERED,
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ /* .pbmBuf = */ NULL,
+ /* .fPendingCr = */ false,
+# endif
+#endif
+ /* .fCurrentCodeSet = */ true,
+ /* .fBinary = */ false,
+ /* .fRecheckMode = */ true,
+#ifndef HAVE_FWRITE_UNLOCKED
+ /* .pCritSect = */ NULL,
+#endif
+#ifdef RTSTREAM_STANDALONE
+ /* .ListEntry = */ { NULL, NULL },
+#endif
+};
+
+/** The standard error stream. */
+static RTSTREAM g_StdErr =
+{
+ /* .u32Magic = */ RTSTREAM_MAGIC,
+ /* .i32Error = */ 0,
+#ifndef RTSTREAM_STANDALONE
+ /* .pFile = */ stderr,
+#else
+ /* .enmStdHandle = */ RTHANDLESTD_ERROR,
+ /* .hFile = */ NIL_RTFILE,
+ /* .pchBuf = */ NULL,
+ /* .cbBufAlloc = */ 0,
+ /* .offBufFirst = */ 0,
+ /* .offBufEnd = */ 0,
+ /* .enmBufDir = */ RTSTREAMBUFDIR_NONE,
+ /* .enmBufStyle = */ RTSTREAMBUFSTYLE_UNBUFFERED,
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ /* .pbmBuf = */ NULL,
+ /* .fPendingCr = */ false,
+# endif
+#endif
+ /* .fCurrentCodeSet = */ true,
+ /* .fBinary = */ false,
+ /* .fRecheckMode = */ true,
+#ifndef HAVE_FWRITE_UNLOCKED
+ /* .pCritSect = */ NULL,
+#endif
+#ifdef RTSTREAM_STANDALONE
+ /* .ListEntry = */ { NULL, NULL },
+#endif
+};
+
+/** The standard output stream. */
+static RTSTREAM g_StdOut =
+{
+ /* .u32Magic = */ RTSTREAM_MAGIC,
+ /* .i32Error = */ 0,
+#ifndef RTSTREAM_STANDALONE
+ /* .pFile = */ stdout,
+#else
+ /* .enmStdHandle = */ RTHANDLESTD_OUTPUT,
+ /* .hFile = */ NIL_RTFILE,
+ /* .pchBuf = */ NULL,
+ /* .cbBufAlloc = */ 0,
+ /* .offBufFirst = */ 0,
+ /* .offBufEnd = */ 0,
+ /* .enmBufDir = */ RTSTREAMBUFDIR_NONE,
+ /* .enmBufStyle = */ RTSTREAMBUFSTYLE_LINE,
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ /* .pbmBuf = */ NULL,
+ /* .fPendingCr = */ false,
+# endif
+#endif
+ /* .fCurrentCodeSet = */ true,
+ /* .fBinary = */ false,
+ /* .fRecheckMode = */ true,
+#ifndef HAVE_FWRITE_UNLOCKED
+ /* .pCritSect = */ NULL,
+#endif
+#ifdef RTSTREAM_STANDALONE
+ /* .ListEntry = */ { NULL, NULL },
+#endif
+};
+
+/** Pointer to the standard input stream. */
+RTDATADECL(PRTSTREAM) g_pStdIn = &g_StdIn;
+
+/** Pointer to the standard output stream. */
+RTDATADECL(PRTSTREAM) g_pStdErr = &g_StdErr;
+
+/** Pointer to the standard output stream. */
+RTDATADECL(PRTSTREAM) g_pStdOut = &g_StdOut;
+
+#ifdef RTSTREAM_STANDALONE
+/** Run-once initializer for the stream list (g_StreamList + g_StreamListCritSect). */
+static RTONCE g_StreamListOnce = RTONCE_INITIALIZER;
+/** List of user created streams (excludes the standard streams). */
+static RTLISTANCHOR g_StreamList;
+/** Critical section protecting the stream list. */
+static RTCRITSECT g_StreamListCritSect;
+
+
+/** @callback_method_impl{FNRTONCE} */
+static DECLCALLBACK(int32_t) rtStrmListInitOnce(void *pvUser)
+{
+ RT_NOREF(pvUser);
+ RTListInit(&g_StreamList);
+ return RTCritSectInit(&g_StreamListCritSect);
+}
+
+#endif
+
+
+#ifndef HAVE_FWRITE_UNLOCKED
+/**
+ * Allocates and acquires the lock for the stream.
+ *
+ * @returns IPRT status code.
+ * @param pStream The stream (valid).
+ */
+static int rtStrmAllocLock(PRTSTREAM pStream)
+{
+ Assert(pStream->pCritSect == NULL);
+
+ PRTCRITSECT pCritSect = (PRTCRITSECT)RTMemAlloc(sizeof(*pCritSect));
+ if (!pCritSect)
+ return VERR_NO_MEMORY;
+
+ /* The native stream lock are normally not recursive. */
+ uint32_t fFlags = RTCRITSECT_FLAGS_NO_NESTING;
+# if defined(IPRT_NO_CRT) || defined(IN_RT_STATIC)
+ /* IPRT is often used deliberatly without initialization in no-CRT
+ binaries (for instance VBoxAddInstallNt3x.exe), so in order to avoid
+ asserting in the lock validator we add the bootstrap hack that disable
+ lock validation for the section.
+ Update: Applying this to all builds involving static linking, as it's
+ now going to be used for tests running at compile-time too. */
+ if (!rtInitIsInitialized())
+ fFlags |= RTCRITSECT_FLAGS_BOOTSTRAP_HACK;
+# endif
+ int rc = RTCritSectInitEx(pCritSect, fFlags, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "RTSemSpinMutex");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectEnter(pCritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (RT_LIKELY(ASMAtomicCmpXchgPtr(&pStream->pCritSect, pCritSect, NULL)))
+ return VINF_SUCCESS;
+
+ RTCritSectLeave(pCritSect);
+ }
+ RTCritSectDelete(pCritSect);
+ }
+ RTMemFree(pCritSect);
+
+ /* Handle the lost race case... */
+ pCritSect = ASMAtomicReadPtrT(&pStream->pCritSect, PRTCRITSECT);
+ if (pCritSect)
+ return RTCritSectEnter(pCritSect);
+
+ return rc;
+}
+#endif /* !HAVE_FWRITE_UNLOCKED */
+
+
+/**
+ * Locks the stream. May have to allocate the lock as well.
+ *
+ * @param pStream The stream (valid).
+ */
+DECLINLINE(void) rtStrmLock(PRTSTREAM pStream)
+{
+#ifdef HAVE_FWRITE_UNLOCKED
+ flockfile(pStream->pFile);
+#else
+ if (RT_LIKELY(pStream->pCritSect))
+ RTCritSectEnter(pStream->pCritSect);
+ else
+ rtStrmAllocLock(pStream);
+#endif
+}
+
+
+/**
+ * Unlocks the stream.
+ *
+ * @param pStream The stream (valid).
+ */
+DECLINLINE(void) rtStrmUnlock(PRTSTREAM pStream)
+{
+#ifdef HAVE_FWRITE_UNLOCKED
+ funlockfile(pStream->pFile);
+#else
+ if (RT_LIKELY(pStream->pCritSect))
+ RTCritSectLeave(pStream->pCritSect);
+#endif
+}
+
+
+/**
+ * Opens a file stream.
+ *
+ * @returns iprt status code.
+ * @param pszFilename Path to the file to open, hFile must be NIL_RTFILE.
+ * NULL if a hFile is to be used instead.
+ * @param hFile File handle to use when called from
+ * RTStrmOpenFileHandle. pszFilename must be NULL.
+ * @param pszMode See RTStrmOpen.
+ * @param ppStream Where to store the opened stream.
+ */
+static int rtStrmOpenComon(const char *pszFilename, RTFILE hFile, const char *pszMode, PRTSTREAM *ppStream)
+{
+ /*
+ * Validate input and look for things we care for in the pszMode string.
+ */
+ AssertReturn(pszMode && *pszMode, VERR_INVALID_FLAGS);
+
+ /*
+ * Process the mode string.
+ */
+ char chMode = '\0'; /* a|r|w */
+ bool fPlus = false; /* + */
+ bool fBinary = false; /* b | !t */
+ bool fExclusive = false; /* x */
+ bool fNoInherit = false; /* e (linux, freebsd) | N (win) | E (our for reverse) */
+ const char *psz = pszMode;
+ char ch;
+ while ((ch = *psz++) != '\0')
+ {
+ switch (ch)
+ {
+ case 'a':
+ case 'r':
+ case 'w':
+ chMode = ch;
+ break;
+ case '+':
+ fPlus = true;
+ break;
+ case 'b':
+ fBinary = true;
+ break;
+ case 't':
+ fBinary = false;
+ break;
+ case 'x':
+ fExclusive = true;
+ break;
+ case 'e':
+ case 'N':
+ fNoInherit = true;
+ break;
+ case 'E':
+ fNoInherit = false;
+ break;
+ default:
+ AssertMsgFailedReturn(("Invalid ch='%c' in pszMode='%s', '<a|r|w>[+][b|t][x][e|N|E]'\n", ch, pszMode),
+ VERR_INVALID_FLAGS);
+ }
+ }
+
+ /*
+ * Translate into to RTFILE_O_* flags:
+ */
+ uint64_t fOpen;
+ switch (chMode)
+ {
+ case 'a': fOpen = RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_APPEND; break;
+ case 'w': fOpen = !fExclusive
+ ? RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE
+ : RTFILE_O_CREATE | RTFILE_O_WRITE; break;
+ case 'r': fOpen = RTFILE_O_OPEN | RTFILE_O_READ; break;
+ default: AssertMsgFailedReturn(("No main mode (a|r|w) specified in '%s'!\n", pszMode), VERR_INVALID_FLAGS);
+ }
+ AssertMsgReturn(!fExclusive || chMode == 'w', ("the 'x' flag is only allowed with 'w'! (%s)\n", pszMode),
+ VERR_INVALID_FLAGS);
+ if (fExclusive)
+ fOpen |= RTFILE_O_READ | RTFILE_O_WRITE;
+ if (fPlus)
+ fOpen |= RTFILE_O_READ | RTFILE_O_WRITE;
+ if (!fNoInherit)
+ fOpen |= RTFILE_O_INHERIT;
+ fOpen |= RTFILE_O_DENY_NONE;
+ fOpen |= 0666 << RTFILE_O_CREATE_MODE_SHIFT;
+
+#ifndef RTSTREAM_STANDALONE
+ /*
+ * Normalize mode for fdopen.
+ */
+ char szNormalizedMode[8];
+ szNormalizedMode[0] = chMode;
+ size_t off = 1;
+ if (fPlus)
+ szNormalizedMode[off++] = '+';
+ if (fBinary)
+ szNormalizedMode[off++] = 'b';
+ szNormalizedMode[off] = '\0';
+#endif
+
+#ifdef RTSTREAM_STANDALONE
+ /*
+ * Make the the stream list is initialized before we allocate anything.
+ */
+ int rc2 = RTOnce(&g_StreamListOnce, rtStrmListInitOnce, NULL);
+ AssertRCReturn(rc2, rc2);
+#endif
+
+ /*
+ * Allocate the stream handle and try open it.
+ */
+ int rc = VERR_NO_MEMORY;
+ PRTSTREAM pStream = (PRTSTREAM)RTMemAllocZ(sizeof(*pStream));
+ if (pStream)
+ {
+ pStream->u32Magic = RTSTREAM_MAGIC;
+#ifdef RTSTREAM_STANDALONE
+ pStream->enmStdHandle = RTHANDLESTD_INVALID;
+ pStream->hFile = NIL_RTFILE;
+ pStream->pchBuf = NULL;
+ pStream->cbBufAlloc = 0;
+ pStream->offBufFirst = 0;
+ pStream->offBufEnd = 0;
+ pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
+ pStream->enmBufStyle = RTSTREAMBUFSTYLE_FULL;
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ pStream->pbmBuf = NULL;
+ pStream->fPendingCr = false,
+# endif
+#endif
+ pStream->i32Error = VINF_SUCCESS;
+ pStream->fCurrentCodeSet = false;
+ pStream->fBinary = fBinary;
+ pStream->fRecheckMode = false;
+#ifndef HAVE_FWRITE_UNLOCKED
+ pStream->pCritSect = NULL;
+#endif
+ RTFILEACTION enmActionTaken = RTFILEACTION_INVALID;
+ if (pszFilename)
+ rc = RTFileOpenEx(pszFilename, fOpen, &hFile, &enmActionTaken);
+ else
+ rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ {
+#ifndef RTSTREAM_STANDALONE
+# ifndef _MSC_VER
+ int fd = (int)RTFileToNative(hFile);
+# else
+ int fd = _open_osfhandle(RTFileToNative(hFile),
+ (fPlus ? _O_RDWR : chMode == 'r' ? _O_RDONLY : _O_WRONLY)
+ | (chMode == 'a' ? _O_APPEND : 0)
+ | (fBinary ? _O_BINARY : _O_TEXT)
+ | (fNoInherit ? _O_NOINHERIT : 0));
+# endif
+ if (fd >= 0)
+ {
+ pStream->pFile = fdopen(fd, szNormalizedMode);
+ if (pStream->pFile)
+#endif
+ {
+#ifdef RTSTREAM_STANDALONE
+ pStream->hFile = hFile;
+
+ /* We keep a list of these for cleanup purposes. */
+ RTCritSectEnter(&g_StreamListCritSect);
+ RTListAppend(&g_StreamList, &pStream->ListEntry);
+ RTCritSectLeave(&g_StreamListCritSect);
+#endif
+ *ppStream = pStream;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * This better not happen too often as in 'w' mode we might've
+ * truncated a file, and in 'w' and 'a' modes there is a chance
+ * that we'll race other access to the file when deleting it.
+ */
+#ifndef RTSTREAM_STANDALONE
+ rc = RTErrConvertFromErrno(errno);
+# ifdef _MSC_VER
+ close(fd);
+ hFile = NIL_RTFILE;
+ /** @todo we're in trouble here when called from RTStrmOpenFileHandle! */
+# endif
+ }
+ else
+ {
+# ifdef _MSC_VER
+ rc = RTErrConvertFromErrno(errno);
+# else
+ AssertFailedStmt(rc = VERR_INVALID_HANDLE);
+# endif
+ }
+ if (pszFilename)
+ {
+ RTFileClose(hFile);
+ if (enmActionTaken == RTFILEACTION_CREATED)
+ RTFileDelete(pszFilename);
+ }
+#endif
+ }
+ RTMemFree(pStream);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrmOpen(const char *pszFilename, const char *pszMode, PRTSTREAM *ppStream)
+{
+ *ppStream = NULL;
+ AssertReturn(pszFilename, VERR_INVALID_PARAMETER);
+ return rtStrmOpenComon(pszFilename, NIL_RTFILE, pszMode, ppStream);
+}
+
+
+RTR3DECL(int) RTStrmOpenFV(const char *pszMode, PRTSTREAM *ppStream, const char *pszFilenameFmt, va_list args)
+{
+ int rc;
+ char szFilename[RTPATH_MAX];
+ size_t cch = RTStrPrintfV(szFilename, sizeof(szFilename), pszFilenameFmt, args);
+ if (cch < sizeof(szFilename))
+ rc = RTStrmOpen(szFilename, pszMode, ppStream);
+ else
+ {
+ AssertMsgFailed(("The filename is too long cch=%d\n", cch));
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrmOpenF(const char *pszMode, PRTSTREAM *ppStream, const char *pszFilenameFmt, ...)
+{
+ va_list args;
+ va_start(args, pszFilenameFmt);
+ int rc = RTStrmOpenFV(pszMode, ppStream, pszFilenameFmt, args);
+ va_end(args);
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrmOpenFileHandle(RTFILE hFile, const char *pszMode, uint32_t fFlags, PRTSTREAM *ppStream)
+{
+ *ppStream = NULL;
+ AssertReturn(RTFileIsValid(hFile), VERR_INVALID_HANDLE);
+ AssertReturn(fFlags == 0, VERR_INVALID_FLAGS);
+ return rtStrmOpenComon(NULL, hFile, pszMode, ppStream);
+}
+
+
+RTR3DECL(int) RTStrmClose(PRTSTREAM pStream)
+{
+ /*
+ * Validate input.
+ */
+ if (!pStream)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pStream, VERR_INVALID_POINTER);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
+
+ /* We don't implement closing any of the standard handles at present. */
+ AssertReturn(pStream != &g_StdIn, VERR_NOT_SUPPORTED);
+ AssertReturn(pStream != &g_StdOut, VERR_NOT_SUPPORTED);
+ AssertReturn(pStream != &g_StdErr, VERR_NOT_SUPPORTED);
+
+ /*
+ * Invalidate the stream and destroy the critical section first.
+ */
+#ifdef RTSTREAM_STANDALONE
+ RTCritSectEnter(&g_StreamListCritSect);
+ RTListNodeRemove(&pStream->ListEntry);
+ RTCritSectLeave(&g_StreamListCritSect);
+#endif
+ pStream->u32Magic = 0xdeaddead;
+#ifndef HAVE_FWRITE_UNLOCKED
+ if (pStream->pCritSect)
+ {
+ RTCritSectEnter(pStream->pCritSect);
+ RTCritSectLeave(pStream->pCritSect);
+ RTCritSectDelete(pStream->pCritSect);
+ RTMemFree(pStream->pCritSect);
+ pStream->pCritSect = NULL;
+ }
+#endif
+
+ /*
+ * Flush and close the underlying file.
+ */
+#ifdef RTSTREAM_STANDALONE
+ int const rc1 = RTStrmFlush(pStream);
+ AssertRC(rc1);
+ int const rc2 = RTFileClose(pStream->hFile);
+ AssertRC(rc2);
+ int const rc = RT_SUCCESS(rc1) ? rc2 : rc1;
+#else
+ int const rc = !fclose(pStream->pFile) ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
+#endif
+
+ /*
+ * Destroy the stream.
+ */
+#ifdef RTSTREAM_STANDALONE
+ pStream->hFile = NIL_RTFILE;
+ RTMemFree(pStream->pchBuf);
+ pStream->pchBuf = NULL;
+ pStream->cbBufAlloc = 0;
+ pStream->offBufFirst = 0;
+ pStream->offBufEnd = 0;
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ RTMemFree(pStream->pbmBuf);
+ pStream->pbmBuf = NULL;
+# endif
+#else
+ pStream->pFile = NULL;
+#endif
+ RTMemFree(pStream);
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrmError(PRTSTREAM pStream)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_POINTER);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
+ return pStream->i32Error;
+}
+
+
+RTR3DECL(int) RTStrmClearError(PRTSTREAM pStream)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_POINTER);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
+
+#ifndef RTSTREAM_STANDALONE
+ clearerr(pStream->pFile);
+#endif
+ ASMAtomicWriteS32(&pStream->i32Error, VINF_SUCCESS);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTStrmSetMode(PRTSTREAM pStream, int fBinary, int fCurrentCodeSet)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn((unsigned)(fBinary + 1) <= 2, VERR_INVALID_PARAMETER);
+ AssertReturn((unsigned)(fCurrentCodeSet + 1) <= 2, VERR_INVALID_PARAMETER);
+
+ rtStrmLock(pStream);
+
+ if (fBinary != -1)
+ {
+ pStream->fBinary = RT_BOOL(fBinary);
+ pStream->fRecheckMode = true;
+ }
+
+ if (fCurrentCodeSet != -1)
+ pStream->fCurrentCodeSet = RT_BOOL(fCurrentCodeSet);
+
+ rtStrmUnlock(pStream);
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTStrmSetBufferingMode(PRTSTREAM pStream, RTSTRMBUFMODE enmMode)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(enmMode > RTSTRMBUFMODE_INVALID && enmMode < RTSTRMBUFMODE_END, VERR_INVALID_PARAMETER);
+
+#ifndef RTSTREAM_STANDALONE
+ int iCrtMode = enmMode == RTSTRMBUFMODE_FULL ? _IOFBF : enmMode == RTSTRMBUFMODE_LINE ? _IOLBF : _IONBF;
+ int rc = setvbuf(pStream->pFile, NULL, iCrtMode, 0);
+ if (rc >= 0)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+
+#else
+ rtStrmLock(pStream);
+ pStream->enmBufStyle = enmMode == RTSTRMBUFMODE_FULL ? RTSTREAMBUFSTYLE_FULL
+ : enmMode == RTSTRMBUFMODE_LINE ? RTSTREAMBUFSTYLE_LINE : RTSTREAMBUFSTYLE_UNBUFFERED;
+ rtStrmUnlock(pStream);
+ return VINF_SUCCESS;
+#endif
+}
+
+
+#ifdef RTSTREAM_STANDALONE
+
+/**
+ * Deals with NIL_RTFILE in rtStrmGetFile.
+ */
+DECL_NO_INLINE(static, RTFILE) rtStrmGetFileNil(PRTSTREAM pStream)
+{
+# ifdef RT_OS_WINDOWS
+ DWORD dwStdHandle;
+ switch (pStream->enmStdHandle)
+ {
+ case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break;
+ case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break;
+ case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break;
+ default: return NIL_RTFILE;
+ }
+ HANDLE hHandle = GetStdHandle(dwStdHandle);
+ if (hHandle != INVALID_HANDLE_VALUE && hHandle != NULL)
+ {
+ int rc = RTFileFromNative(&pStream->hFile, (uintptr_t)hHandle);
+ if (RT_SUCCESS(rc))
+ {
+ /* Switch to full buffering if not a console handle. */
+ DWORD dwMode;
+ if (!GetConsoleMode(hHandle, &dwMode))
+ pStream->enmBufStyle = RTSTREAMBUFSTYLE_FULL;
+
+ return pStream->hFile;
+ }
+ }
+
+# else
+ uintptr_t uNative;
+ switch (pStream->enmStdHandle)
+ {
+ case RTHANDLESTD_INPUT: uNative = RTFILE_NATIVE_STDIN; break;
+ case RTHANDLESTD_OUTPUT: uNative = RTFILE_NATIVE_STDOUT; break;
+ case RTHANDLESTD_ERROR: uNative = RTFILE_NATIVE_STDERR; break;
+ default: return NIL_RTFILE;
+ }
+ int rc = RTFileFromNative(&pStream->hFile, uNative);
+ if (RT_SUCCESS(rc))
+ {
+ /* Switch to full buffering if not a console handle. */
+ if (!isatty((int)uNative))
+ pStream->enmBufStyle = RTSTREAMBUFDIR_FULL;
+
+ return pStream->hFile;
+ }
+
+# endif
+ return NIL_RTFILE;
+}
+
+
+/**
+ * For lazily resolving handles for the standard streams.
+ */
+DECLINLINE(RTFILE) rtStrmGetFile(PRTSTREAM pStream)
+{
+ RTFILE hFile = pStream->hFile;
+ if (hFile != NIL_RTFILE)
+ return hFile;
+ return rtStrmGetFileNil(pStream);
+}
+
+
+RTR3DECL(int) RTStrmQueryFileHandle(PRTSTREAM pStream, PRTFILE phFile)
+{
+ AssertPtrReturn(phFile, VERR_INVALID_POINTER);
+ *phFile = NIL_RTFILE;
+ AssertPtrReturn(pStream, VERR_INVALID_POINTER);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_MAGIC);
+
+ rtStrmLock(pStream);
+ RTFILE hFile = rtStrmGetFile(pStream);
+ rtStrmUnlock(pStream);
+ if (hFile != NIL_RTFILE)
+ {
+ *phFile = hFile;
+ return VINF_SUCCESS;
+ }
+ return VERR_NOT_AVAILABLE;
+}
+
+#endif /* RTSTREAM_STANDALONE */
+
+
+/**
+ * Wrapper around isatty, assumes caller takes care of stream locking/whatever
+ * is needed.
+ */
+DECLINLINE(bool) rtStrmIsTerminal(PRTSTREAM pStream)
+{
+#ifdef RTSTREAM_STANDALONE
+ RTFILE hFile = rtStrmGetFile(pStream);
+ if (hFile != NIL_RTFILE)
+ {
+ HANDLE hNative = (HANDLE)RTFileToNative(hFile);
+ DWORD dwType = GetFileType(hNative);
+ if (dwType == FILE_TYPE_CHAR)
+ {
+ DWORD dwMode;
+ if (GetConsoleMode(hNative, &dwMode))
+ return true;
+ }
+ }
+ return false;
+
+#else
+ if (pStream->pFile)
+ {
+ int fh = fileno(pStream->pFile);
+ if (isatty(fh) != 0)
+ {
+# ifdef RT_OS_WINDOWS
+ DWORD dwMode;
+ HANDLE hCon = (HANDLE)_get_osfhandle(fh);
+ if (GetConsoleMode(hCon, &dwMode))
+ return true;
+# else
+ return true;
+# endif
+ }
+ }
+ return false;
+#endif
+}
+
+
+static int rtStrmInputGetEchoCharsNative(uintptr_t hNative, bool *pfEchoChars)
+{
+#ifdef RT_OS_WINDOWS
+ DWORD dwMode;
+ if (GetConsoleMode((HANDLE)hNative, &dwMode))
+ *pfEchoChars = RT_BOOL(dwMode & ENABLE_ECHO_INPUT);
+ else
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_INVALID_HANDLE)
+ return GetFileType((HANDLE)hNative) != FILE_TYPE_UNKNOWN ? VERR_INVALID_FUNCTION : VERR_INVALID_HANDLE;
+ return RTErrConvertFromWin32(dwErr);
+ }
+#else
+ struct termios Termios;
+ int rcPosix = tcgetattr((int)hNative, &Termios);
+ if (!rcPosix)
+ *pfEchoChars = RT_BOOL(Termios.c_lflag & ECHO);
+ else
+ return errno == ENOTTY ? VERR_INVALID_FUNCTION : RTErrConvertFromErrno(errno);
+#endif
+ return VINF_SUCCESS;
+}
+
+
+
+RTR3DECL(int) RTStrmInputGetEchoChars(PRTSTREAM pStream, bool *pfEchoChars)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pfEchoChars, VERR_INVALID_POINTER);
+
+#ifdef RTSTREAM_STANDALONE
+ return rtStrmInputGetEchoCharsNative(RTFileToNative(pStream->hFile), pfEchoChars);
+#else
+ int rc;
+ int fh = fileno(pStream->pFile);
+ if (isatty(fh))
+ {
+# ifdef RT_OS_WINDOWS
+ rc = rtStrmInputGetEchoCharsNative(_get_osfhandle(fh), pfEchoChars);
+# else
+ rc = rtStrmInputGetEchoCharsNative(fh, pfEchoChars);
+# endif
+ }
+ else
+ rc = VERR_INVALID_FUNCTION;
+ return rc;
+#endif
+}
+
+
+static int rtStrmInputSetEchoCharsNative(uintptr_t hNative, bool fEchoChars)
+{
+ int rc;
+#ifdef RT_OS_WINDOWS
+ DWORD dwMode;
+ if (GetConsoleMode((HANDLE)hNative, &dwMode))
+ {
+ if (fEchoChars)
+ dwMode |= ENABLE_ECHO_INPUT;
+ else
+ dwMode &= ~ENABLE_ECHO_INPUT;
+ if (SetConsoleMode((HANDLE)hNative, dwMode))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_INVALID_HANDLE)
+ return GetFileType((HANDLE)hNative) != FILE_TYPE_UNKNOWN ? VERR_INVALID_FUNCTION : VERR_INVALID_HANDLE;
+ return RTErrConvertFromWin32(dwErr);
+ }
+#else
+ struct termios Termios;
+ int rcPosix = tcgetattr((int)hNative, &Termios);
+ if (!rcPosix)
+ {
+ if (fEchoChars)
+ Termios.c_lflag |= ECHO;
+ else
+ Termios.c_lflag &= ~ECHO;
+
+ rcPosix = tcsetattr((int)hNative, TCSAFLUSH, &Termios);
+ if (rcPosix == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ }
+ else
+ rc = errno == ENOTTY ? VERR_INVALID_FUNCTION : RTErrConvertFromErrno(errno);
+#endif
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrmInputSetEchoChars(PRTSTREAM pStream, bool fEchoChars)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSTREAM_STANDALONE
+ return rtStrmInputSetEchoCharsNative(RTFileToNative(pStream->hFile), fEchoChars);
+#else
+ int rc;
+ int fh = fileno(pStream->pFile);
+ if (isatty(fh))
+ {
+# ifdef RT_OS_WINDOWS
+ rc = rtStrmInputSetEchoCharsNative(_get_osfhandle(fh), fEchoChars);
+# else
+ rc = rtStrmInputSetEchoCharsNative(fh, fEchoChars);
+# endif
+ }
+ else
+ rc = VERR_INVALID_FUNCTION;
+ return rc;
+#endif
+}
+
+
+RTR3DECL(bool) RTStrmIsTerminal(PRTSTREAM pStream)
+{
+ AssertPtrReturn(pStream, false);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, false);
+
+ return rtStrmIsTerminal(pStream);
+}
+
+
+RTR3DECL(int) RTStrmQueryTerminalWidth(PRTSTREAM pStream, uint32_t *pcchWidth)
+{
+ AssertPtrReturn(pcchWidth, VERR_INVALID_HANDLE);
+ *pcchWidth = 80;
+
+ AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
+
+ if (rtStrmIsTerminal(pStream))
+ {
+#ifdef RT_OS_WINDOWS
+# ifdef RTSTREAM_STANDALONE
+ HANDLE hCon = (HANDLE)RTFileToNative(pStream->hFile);
+# else
+ HANDLE hCon = (HANDLE)_get_osfhandle(fileno(pStream->pFile));
+# endif
+ CONSOLE_SCREEN_BUFFER_INFO Info;
+ RT_ZERO(Info);
+ if (GetConsoleScreenBufferInfo(hCon, &Info))
+ {
+ *pcchWidth = Info.dwSize.X ? Info.dwSize.X : 80;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+
+#elif defined(RT_OS_OS2) && !defined(TIOCGWINSZ) /* only OS/2 should currently miss this */
+ return VINF_SUCCESS; /* just pretend for now. */
+
+#else
+ struct winsize Info;
+ RT_ZERO(Info);
+ int rc = ioctl(fileno(pStream->pFile), TIOCGWINSZ, &Info);
+ if (rc >= 0)
+ {
+ *pcchWidth = Info.ws_col ? Info.ws_col : 80;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+#endif
+ }
+ return VERR_INVALID_FUNCTION;
+}
+
+
+#ifdef RTSTREAM_STANDALONE
+
+DECLINLINE(void) rtStrmBufInvalidate(PRTSTREAM pStream)
+{
+ pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
+ pStream->offBufEnd = 0;
+ pStream->offBufFirst = 0;
+}
+
+
+static int rtStrmBufFlushWrite(PRTSTREAM pStream, size_t cbToFlush)
+{
+ Assert(cbToFlush <= pStream->offBufEnd - pStream->offBufFirst);
+
+ RTFILE const hFile = rtStrmGetFile(pStream);
+ if (hFile != NIL_RTFILE)
+ {
+ /** @todo do nonblocking & incomplete writes? */
+ size_t offBufFirst = pStream->offBufFirst;
+ int rc = RTFileWrite(hFile, &pStream->pchBuf[offBufFirst], cbToFlush, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ offBufFirst += cbToFlush;
+ if (offBufFirst >= pStream->offBufEnd)
+ pStream->offBufEnd = 0;
+ else
+ {
+ /* Shift up the remaining content so the next write can take full
+ advantage of the buffer size. */
+ size_t cbLeft = pStream->offBufEnd - offBufFirst;
+ memmove(pStream->pchBuf, &pStream->pchBuf[offBufFirst], cbLeft);
+ pStream->offBufEnd = cbLeft;
+ }
+ pStream->offBufFirst = 0;
+ return VINF_SUCCESS;
+ }
+ return rc;
+ }
+ return VERR_INVALID_HANDLE;
+}
+
+
+static int rtStrmBufFlushWriteMaybe(PRTSTREAM pStream, bool fInvalidate)
+{
+ if (pStream->enmBufDir == RTSTREAMBUFDIR_WRITE)
+ {
+ size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
+ if (cbInBuffer > 0)
+ {
+ int rc = rtStrmBufFlushWrite(pStream, cbInBuffer);
+ if (fInvalidate)
+ pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
+ return rc;
+ }
+ }
+ if (fInvalidate)
+ rtStrmBufInvalidate(pStream);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtStrmBufCheckErrorAndSwitchToReadMode and
+ * rtStrmBufCheckErrorAndSwitchToWriteMode that allocates a buffer.
+ *
+ * Only updates cbBufAlloc and pchBuf, callers deals with error fallout.
+ */
+static int rtStrmBufAlloc(PRTSTREAM pStream)
+{
+ size_t cbBuf = pStream->enmBufStyle == RTSTREAMBUFSTYLE_FULL ? _64K : _16K;
+ do
+ {
+ pStream->pchBuf = (char *)RTMemAllocZ(cbBuf);
+ if (RT_LIKELY(pStream->pchBuf))
+ {
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ Assert(RT_ALIGN_Z(cbBuf, 64 / 8) == cbBuf);
+ pStream->pbmBuf = (uint32_t *)RTMemAllocZ(cbBuf / 8);
+ if (RT_LIKELY(pStream->pbmBuf))
+# endif
+ {
+ pStream->cbBufAlloc = cbBuf;
+ return VINF_SUCCESS;
+ }
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ RTMemFree(pStream->pchBuf);
+ pStream->pchBuf = NULL;
+# endif
+ }
+ cbBuf /= 2;
+ } while (cbBuf >= 256);
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Checks the stream error status, flushed any pending writes, ensures there is
+ * a buffer allocated and switches the stream to the read direction.
+ *
+ * @returns IPRT status code (same as i32Error).
+ * @param pStream The stream.
+ */
+static int rtStrmBufCheckErrorAndSwitchToReadMode(PRTSTREAM pStream)
+{
+ int rc = pStream->i32Error;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We're very likely already in read mode and can return without doing
+ * anything here.
+ */
+ if (pStream->enmBufDir == RTSTREAMBUFDIR_READ)
+ return VINF_SUCCESS;
+
+ /*
+ * Flush any pending writes before switching the buffer to read:
+ */
+ rc = rtStrmBufFlushWriteMaybe(pStream, false /*fInvalidate*/);
+ if (RT_SUCCESS(rc))
+ {
+ pStream->enmBufDir = RTSTREAMBUFDIR_READ;
+ pStream->offBufEnd = 0;
+ pStream->offBufFirst = 0;
+ pStream->fPendingCr = false;
+
+ /*
+ * Read direction implies a buffer, so make sure we've got one and
+ * change to NONE direction if allocating one fails.
+ */
+ if (pStream->pchBuf)
+ {
+ Assert(pStream->cbBufAlloc >= 256);
+ return VINF_SUCCESS;
+ }
+
+ rc = rtStrmBufAlloc(pStream);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
+ }
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Checks the stream error status, ensures there is a buffer allocated and
+ * switches the stream to the write direction.
+ *
+ * @returns IPRT status code (same as i32Error).
+ * @param pStream The stream.
+ */
+static int rtStrmBufCheckErrorAndSwitchToWriteMode(PRTSTREAM pStream)
+{
+ int rc = pStream->i32Error;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We're very likely already in write mode and can return without doing
+ * anything here.
+ */
+ if (pStream->enmBufDir == RTSTREAMBUFDIR_WRITE)
+ return VINF_SUCCESS;
+
+ /*
+ * A read buffer does not need any flushing, so we just have to make
+ * sure there is a buffer present before switching to the write direction.
+ */
+ pStream->enmBufDir = RTSTREAMBUFDIR_WRITE;
+ pStream->offBufEnd = 0;
+ pStream->offBufFirst = 0;
+ if (pStream->pchBuf)
+ {
+ Assert(pStream->cbBufAlloc >= 256);
+ return VINF_SUCCESS;
+ }
+
+ rc = rtStrmBufAlloc(pStream);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ pStream->enmBufDir = RTSTREAMBUFDIR_NONE;
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Reads more bytes into the buffer.
+ *
+ * @returns IPRT status code (same as i32Error).
+ * @param pStream The stream.
+ */
+static int rtStrmBufFill(PRTSTREAM pStream)
+{
+ /*
+ * Check preconditions
+ */
+ Assert(pStream->i32Error == VINF_SUCCESS);
+ Assert(pStream->enmBufDir == RTSTREAMBUFDIR_READ);
+ AssertPtr(pStream->pchBuf);
+ Assert(pStream->cbBufAlloc >= 256);
+ Assert(RT_ALIGN_Z(pStream->cbBufAlloc, 64) == pStream->cbBufAlloc);
+ Assert(pStream->offBufFirst <= pStream->cbBufAlloc);
+ Assert(pStream->offBufEnd <= pStream->cbBufAlloc);
+ Assert(pStream->offBufFirst <= pStream->offBufEnd);
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ AssertPtr(pStream->pbmBuf);
+# endif
+ /*
+ * If there is data in the buffer, move it up to the start.
+ */
+ size_t cbInBuffer;
+ if (!pStream->offBufFirst)
+ cbInBuffer = pStream->offBufEnd;
+ else
+ {
+ cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
+ if (cbInBuffer)
+ {
+ memmove(pStream->pchBuf, &pStream->pchBuf[pStream->offBufFirst], cbInBuffer);
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ if (!pStream->fBinary) /** @todo this isn't very efficient, must be a better way of shifting a bitmap. */
+ for (size_t off = 0; off < pStream->offBufFirst; off++)
+ if (ASMBitTest(pStream->pbmBuf, (int32_t)off))
+ ASMBitSet(pStream->pbmBuf, (int32_t)off);
+ else
+ ASMBitClear(pStream->pbmBuf, (int32_t)off);
+# endif
+ }
+ pStream->offBufFirst = 0;
+ pStream->offBufEnd = cbInBuffer;
+ }
+
+ /*
+ * Add pending CR to the buffer.
+ */
+ size_t const offCrLfConvStart = cbInBuffer;
+ Assert(cbInBuffer + 2 <= pStream->cbBufAlloc);
+ if (!pStream->fPendingCr || pStream->fBinary)
+ { /* likely */ }
+ else
+ {
+ pStream->pchBuf[cbInBuffer] = '\r';
+ pStream->fPendingCr = false;
+ pStream->offBufEnd = ++cbInBuffer;
+ }
+
+ /*
+ * Read data till the buffer is full.
+ */
+ int rc = VERR_INVALID_HANDLE;
+ RTFILE const hFile = rtStrmGetFile(pStream);
+ if (hFile != NIL_RTFILE)
+ {
+ size_t cbRead = 0;
+ rc = RTFileRead(hFile, &pStream->pchBuf[cbInBuffer], pStream->cbBufAlloc - cbInBuffer, &cbRead);
+ if (RT_SUCCESS(rc))
+ {
+ cbInBuffer += cbRead;
+ pStream->offBufEnd = cbInBuffer;
+
+ if (cbInBuffer != 0)
+ {
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ if (pStream->fBinary)
+# endif
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ /** @todo this shouldn't be sticky, should it? */
+ ASMAtomicWriteS32(&pStream->i32Error, VERR_EOF);
+ return VERR_EOF;
+ }
+
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ /*
+ * Do CRLF -> LF conversion in the buffer.
+ */
+ ASMBitClearRange(pStream->pbmBuf, offCrLfConvStart, RT_ALIGN_Z(cbInBuffer, 64));
+ char *pchCur = &pStream->pchBuf[offCrLfConvStart];
+ size_t cbLeft = cbInBuffer - offCrLfConvStart;
+ while (cbLeft > 0)
+ {
+ Assert(&pchCur[cbLeft] == &pStream->pchBuf[pStream->offBufEnd]);
+ char *pchCr = (char *)memchr(pchCur, '\r', cbLeft);
+ if (pchCr)
+ {
+ size_t offCur = (size_t)(pchCr - pchCur);
+ if (offCur + 1 < cbLeft)
+ {
+ if (pchCr[1] == '\n')
+ {
+ /* Found one '\r\n' sequence. Look for more before shifting the buffer content. */
+ cbLeft -= offCur;
+ pchCur = pchCr;
+
+ do
+ {
+ ASMBitSet(pStream->pbmBuf, (int32_t)(pchCur - pStream->pchBuf));
+ *pchCur++ = '\n'; /* dst */
+ cbLeft -= 2;
+ pchCr += 2; /* src */
+ } while (cbLeft >= 2 && pchCr[0] == '\r' && pchCr[1] == '\n');
+
+ memmove(&pchCur, pchCr, cbLeft);
+ }
+ else
+ {
+ cbLeft -= offCur + 1;
+ pchCur = pchCr + 1;
+ }
+ }
+ else
+ {
+ Assert(pchCr == &pStream->pchBuf[pStream->offBufEnd - 1]);
+ pStream->fPendingCr = true;
+ pStream->offBufEnd = --cbInBuffer;
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ return VINF_SUCCESS;
+# endif
+ }
+ }
+
+ /*
+ * If there is data in the buffer, don't raise the error till it has all
+ * been consumed, ASSUMING that another fill call will follow and that the
+ * error condition will reoccur then.
+ *
+ * Note! We may currently end up not converting a CRLF pair, if it's
+ * split over a temporary EOF condition, since we forces the caller
+ * to read the CR before requesting more data. However, it's not a
+ * very likely scenario, so we'll just leave it like that for now.
+ */
+ if (cbInBuffer)
+ return VINF_SUCCESS;
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+ return rc;
+}
+
+
+/**
+ * Copies @a cbSrc bytes from @a pvSrc and into the buffer, flushing as needed
+ * to make space available.
+ *
+ *
+ * @returns IPRT status code (errors not assigned to i32Error).
+ * @param pStream The stream.
+ * @param pvSrc The source buffer.
+ * @param cbSrc Number of bytes to copy from @a pvSrc.
+ * @param pcbTotal A total counter to update with what was copied.
+ */
+static int rtStrmBufCopyTo(PRTSTREAM pStream, const void *pvSrc, size_t cbSrc, size_t *pcbTotal)
+{
+ Assert(cbSrc > 0);
+ for (;;)
+ {
+ size_t cbToCopy = RT_MIN(pStream->cbBufAlloc - pStream->offBufEnd, cbSrc);
+ if (cbToCopy)
+ {
+ memcpy(&pStream->pchBuf[pStream->offBufEnd], pvSrc, cbToCopy);
+ pStream->offBufEnd += cbToCopy;
+ pvSrc = (const char *)pvSrc + cbToCopy;
+ *pcbTotal += cbToCopy;
+ cbSrc -= cbToCopy;
+ if (!cbSrc)
+ break;
+ }
+
+ int rc = rtStrmBufFlushWrite(pStream, pStream->offBufEnd - pStream->offBufFirst);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtStrmFlushAndCloseAll and rtStrmFlushAndClose.
+ */
+static RTFILE rtStrmFlushAndCleanup(PRTSTREAM pStream)
+{
+ if (pStream->pchBuf)
+ {
+ if ( pStream->enmBufDir == RTSTREAMBUFDIR_WRITE
+ && pStream->offBufFirst < pStream->offBufEnd
+ && RT_SUCCESS(pStream->i32Error) )
+ rtStrmBufFlushWrite(pStream, pStream->offBufEnd - pStream->offBufFirst);
+ RTMemFree(pStream->pchBuf);
+ pStream->pchBuf = NULL;
+ pStream->offBufFirst = 0;
+ pStream->offBufEnd = 0;
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ RTMemFree(pStream->pbmBuf);
+ pStream->pbmBuf = NULL;
+# endif
+ }
+
+ PRTCRITSECT pCritSect = pStream->pCritSect;
+ if (pCritSect)
+ {
+ pStream->pCritSect = NULL;
+ RTCritSectDelete(pCritSect);
+ RTMemFree(pCritSect);
+ }
+
+ RTFILE hFile = pStream->hFile;
+ pStream->hFile = NIL_RTFILE;
+ return hFile;
+}
+
+
+/**
+ * Worker for rtStrmFlushAndCloseAll.
+ */
+static void rtStrmFlushAndClose(PRTSTREAM pStream)
+{
+ pStream->u32Magic = ~RTSTREAM_MAGIC;
+ RTFILE hFile = rtStrmFlushAndCleanup(pStream);
+ if (hFile != NIL_RTFILE)
+ RTFileClose(hFile);
+ RTMemFree(pStream);
+}
+
+
+/**
+ * Flushes and cleans up the standard streams, should flush and close all others
+ * too but doesn't yet...
+ */
+DECLCALLBACK(void) rtStrmFlushAndCloseAll(void)
+{
+ /*
+ * Flush the standard handles.
+ */
+ rtStrmFlushAndCleanup(&g_StdOut);
+ rtStrmFlushAndCleanup(&g_StdErr);
+ rtStrmFlushAndCleanup(&g_StdIn);
+
+ /*
+ * Make a list of the rest and flush+close those too.
+ */
+ if (RTOnceWasInitialized(&g_StreamListOnce))
+ {
+ RTCritSectDelete(&g_StreamListCritSect);
+
+ PRTSTREAM pStream;
+ while ((pStream = RTListRemoveFirst(&g_StreamList, RTSTREAM, ListEntry)) != NULL)
+ rtStrmFlushAndClose(pStream);
+
+ RTOnceReset(&g_StreamListOnce);
+ }
+}
+
+# ifdef IPRT_COMPILER_TERM_CALLBACK
+IPRT_COMPILER_TERM_CALLBACK(rtStrmFlushAndCloseAll);
+# endif
+
+#endif /* RTSTREAM_STANDALONE */
+
+
+RTR3DECL(int) RTStrmRewind(PRTSTREAM pStream)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSTREAM_STANDALONE
+ rtStrmLock(pStream);
+ int const rc1 = rtStrmBufFlushWriteMaybe(pStream, true /*fInvalidate*/);
+ int const rc2 = RTFileSeek(rtStrmGetFile(pStream), 0, RTFILE_SEEK_BEGIN, NULL);
+ int rc = RT_SUCCESS(rc1) ? rc2 : rc1;
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+ rtStrmUnlock(pStream);
+#else
+ clearerr(pStream->pFile);
+ errno = 0;
+ int rc;
+ if (!fseek(pStream->pFile, 0, SEEK_SET))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+#endif
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrmSeek(PRTSTREAM pStream, RTFOFF off, uint32_t uMethod)
+{
+ AssertReturn(uMethod <= RTFILE_SEEK_END, VERR_INVALID_PARAMETER);
+#ifdef RTSTREAM_STANDALONE
+ rtStrmLock(pStream);
+ int rc = rtStrmBufFlushWriteMaybe(pStream, true /*fInvalidate*/);
+ if (RT_SUCCESS(rc))
+ rc = RTFileSeek(rtStrmGetFile(pStream), off, uMethod, NULL);
+ if (RT_FAILURE(rc))
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+ rtStrmUnlock(pStream);
+#else
+ int const iCrtMethod = uMethod == RTFILE_SEEK_BEGIN ? SEEK_SET : uMethod == RTFILE_SEEK_CURRENT ? SEEK_CUR : SEEK_END;
+ errno = 0;
+ int rc;
+# ifdef _MSC_VER
+ if (!_fseeki64(pStream->pFile, off, iCrtMethod))
+# else
+ if (!fseeko(pStream->pFile, off, iCrtMethod))
+# endif
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(errno);
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+#endif
+ return rc;
+}
+
+
+RTR3DECL(RTFOFF) RTStrmTell(PRTSTREAM pStream)
+{
+#ifdef RTSTREAM_STANDALONE
+ uint64_t off = 0;
+ rtStrmLock(pStream);
+ int rc = pStream->i32Error;
+ if (RT_SUCCESS(rc))
+ {
+ RTFILE const hFile = rtStrmGetFile(pStream);
+ if (hFile != NIL_RTFILE)
+ {
+ rc = RTFileSeek(hFile, 0, RTFILE_SEEK_CURRENT, &off);
+ if (RT_SUCCESS(rc))
+ {
+ switch (pStream->enmBufDir)
+ {
+ case RTSTREAMBUFDIR_READ:
+ /* Subtract unconsumed chars and removed '\r' characters. */
+ off -= pStream->offBufEnd - pStream->offBufFirst;
+ if (!pStream->fBinary)
+ for (size_t offBuf = pStream->offBufFirst; offBuf < pStream->offBufEnd; offBuf++)
+ off -= ASMBitTest(pStream->pbmBuf, (int32_t)offBuf);
+ break;
+ case RTSTREAMBUFDIR_WRITE:
+ /* Add unwrittend chars in the buffer. */
+ off += pStream->offBufEnd - pStream->offBufFirst;
+ break;
+ default:
+ AssertFailed();
+ case RTSTREAMBUFDIR_NONE:
+ break;
+ }
+ }
+ }
+ else
+ rc = VERR_INVALID_HANDLE;
+ }
+ if (RT_FAILURE(rc))
+ {
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+ off = rc;
+ }
+ rtStrmUnlock(pStream);
+#else
+# ifdef _MSC_VER
+ RTFOFF off = _ftelli64(pStream->pFile);
+# else
+ RTFOFF off = ftello(pStream->pFile);
+# endif
+ if (off < 0)
+ {
+ int rc = RTErrConvertFromErrno(errno);
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+ off = rc;
+ }
+#endif
+ return off;
+}
+
+
+/**
+ * Recheck the stream mode.
+ *
+ * @param pStream The stream (locked).
+ */
+static void rtStreamRecheckMode(PRTSTREAM pStream)
+{
+#if (defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)) && !defined(RTSTREAM_STANDALONE)
+ int fh = fileno(pStream->pFile);
+ if (fh >= 0)
+ {
+ int fExpected = pStream->fBinary ? _O_BINARY : _O_TEXT;
+ int fActual = _setmode(fh, fExpected);
+ if (fActual != -1 && fExpected != (fActual & (_O_BINARY | _O_TEXT)))
+ {
+ fActual = _setmode(fh, fActual & (_O_BINARY | _O_TEXT));
+ pStream->fBinary = !(fActual & _O_TEXT);
+ }
+ }
+#else
+ NOREF(pStream);
+#endif
+ pStream->fRecheckMode = false;
+}
+
+
+RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSTREAM_STANDALONE
+ rtStrmLock(pStream);
+ int rc = rtStrmBufCheckErrorAndSwitchToReadMode(pStream);
+#else
+ int rc = pStream->i32Error;
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ if (pStream->fRecheckMode)
+ rtStreamRecheckMode(pStream);
+
+#ifdef RTSTREAM_STANDALONE
+
+ /*
+ * Copy data thru the read buffer for now as that'll handle both binary
+ * and text modes seamlessly. We could optimize larger reads here when
+ * in binary mode, that can wait till the basics work, I think.
+ */
+ size_t cbTotal = 0;
+ if (cbToRead > 0)
+ for (;;)
+ {
+ size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
+ if (cbInBuffer > 0)
+ {
+ size_t cbToCopy = RT_MIN(cbInBuffer, cbToRead);
+ memcpy(pvBuf, &pStream->pchBuf[pStream->offBufFirst], cbToCopy);
+ cbTotal += cbToRead;
+ cbToRead -= cbToCopy;
+ pvBuf = (char *)pvBuf + cbToCopy;
+ if (!cbToRead)
+ break;
+ }
+ rc = rtStrmBufFill(pStream);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ {
+ if (rc == VERR_EOF && pcbRead && cbTotal > 0)
+ rc = VINF_EOF;
+ break;
+ }
+ }
+ if (pcbRead)
+ *pcbRead = cbTotal;
+
+#else /* !RTSTREAM_STANDALONE */
+ if (pcbRead)
+ {
+ /*
+ * Can do with a partial read.
+ */
+ *pcbRead = fread(pvBuf, 1, cbToRead, pStream->pFile);
+ if ( *pcbRead == cbToRead
+ || !ferror(pStream->pFile))
+ rc = VINF_SUCCESS;
+ else if (feof(pStream->pFile))
+ rc = *pcbRead ? VINF_EOF : VERR_EOF;
+ else if (ferror(pStream->pFile))
+ rc = VERR_READ_ERROR;
+ else
+ {
+ AssertMsgFailed(("This shouldn't happen\n"));
+ rc = VERR_INTERNAL_ERROR;
+ }
+ }
+ else
+ {
+ /*
+ * Must read it all!
+ */
+ if (fread(pvBuf, cbToRead, 1, pStream->pFile) == 1)
+ rc = VINF_SUCCESS;
+ /* possible error/eof. */
+ else if (feof(pStream->pFile))
+ rc = VERR_EOF;
+ else if (ferror(pStream->pFile))
+ rc = VERR_READ_ERROR;
+ else
+ {
+ AssertMsgFailed(("This shouldn't happen\n"));
+ rc = VERR_INTERNAL_ERROR;
+ }
+ }
+#endif /* !RTSTREAM_STANDALONE */
+ if (RT_FAILURE(rc))
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+ }
+#ifdef RTSTREAM_STANDALONE
+ rtStrmUnlock(pStream);
+#endif
+ return rc;
+}
+
+
+/**
+ * Check if the input text is valid UTF-8.
+ *
+ * @returns true/false.
+ * @param pvBuf Pointer to the buffer.
+ * @param cbBuf Size of the buffer.
+ */
+static bool rtStrmIsUtf8Text(const void *pvBuf, size_t cbBuf)
+{
+ NOREF(pvBuf);
+ NOREF(cbBuf);
+ /** @todo not sure this is a good idea... Better redefine RTStrmWrite. */
+ return false;
+}
+
+
+#if defined(RT_OS_WINDOWS) && !defined(RTSTREAM_STANDALONE)
+
+/**
+ * Check if the stream is for a Window console.
+ *
+ * @returns true / false.
+ * @param pStream The stream.
+ * @param phCon Where to return the console handle.
+ */
+static bool rtStrmIsConsoleUnlocked(PRTSTREAM pStream, HANDLE *phCon)
+{
+ int fh = fileno(pStream->pFile);
+ if (isatty(fh))
+ {
+ DWORD dwMode;
+ HANDLE hCon = (HANDLE)_get_osfhandle(fh);
+ if (GetConsoleMode(hCon, &dwMode))
+ {
+ *phCon = hCon;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+static int rtStrmWriteWinConsoleLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, HANDLE hCon)
+{
+ int rc;
+# ifdef HAVE_FWRITE_UNLOCKED
+ if (!fflush_unlocked(pStream->pFile))
+# else
+ if (!fflush(pStream->pFile))
+# endif
+ {
+ /** @todo Consider buffering later. For now, we'd rather correct output than
+ * fast output. */
+ DWORD cwcWritten = 0;
+ PRTUTF16 pwszSrc = NULL;
+ size_t cwcSrc = 0;
+ rc = RTStrToUtf16Ex((const char *)pvBuf, cbToWrite, &pwszSrc, 0, &cwcSrc);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ if (!WriteConsoleW(hCon, pwszSrc, (DWORD)cwcSrc, &cwcWritten, NULL))
+ {
+ /* try write char-by-char to avoid heap problem. */
+ cwcWritten = 0;
+ while (cwcWritten != cwcSrc)
+ {
+ DWORD cwcThis;
+ if (!WriteConsoleW(hCon, &pwszSrc[cwcWritten], 1, &cwcThis, NULL))
+ {
+ if (!pcbWritten || cwcWritten == 0)
+ rc = RTErrConvertFromErrno(GetLastError());
+ break;
+ }
+ if (cwcThis != 1) /* Unable to write current char (amount)? */
+ break;
+ cwcWritten++;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ if (cwcWritten == cwcSrc)
+ {
+ if (pcbWritten)
+ *pcbWritten = cbToWrite;
+ }
+ else if (pcbWritten)
+ {
+ PCRTUTF16 pwszCur = pwszSrc;
+ const char *pszCur = (const char *)pvBuf;
+ while ((uintptr_t)(pwszCur - pwszSrc) < cwcWritten)
+ {
+ RTUNICP CpIgnored;
+ RTUtf16GetCpEx(&pwszCur, &CpIgnored);
+ RTStrGetCpEx(&pszCur, &CpIgnored);
+ }
+ *pcbWritten = pszCur - (const char *)pvBuf;
+ }
+ else
+ rc = VERR_WRITE_ERROR;
+ }
+ RTUtf16Free(pwszSrc);
+ }
+ }
+ else
+ rc = RTErrConvertFromErrno(errno);
+ return rc;
+}
+
+#endif /* RT_OS_WINDOWS */
+
+static int rtStrmWriteWorkerLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fMustWriteAll)
+{
+#ifdef RTSTREAM_STANDALONE
+ /*
+ * Check preconditions.
+ */
+ Assert(pStream->enmBufDir == RTSTREAMBUFDIR_WRITE);
+ Assert(pStream->cbBufAlloc >= 256);
+ Assert(pStream->offBufFirst <= pStream->cbBufAlloc);
+ Assert(pStream->offBufEnd <= pStream->cbBufAlloc);
+ Assert(pStream->offBufFirst <= pStream->offBufEnd);
+
+ /*
+ * We write everything via the buffer, letting the buffer flushing take
+ * care of console output hacks and similar.
+ */
+ RT_NOREF(fMustWriteAll);
+ int rc = VINF_SUCCESS;
+ size_t cbTotal = 0;
+ if (cbToWrite > 0)
+ {
+# ifdef RTSTREAM_WITH_TEXT_MODE
+ const char *pchLf;
+ if ( !pStream->fBinary
+ && (pchLf = (const char *)memchr(pvBuf, '\n', cbToWrite)) != NULL)
+ for (;;)
+ {
+ /* Deal with everything up to the newline. */
+ size_t const cbToLf = (size_t)(pchLf - (const char *)pvBuf);
+ if (cbToLf > 0)
+ {
+ rc = rtStrmBufCopyTo(pStream, pvBuf, cbToLf, &cbTotal);
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ /* Copy the CRLF sequence into the buffer in one go to avoid complications. */
+ if (pStream->cbBufAlloc - pStream->offBufEnd < 2)
+ {
+ rc = rtStrmBufFlushWrite(pStream, pStream->offBufEnd - pStream->offBufFirst);
+ if (RT_FAILURE(rc))
+ break;
+ Assert(pStream->cbBufAlloc - pStream->offBufEnd >= 2);
+ }
+ pStream->pchBuf[pStream->offBufEnd++] = '\r';
+ pStream->pchBuf[pStream->offBufEnd++] = '\n';
+
+ /* Advance past the newline. */
+ pvBuf = (const char *)pvBuf + 1 + cbToLf;
+ cbTotal += 1 + cbToLf;
+ cbToWrite -= 1 + cbToLf;
+ if (!cbToWrite)
+ break;
+
+ /* More newlines? */
+ pchLf = (const char *)memchr(pvBuf, '\n', cbToWrite);
+ if (!pchLf)
+ {
+ rc = rtStrmBufCopyTo(pStream, pvBuf, cbToWrite, &cbTotal);
+ break;
+ }
+ }
+ else
+# endif
+ rc = rtStrmBufCopyTo(pStream, pvBuf, cbToWrite, &cbTotal);
+
+ /*
+ * If line buffered or unbuffered, we probably have to do some flushing now.
+ */
+ if (RT_SUCCESS(rc) && pStream->enmBufStyle != RTSTREAMBUFSTYLE_FULL)
+ {
+ Assert(pStream->enmBufStyle == RTSTREAMBUFSTYLE_LINE || pStream->enmBufStyle == RTSTREAMBUFSTYLE_UNBUFFERED);
+ size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
+ if (cbInBuffer > 0)
+ {
+ if ( pStream->enmBufStyle != RTSTREAMBUFSTYLE_LINE
+ || pStream->pchBuf[pStream->offBufEnd - 1] == '\n')
+ rc = rtStrmBufFlushWrite(pStream, cbInBuffer);
+ else
+ {
+ const char *pchToFlush = &pStream->pchBuf[pStream->offBufFirst];
+ const char *pchLastLf = (const char *)memrchr(pchToFlush, '\n', cbInBuffer);
+ if (pchLastLf)
+ rc = rtStrmBufFlushWrite(pStream, (size_t)(&pchLastLf[1] - pchToFlush));
+ }
+ }
+ }
+ }
+ if (pcbWritten)
+ *pcbWritten = cbTotal;
+ return rc;
+
+
+#else
+ if (!fMustWriteAll)
+ {
+ IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
+# ifdef HAVE_FWRITE_UNLOCKED
+ *pcbWritten = fwrite_unlocked(pvBuf, 1, cbToWrite, pStream->pFile);
+# else
+ *pcbWritten = fwrite(pvBuf, 1, cbToWrite, pStream->pFile);
+# endif
+ IPRT_ALIGNMENT_CHECKS_ENABLE();
+ if ( *pcbWritten == cbToWrite
+# ifdef HAVE_FWRITE_UNLOCKED
+ || !ferror_unlocked(pStream->pFile))
+# else
+ || !ferror(pStream->pFile))
+# endif
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ /* Must write it all! */
+ IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc / mempcpy again */
+# ifdef HAVE_FWRITE_UNLOCKED
+ size_t cbWritten = fwrite_unlocked(pvBuf, cbToWrite, 1, pStream->pFile);
+# else
+ size_t cbWritten = fwrite(pvBuf, cbToWrite, 1, pStream->pFile);
+# endif
+ if (pcbWritten)
+ *pcbWritten = cbWritten;
+ IPRT_ALIGNMENT_CHECKS_ENABLE();
+ if (cbWritten == 1)
+ return VINF_SUCCESS;
+# ifdef HAVE_FWRITE_UNLOCKED
+ if (!ferror_unlocked(pStream->pFile))
+# else
+ if (!ferror(pStream->pFile))
+# endif
+ return VINF_SUCCESS; /* WEIRD! But anyway... */
+ }
+ return VERR_WRITE_ERROR;
+#endif
+}
+
+
+/**
+ * Internal write API, stream lock already held.
+ *
+ * @returns IPRT status code.
+ * @param pStream The stream.
+ * @param pvBuf What to write.
+ * @param cbToWrite How much to write.
+ * @param pcbWritten Where to optionally return the number of bytes
+ * written.
+ * @param fSureIsText Set if we're sure this is UTF-8 text already.
+ */
+static int rtStrmWriteLocked(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fSureIsText)
+{
+#ifdef RTSTREAM_STANDALONE
+ int rc = rtStrmBufCheckErrorAndSwitchToWriteMode(pStream);
+#else
+ int rc = pStream->i32Error;
+#endif
+ if (RT_FAILURE(rc))
+ return rc;
+ if (pStream->fRecheckMode)
+ rtStreamRecheckMode(pStream);
+
+#if defined(RT_OS_WINDOWS) && !defined(RTSTREAM_STANDALONE)
+ /*
+ * Use the unicode console API when possible in order to avoid stuff
+ * getting lost in unnecessary code page translations.
+ */
+ HANDLE hCon;
+ if (rtStrmIsConsoleUnlocked(pStream, &hCon))
+ rc = rtStrmWriteWinConsoleLocked(pStream, pvBuf, cbToWrite, pcbWritten, hCon);
+#else
+ if (0) { }
+#endif /* RT_OS_WINDOWS && !RTSTREAM_STANDALONE */
+
+ /*
+ * If we're sure it's text output, convert it from UTF-8 to the current
+ * code page before printing it.
+ *
+ * Note! Partial writes are not supported in this scenario because we
+ * cannot easily report back a written length matching the input.
+ */
+ /** @todo Skip this if the current code set is UTF-8. */
+ else if ( pStream->fCurrentCodeSet
+ && !pStream->fBinary
+ && ( fSureIsText
+ || rtStrmIsUtf8Text(pvBuf, cbToWrite))
+ )
+ {
+ char *pszSrcFree = NULL;
+ const char *pszSrc = (const char *)pvBuf;
+ if (pszSrc[cbToWrite - 1])
+ {
+ pszSrc = pszSrcFree = RTStrDupN(pszSrc, cbToWrite);
+ if (pszSrc == NULL)
+ rc = VERR_NO_STR_MEMORY;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ char *pszSrcCurCP;
+ rc = RTStrUtf8ToCurrentCP(&pszSrcCurCP, pszSrc);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchSrcCurCP = strlen(pszSrcCurCP);
+ size_t cbWritten = 0;
+ rc = rtStrmWriteWorkerLocked(pStream, pszSrcCurCP, cchSrcCurCP, &cbWritten, true /*fMustWriteAll*/);
+ if (pcbWritten)
+ *pcbWritten = cbWritten == cchSrcCurCP ? cbToWrite : 0;
+ RTStrFree(pszSrcCurCP);
+ }
+ RTStrFree(pszSrcFree);
+ }
+ }
+ /*
+ * Otherwise, just write it as-is.
+ */
+ else
+ rc = rtStrmWriteWorkerLocked(pStream, pvBuf, cbToWrite, pcbWritten, pcbWritten == NULL);
+
+ /*
+ * Update error status on failure and return.
+ *
+ * We ignore failures from RTStrUtf8ToCurrentCP and RTStrToUtf16Ex regarding
+ * invalid UTF-8 encoding, as that's an input issue and shouldn't affect the
+ * stream state.
+ */
+ if (RT_FAILURE(rc) && rc != VERR_INVALID_UTF8_ENCODING)
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+ return rc;
+}
+
+
+/**
+ * Internal write API.
+ *
+ * @returns IPRT status code.
+ * @param pStream The stream.
+ * @param pvBuf What to write.
+ * @param cbToWrite How much to write.
+ * @param pcbWritten Where to optionally return the number of bytes
+ * written.
+ * @param fSureIsText Set if we're sure this is UTF-8 text already.
+ */
+DECLINLINE(int) rtStrmWrite(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten, bool fSureIsText)
+{
+ rtStrmLock(pStream);
+ int rc = rtStrmWriteLocked(pStream, pvBuf, cbToWrite, pcbWritten, fSureIsText);
+ rtStrmUnlock(pStream);
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
+ return rtStrmWrite(pStream, pvBuf, cbToWrite, pcbWritten, false);
+}
+
+
+RTR3DECL(int) RTStrmGetCh(PRTSTREAM pStream)
+{
+ unsigned char ch;
+ int rc = RTStrmReadEx(pStream, &ch, 1, NULL);
+ if (RT_SUCCESS(rc))
+ return ch;
+ return -1;
+}
+
+
+RTR3DECL(int) RTStrmPutCh(PRTSTREAM pStream, int ch)
+{
+ return rtStrmWrite(pStream, &ch, 1, NULL, true /*fSureIsText*/);
+}
+
+
+RTR3DECL(int) RTStrmPutStr(PRTSTREAM pStream, const char *pszString)
+{
+ size_t cch = strlen(pszString);
+ return rtStrmWrite(pStream, pszString, cch, NULL, true /*fSureIsText*/);
+}
+
+
+RTR3DECL(int) RTStrmGetLine(PRTSTREAM pStream, char *pszString, size_t cbString)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pszString, VERR_INVALID_POINTER);
+ AssertReturn(cbString >= 2, VERR_INVALID_PARAMETER);
+
+ rtStrmLock(pStream);
+
+#ifdef RTSTREAM_STANDALONE
+ int rc = rtStrmBufCheckErrorAndSwitchToReadMode(pStream);
+#else
+ int rc = pStream->i32Error;
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ cbString--; /* Reserve space for the terminator. */
+
+#ifdef RTSTREAM_STANDALONE
+ char * const pszStringStart = pszString;
+#endif
+ for (;;)
+ {
+#ifdef RTSTREAM_STANDALONE
+ /* Make sure there is at least one character in the buffer: */
+ size_t cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
+ if (cbInBuffer == 0)
+ {
+ rc = rtStrmBufFill(pStream);
+ if (RT_SUCCESS(rc))
+ cbInBuffer = pStream->offBufEnd - pStream->offBufFirst;
+ else
+ break;
+ }
+
+ /* Scan the buffer content terminating on a '\n', '\r\n' and '\0' sequence. */
+ const char *pchSrc = &pStream->pchBuf[pStream->offBufFirst];
+ const char *pchNewline = (const char *)memchr(pchSrc, '\n', cbInBuffer);
+ const char *pchTerm = (const char *)memchr(pchSrc, '\0', cbInBuffer);
+ size_t cbCopy;
+ size_t cbAdvance;
+ bool fStop = pchNewline || pchTerm;
+ if (!fStop)
+ cbAdvance = cbCopy = cbInBuffer;
+ else if (!pchTerm || (pchNewline && pchTerm && (uintptr_t)pchNewline < (uintptr_t)pchTerm))
+ {
+ cbCopy = (size_t)(pchNewline - pchSrc);
+ cbAdvance = cbCopy + 1;
+ if (cbCopy && pchNewline[-1] == '\r')
+ cbCopy--;
+ else if (cbCopy == 0 && (uintptr_t)pszString > (uintptr_t)pszStringStart && pszString[-1] == '\r')
+ pszString--, cbString++; /* drop trailing '\r' that it turns out was followed by '\n' */
+ }
+ else
+ {
+ cbCopy = (size_t)(pchTerm - pchSrc);
+ cbAdvance = cbCopy + 1;
+ }
+
+ /* Adjust for available space in the destination buffer, copy over the string
+ characters and advance the buffer position (even on overflow). */
+ if (cbCopy <= cbString)
+ pStream->offBufFirst += cbAdvance;
+ else
+ {
+ rc = VERR_BUFFER_OVERFLOW;
+ fStop = true;
+ cbCopy = cbString;
+ pStream->offBufFirst += cbString;
+ }
+
+ memcpy(pszString, pchSrc, cbCopy);
+ pszString += cbCopy;
+ cbString -= cbCopy;
+
+ if (fStop)
+ break;
+
+#else /* !RTSTREAM_STANDALONE */
+# ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
+ int ch = fgetc_unlocked(pStream->pFile);
+# else
+ int ch = fgetc(pStream->pFile);
+# endif
+
+ /* Deal with \r\n sequences here. We'll return lone CR, but
+ treat CRLF as LF. */
+ if (ch == '\r')
+ {
+# ifdef HAVE_FWRITE_UNLOCKED /** @todo darwin + freebsd(?) has fgetc_unlocked but not fwrite_unlocked, optimize... */
+ ch = fgetc_unlocked(pStream->pFile);
+# else
+ ch = fgetc(pStream->pFile);
+# endif
+ if (ch == '\n')
+ break;
+
+ *pszString++ = '\r';
+ if (--cbString <= 0)
+ {
+ /* yeah, this is an error, we dropped a character. */
+ rc = VERR_BUFFER_OVERFLOW;
+ break;
+ }
+ }
+
+ /* Deal with end of file. */
+ if (ch == EOF)
+ {
+# ifdef HAVE_FWRITE_UNLOCKED
+ if (feof_unlocked(pStream->pFile))
+# else
+ if (feof(pStream->pFile))
+# endif
+ {
+ rc = VERR_EOF;
+ break;
+ }
+# ifdef HAVE_FWRITE_UNLOCKED
+ if (ferror_unlocked(pStream->pFile))
+# else
+ if (ferror(pStream->pFile))
+# endif
+ rc = VERR_READ_ERROR;
+ else
+ {
+ AssertMsgFailed(("This shouldn't happen\n"));
+ rc = VERR_INTERNAL_ERROR;
+ }
+ break;
+ }
+
+ /* Deal with null terminator and (lone) new line. */
+ if (ch == '\0' || ch == '\n')
+ break;
+
+ /* No special character, append it to the return string. */
+ *pszString++ = ch;
+ if (--cbString <= 0)
+ {
+ rc = VINF_BUFFER_OVERFLOW;
+ break;
+ }
+#endif /* !RTSTREAM_STANDALONE */
+ }
+
+ *pszString = '\0';
+ if (RT_FAILURE(rc))
+ ASMAtomicWriteS32(&pStream->i32Error, rc);
+ }
+
+ rtStrmUnlock(pStream);
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream)
+{
+ AssertPtrReturn(pStream, VERR_INVALID_HANDLE);
+ AssertReturn(pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSTREAM_STANDALONE
+ rtStrmLock(pStream);
+ int rc = rtStrmBufFlushWriteMaybe(pStream, true /*fInvalidate*/);
+ rtStrmUnlock(pStream);
+ return rc;
+
+#else
+ if (!fflush(pStream->pFile))
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(errno);
+#endif
+}
+
+
+/**
+ * Output callback.
+ *
+ * @returns number of bytes written.
+ * @param pvArg User argument.
+ * @param pachChars Pointer to an array of utf-8 characters.
+ * @param cchChars Number of bytes in the character array pointed to by pachChars.
+ */
+static DECLCALLBACK(size_t) rtstrmOutput(void *pvArg, const char *pachChars, size_t cchChars)
+{
+ if (cchChars)
+ rtStrmWriteLocked((PRTSTREAM)pvArg, pachChars, cchChars, NULL, true /*fSureIsText*/);
+ /* else: ignore termination call. */
+ return cchChars;
+}
+
+
+RTR3DECL(int) RTStrmPrintfV(PRTSTREAM pStream, const char *pszFormat, va_list args)
+{
+ AssertReturn(RT_VALID_PTR(pStream) && pStream->u32Magic == RTSTREAM_MAGIC, VERR_INVALID_PARAMETER);
+ int rc = pStream->i32Error;
+ if (RT_SUCCESS(rc))
+ {
+ rtStrmLock(pStream);
+// pStream->fShouldFlush = true;
+ rc = (int)RTStrFormatV(rtstrmOutput, pStream, NULL, NULL, pszFormat, args);
+ rtStrmUnlock(pStream);
+ Assert(rc >= 0);
+ }
+ else
+ rc = -1;
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrmPrintf(PRTSTREAM pStream, const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ int rc = RTStrmPrintfV(pStream, pszFormat, args);
+ va_end(args);
+ return rc;
+}
+
+
+RTDECL(void) RTStrmDumpPrintfV(void *pvUser, const char *pszFormat, va_list va)
+{
+ RTStrmPrintfV(pvUser ? (PRTSTREAM)pvUser : g_pStdOut, pszFormat, va);
+}
+
+
+RTR3DECL(int) RTPrintfV(const char *pszFormat, va_list args)
+{
+ return RTStrmPrintfV(g_pStdOut, pszFormat, args);
+}
+
+
+RTR3DECL(int) RTPrintf(const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ int rc = RTStrmPrintfV(g_pStdOut, pszFormat, args);
+ va_end(args);
+ return rc;
+}
+
+
+/**
+ * Outputs @a cchIndent spaces.
+ */
+static void rtStrmWrapppedIndent(RTSTRMWRAPPEDSTATE *pState, uint32_t cchIndent)
+{
+ static const char s_szSpaces[] = " ";
+ while (cchIndent)
+ {
+ uint32_t cchToWrite = RT_MIN(cchIndent, sizeof(s_szSpaces) - 1);
+ int rc = RTStrmWrite(pState->pStream, s_szSpaces, cchToWrite);
+ if (RT_SUCCESS(rc))
+ cchIndent -= cchToWrite;
+ else
+ {
+ pState->rcStatus = rc;
+ break;
+ }
+ }
+}
+
+
+/**
+ * Flushes the current line.
+ *
+ * @param pState The wrapped output state.
+ * @param fPartial Set if partial flush due to buffer overflow, clear when
+ * flushing due to '\n'.
+ */
+static void rtStrmWrappedFlushLine(RTSTRMWRAPPEDSTATE *pState, bool fPartial)
+{
+ /*
+ * Check indentation in case we need to split the line later.
+ */
+ uint32_t cchIndent = pState->cchIndent;
+ if (cchIndent == UINT32_MAX)
+ {
+ pState->cchIndent = 0;
+ cchIndent = pState->cchHangingIndent;
+ while (RT_C_IS_BLANK(pState->szLine[cchIndent]))
+ cchIndent++;
+ }
+
+ /*
+ * Do the flushing.
+ */
+ uint32_t cchLine = pState->cchLine;
+ Assert(cchLine < sizeof(pState->szLine));
+ while (cchLine >= pState->cchWidth || !fPartial)
+ {
+ /*
+ * Hopefully we don't need to do any wrapping ...
+ */
+ uint32_t offSplit;
+ if (pState->cchIndent + cchLine <= pState->cchWidth)
+ {
+ if (!fPartial)
+ {
+ rtStrmWrapppedIndent(pState, pState->cchIndent);
+ pState->szLine[cchLine] = '\n';
+ int rc = RTStrmWrite(pState->pStream, pState->szLine, cchLine + 1);
+ if (RT_FAILURE(rc))
+ pState->rcStatus = rc;
+ pState->cLines += 1;
+ pState->cchLine = 0;
+ pState->cchIndent = UINT32_MAX;
+ return;
+ }
+
+ /*
+ * ... no such luck.
+ */
+ offSplit = cchLine;
+ }
+ else
+ offSplit = pState->cchWidth - pState->cchIndent;
+
+ /* Find the start of the current word: */
+ while (offSplit > 0 && !RT_C_IS_BLANK(pState->szLine[offSplit - 1]))
+ offSplit--;
+
+ /* Skip spaces. */
+ while (offSplit > 0 && RT_C_IS_BLANK(pState->szLine[offSplit - 1]))
+ offSplit--;
+ uint32_t offNextLine = offSplit;
+
+ /* If the first word + indent is wider than the screen width, so just output it in full. */
+ if (offSplit == 0) /** @todo Split words, look for hyphen... This code is currently a bit crude. */
+ {
+ while (offSplit < cchLine && !RT_C_IS_BLANK(pState->szLine[offSplit]))
+ offSplit++;
+ offNextLine = offSplit;
+ }
+
+ while (offNextLine < cchLine && RT_C_IS_BLANK(pState->szLine[offNextLine]))
+ offNextLine++;
+
+ /*
+ * Output and advance.
+ */
+ rtStrmWrapppedIndent(pState, pState->cchIndent);
+ int rc = RTStrmWrite(pState->pStream, pState->szLine, offSplit);
+ if (RT_SUCCESS(rc))
+ rc = RTStrmPutCh(pState->pStream, '\n');
+ if (RT_FAILURE(rc))
+ pState->rcStatus = rc;
+
+ cchLine -= offNextLine;
+ pState->cchLine = cchLine;
+ pState->cLines += 1;
+ pState->cchIndent = cchIndent;
+ memmove(&pState->szLine[0], &pState->szLine[offNextLine], cchLine);
+ }
+
+ /* The indentation level is reset for each '\n' we process, so only save cchIndent if partial. */
+ pState->cchIndent = fPartial ? cchIndent : UINT32_MAX;
+}
+
+
+/**
+ * @callback_method_impl{FNRTSTROUTPUT}
+ */
+static DECLCALLBACK(size_t) rtStrmWrappedOutput(void *pvArg, const char *pachChars, size_t cbChars)
+{
+ RTSTRMWRAPPEDSTATE *pState = (RTSTRMWRAPPEDSTATE *)pvArg;
+ size_t const cchRet = cbChars;
+ while (cbChars > 0)
+ {
+ if (*pachChars == '\n')
+ {
+ rtStrmWrappedFlushLine(pState, false /*fPartial*/);
+ pachChars++;
+ cbChars--;
+ }
+ else
+ {
+ const char *pszEol = (const char *)memchr(pachChars, '\n', cbChars);
+ size_t cchToCopy = pszEol ? (size_t)(pszEol - pachChars) : cbChars;
+ uint32_t cchLine = pState->cchLine;
+ Assert(cchLine < sizeof(pState->szLine));
+ bool const fFlush = cchLine + cchToCopy >= sizeof(pState->szLine);
+ if (fFlush)
+ cchToCopy = cchToCopy - sizeof(pState->szLine) - 1;
+
+ pState->cchLine = cchLine + (uint32_t)cchToCopy;
+ memcpy(&pState->szLine[cchLine], pachChars, cchToCopy);
+
+ pachChars += cchToCopy;
+ cbChars -= cchToCopy;
+
+ if (fFlush)
+ rtStrmWrappedFlushLine(pState, true /*fPartial*/);
+ }
+ }
+ return cchRet;
+}
+
+
+RTDECL(int32_t) RTStrmWrappedPrintfV(PRTSTREAM pStream, uint32_t fFlags, const char *pszFormat, va_list va)
+{
+ /*
+ * Figure the output width and set up the rest of the output state.
+ */
+ RTSTRMWRAPPEDSTATE State;
+ State.pStream = pStream;
+ State.cchLine = fFlags & RTSTRMWRAPPED_F_LINE_OFFSET_MASK;
+ State.cLines = 0;
+ State.rcStatus = VINF_SUCCESS;
+ State.cchIndent = UINT32_MAX;
+ State.cchHangingIndent = 0;
+ if (fFlags & RTSTRMWRAPPED_F_HANGING_INDENT)
+ {
+ State.cchHangingIndent = (fFlags & RTSTRMWRAPPED_F_HANGING_INDENT_MASK) >> RTSTRMWRAPPED_F_HANGING_INDENT_SHIFT;
+ if (!State.cchHangingIndent)
+ State.cchHangingIndent = 4;
+ }
+
+ int rc = RTStrmQueryTerminalWidth(pStream, &State.cchWidth);
+ if (RT_SUCCESS(rc))
+ State.cchWidth = RT_MIN(State.cchWidth, RTSTRMWRAPPED_F_LINE_OFFSET_MASK + 1);
+ else
+ {
+ State.cchWidth = (uint32_t)fFlags & RTSTRMWRAPPED_F_NON_TERMINAL_WIDTH_MASK;
+ if (!State.cchWidth)
+ State.cchWidth = 80;
+ }
+ if (State.cchWidth < 32)
+ State.cchWidth = 32;
+ //State.cchWidth -= 1; /* necessary here? */
+
+ /*
+ * Do the formatting.
+ */
+ RTStrFormatV(rtStrmWrappedOutput, &State, NULL, NULL, pszFormat, va);
+
+ /*
+ * Returning is simple if the buffer is empty. Otherwise we'll have to
+ * perform a partial flush and write out whatever is left ourselves.
+ */
+ if (RT_SUCCESS(State.rcStatus))
+ {
+ if (State.cchLine == 0)
+ return State.cLines << 16;
+
+ rtStrmWrappedFlushLine(&State, true /*fPartial*/);
+ if (RT_SUCCESS(State.rcStatus) && State.cchLine > 0)
+ {
+ rtStrmWrapppedIndent(&State, State.cchIndent);
+ State.rcStatus = RTStrmWrite(State.pStream, State.szLine, State.cchLine);
+ }
+ if (RT_SUCCESS(State.rcStatus))
+ return RT_MIN(State.cchIndent + State.cchLine, RTSTRMWRAPPED_F_LINE_OFFSET_MASK) | (State.cLines << 16);
+ }
+ return State.rcStatus;
+}
+
+
+RTDECL(int32_t) RTStrmWrappedPrintf(PRTSTREAM pStream, uint32_t fFlags, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int32_t rcRet = RTStrmWrappedPrintfV(pStream, fFlags, pszFormat, va);
+ va_end(va);
+ return rcRet;
+}
+
diff --git a/src/VBox/Runtime/r3/tcp.cpp b/src/VBox/Runtime/r3/tcp.cpp
new file mode 100644
index 00000000..8e9988a6
--- /dev/null
+++ b/src/VBox/Runtime/r3/tcp.cpp
@@ -0,0 +1,1082 @@
+/* $Id: tcp.cpp $ */
+/** @file
+ * IPRT - TCP/IP.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/winsock2.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <errno.h>
+# include <netinet/in.h>
+# include <netinet/tcp.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# ifdef FIX_FOR_3_2
+# include <fcntl.h>
+# endif
+#endif
+#include <limits.h>
+
+#include "internal/iprt.h"
+#include <iprt/tcp.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mempool.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/socket.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "internal/magics.h"
+#include "internal/socket.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* non-standard linux stuff (it seems). */
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+#endif
+#ifndef SHUT_RDWR
+# ifdef SD_BOTH
+# define SHUT_RDWR SD_BOTH
+# else
+# define SHUT_RDWR 2
+# endif
+#endif
+#ifndef SHUT_WR
+# ifdef SD_SEND
+# define SHUT_WR SD_SEND
+# else
+# define SHUT_WR 1
+# endif
+#endif
+
+/* fixup backlevel OSes. */
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+# define socklen_t int
+#endif
+
+/** How many pending connection. */
+#define RTTCP_SERVER_BACKLOG 10
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * TCP Server state.
+ */
+typedef enum RTTCPSERVERSTATE
+{
+ /** Invalid. */
+ RTTCPSERVERSTATE_INVALID = 0,
+ /** Created. */
+ RTTCPSERVERSTATE_CREATED,
+ /** Listener thread is starting up. */
+ RTTCPSERVERSTATE_STARTING,
+ /** Accepting client connections. */
+ RTTCPSERVERSTATE_ACCEPTING,
+ /** Serving a client. */
+ RTTCPSERVERSTATE_SERVING,
+ /** Listener terminating. */
+ RTTCPSERVERSTATE_STOPPING,
+ /** Listener terminated. */
+ RTTCPSERVERSTATE_STOPPED,
+ /** Listener cleans up. */
+ RTTCPSERVERSTATE_DESTROYING
+} RTTCPSERVERSTATE;
+
+/*
+ * Internal representation of the TCP Server handle.
+ */
+typedef struct RTTCPSERVER
+{
+ /** The magic value (RTTCPSERVER_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The server state. */
+ RTTCPSERVERSTATE volatile enmState;
+ /** The server thread. */
+ RTTHREAD Thread;
+ /** The server socket. */
+ RTSOCKET volatile hServerSocket;
+ /** The socket to the client currently being serviced.
+ * This is NIL_RTSOCKET when no client is serviced. */
+ RTSOCKET volatile hClientSocket;
+ /** The connection function. */
+ PFNRTTCPSERVE pfnServe;
+ /** Argument to pfnServer. */
+ void *pvUser;
+} RTTCPSERVER;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) rtTcpServerThread(RTTHREAD ThreadSelf, void *pvServer);
+static int rtTcpServerListen(PRTTCPSERVER pServer);
+static int rtTcpServerListenCleanup(PRTTCPSERVER pServer);
+static int rtTcpClose(RTSOCKET Sock, const char *pszMsg, bool fTryGracefulShutdown);
+
+
+/**
+ * Atomicly updates a socket variable.
+ * @returns The old handle value.
+ * @param phSock The socket handle variable to update.
+ * @param hNew The new socket handle value.
+ */
+DECLINLINE(RTSOCKET) rtTcpAtomicXchgSock(RTSOCKET volatile *phSock, const RTSOCKET hNew)
+{
+ RTSOCKET hRet;
+ ASMAtomicXchgHandle(phSock, hNew, &hRet);
+ return hRet;
+}
+
+
+/**
+ * Tries to change the TCP server state.
+ */
+DECLINLINE(bool) rtTcpServerTrySetState(PRTTCPSERVER pServer, RTTCPSERVERSTATE enmStateNew, RTTCPSERVERSTATE enmStateOld)
+{
+ bool fRc;
+ ASMAtomicCmpXchgSize(&pServer->enmState, enmStateNew, enmStateOld, fRc);
+ return fRc;
+}
+
+/**
+ * Changes the TCP server state.
+ */
+DECLINLINE(void) rtTcpServerSetState(PRTTCPSERVER pServer, RTTCPSERVERSTATE enmStateNew, RTTCPSERVERSTATE enmStateOld)
+{
+ bool fRc;
+ ASMAtomicCmpXchgSize(&pServer->enmState, enmStateNew, enmStateOld, fRc);
+ Assert(fRc); NOREF(fRc);
+}
+
+
+/**
+ * Closes the a socket (client or server).
+ *
+ * @returns IPRT status code.
+ */
+static int rtTcpServerDestroySocket(RTSOCKET volatile *pSock, const char *pszMsg, bool fTryGracefulShutdown)
+{
+ RTSOCKET hSocket = rtTcpAtomicXchgSock(pSock, NIL_RTSOCKET);
+ if (hSocket != NIL_RTSOCKET)
+ {
+ if (!fTryGracefulShutdown)
+ RTSocketShutdown(hSocket, true /*fRead*/, true /*fWrite*/);
+ return rtTcpClose(hSocket, pszMsg, fTryGracefulShutdown);
+ }
+ return VINF_TCP_SERVER_NO_CLIENT;
+}
+
+
+RTR3DECL(int) RTTcpServerCreate(const char *pszAddress, unsigned uPort, RTTHREADTYPE enmType, const char *pszThrdName,
+ PFNRTTCPSERVE pfnServe, void *pvUser, PPRTTCPSERVER ppServer)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(uPort > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfnServe, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszThrdName, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppServer, VERR_INVALID_POINTER);
+
+ /*
+ * Create the server.
+ */
+ PRTTCPSERVER pServer;
+ int rc = RTTcpServerCreateEx(pszAddress, uPort, &pServer);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the listener thread.
+ */
+ RTMemPoolRetain(pServer);
+ pServer->enmState = RTTCPSERVERSTATE_STARTING;
+ pServer->pvUser = pvUser;
+ pServer->pfnServe = pfnServe;
+ rc = RTThreadCreate(&pServer->Thread, rtTcpServerThread, pServer, 0, enmType, /*RTTHREADFLAGS_WAITABLE*/0, pszThrdName);
+ if (RT_SUCCESS(rc))
+ {
+ /* done */
+ if (ppServer)
+ *ppServer = pServer;
+ else
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return rc;
+ }
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+
+ /*
+ * Destroy the server.
+ */
+ rtTcpServerSetState(pServer, RTTCPSERVERSTATE_CREATED, RTTCPSERVERSTATE_STARTING);
+ RTTcpServerDestroy(pServer);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Server thread, loops accepting connections until it's terminated.
+ *
+ * @returns iprt status code. (ignored).
+ * @param ThreadSelf Thread handle.
+ * @param pvServer Server handle.
+ */
+static DECLCALLBACK(int) rtTcpServerThread(RTTHREAD ThreadSelf, void *pvServer)
+{
+ PRTTCPSERVER pServer = (PRTTCPSERVER)pvServer;
+ int rc;
+ if (rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_ACCEPTING, RTTCPSERVERSTATE_STARTING))
+ rc = rtTcpServerListen(pServer);
+ else
+ rc = rtTcpServerListenCleanup(pServer);
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ NOREF(ThreadSelf);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTcpServerCreateEx(const char *pszAddress, uint32_t uPort, PPRTTCPSERVER ppServer)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(uPort > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppServer, VERR_INVALID_PARAMETER);
+
+ /*
+ * Resolve the address.
+ */
+ RTNETADDR LocalAddr;
+ int rc = RTSocketParseInetAddress(pszAddress, uPort, &LocalAddr);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Setting up socket.
+ */
+ RTSOCKET WaitSock;
+ rc = rtSocketCreate(&WaitSock, AF_INET, SOCK_STREAM, IPPROTO_TCP, false /*fInheritable*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set socket options.
+ */
+ int fFlag = 1;
+ if (!rtSocketSetOpt(WaitSock, SOL_SOCKET, SO_REUSEADDR, &fFlag, sizeof(fFlag)))
+ {
+ /*
+ * Bind a name to a socket and set it listening for connections.
+ */
+ rc = rtSocketBind(WaitSock, &LocalAddr);
+ if (RT_SUCCESS(rc))
+ rc = rtSocketListen(WaitSock, RTTCP_SERVER_BACKLOG);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the server handle.
+ */
+ PRTTCPSERVER pServer = (PRTTCPSERVER)RTMemPoolAlloc(RTMEMPOOL_DEFAULT, sizeof(*pServer));
+ if (pServer)
+ {
+ pServer->u32Magic = RTTCPSERVER_MAGIC;
+ pServer->enmState = RTTCPSERVERSTATE_CREATED;
+ pServer->Thread = NIL_RTTHREAD;
+ pServer->hServerSocket = WaitSock;
+ pServer->hClientSocket = NIL_RTSOCKET;
+ pServer->pfnServe = NULL;
+ pServer->pvUser = NULL;
+ *ppServer = pServer;
+ return VINF_SUCCESS;
+ }
+
+ /* bail out */
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ AssertMsgFailed(("rtSocketSetOpt: %Rrc\n", rc));
+ rtTcpClose(WaitSock, "RTServerCreateEx", false /*fTryGracefulShutdown*/);
+ }
+
+ return rc;
+}
+
+
+RTR3DECL(int) RTTcpServerListen(PRTTCPSERVER pServer, PFNRTTCPSERVE pfnServe, void *pvUser)
+{
+ /*
+ * Validate input and retain the instance.
+ */
+ AssertPtrReturn(pfnServe, VERR_INVALID_POINTER);
+ AssertPtrReturn(pServer, VERR_INVALID_HANDLE);
+ AssertReturn(pServer->u32Magic == RTTCPSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRetain(pServer) != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ int rc = VERR_INVALID_STATE;
+ if (rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_ACCEPTING, RTTCPSERVERSTATE_CREATED))
+ {
+ Assert(!pServer->pfnServe);
+ Assert(!pServer->pvUser);
+ Assert(pServer->Thread == NIL_RTTHREAD);
+ Assert(pServer->hClientSocket == NIL_RTSOCKET);
+
+ pServer->pfnServe = pfnServe;
+ pServer->pvUser = pvUser;
+ pServer->Thread = RTThreadSelf();
+ Assert(pServer->Thread != NIL_RTTHREAD);
+ rc = rtTcpServerListen(pServer);
+ }
+ else
+ {
+ AssertMsgFailed(("enmState=%d\n", pServer->enmState));
+ rc = VERR_INVALID_STATE;
+ }
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return rc;
+}
+
+
+/**
+ * Internal worker common for RTTcpServerListen and the thread created by
+ * RTTcpServerCreate().
+ *
+ * The caller makes sure it has its own memory reference and releases it upon
+ * return.
+ */
+static int rtTcpServerListen(PRTTCPSERVER pServer)
+{
+ /*
+ * Accept connection loop.
+ */
+ for (;;)
+ {
+ /*
+ * Change state, getting an extra reference to the socket so we can
+ * allow others to close it while we're stuck in rtSocketAccept.
+ */
+ RTTCPSERVERSTATE enmState = pServer->enmState;
+ RTSOCKET hServerSocket;
+ ASMAtomicXchgHandle(&pServer->hServerSocket, NIL_RTSOCKET, &hServerSocket);
+ if (hServerSocket != NIL_RTSOCKET)
+ {
+ RTSocketRetain(hServerSocket);
+ ASMAtomicWriteHandle(&pServer->hServerSocket, hServerSocket);
+ }
+ if ( enmState != RTTCPSERVERSTATE_ACCEPTING
+ && enmState != RTTCPSERVERSTATE_SERVING)
+ {
+ RTSocketRelease(hServerSocket);
+ return rtTcpServerListenCleanup(pServer);
+ }
+ if (!rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_ACCEPTING, enmState))
+ {
+ RTSocketRelease(hServerSocket);
+ continue;
+ }
+
+ /*
+ * Accept connection.
+ */
+ struct sockaddr_in RemoteAddr;
+ size_t cbRemoteAddr = sizeof(RemoteAddr);
+ RTSOCKET hClientSocket;
+ RT_ZERO(RemoteAddr);
+ int rc = rtSocketAccept(hServerSocket, &hClientSocket, (struct sockaddr *)&RemoteAddr, &cbRemoteAddr);
+ RTSocketRelease(hServerSocket);
+ if (RT_FAILURE(rc))
+ {
+ /* These are typical for what can happen during destruction. */
+ if ( rc == VERR_INVALID_HANDLE
+ || rc == VERR_INVALID_PARAMETER
+ || rc == VERR_NET_NOT_SOCKET)
+ return rtTcpServerListenCleanup(pServer);
+ continue;
+ }
+ RTSocketSetInheritance(hClientSocket, false /*fInheritable*/);
+
+ /*
+ * Run a pfnServe callback.
+ */
+ if (!rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_SERVING, RTTCPSERVERSTATE_ACCEPTING))
+ {
+ rtTcpClose(hClientSocket, "rtTcpServerListen", true /*fTryGracefulShutdown*/);
+ return rtTcpServerListenCleanup(pServer);
+ }
+ RTSocketRetain(hClientSocket);
+ rtTcpAtomicXchgSock(&pServer->hClientSocket, hClientSocket);
+ rc = pServer->pfnServe(hClientSocket, pServer->pvUser);
+ rtTcpServerDestroySocket(&pServer->hClientSocket, "Listener: client (secondary)", true /*fTryGracefulShutdown*/);
+ RTSocketRelease(hClientSocket);
+
+ /*
+ * Stop the server?
+ */
+ if (rc == VERR_TCP_SERVER_STOP)
+ {
+ if (rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_STOPPING, RTTCPSERVERSTATE_SERVING))
+ {
+ /*
+ * Reset the server socket and change the state to stopped. After that state change
+ * we cannot safely access the handle so we'll have to return here.
+ */
+ hServerSocket = rtTcpAtomicXchgSock(&pServer->hServerSocket, NIL_RTSOCKET);
+ rtTcpServerSetState(pServer, RTTCPSERVERSTATE_STOPPED, RTTCPSERVERSTATE_STOPPING);
+ rtTcpClose(hServerSocket, "Listener: server stopped", false /*fTryGracefulShutdown*/);
+ }
+ else
+ rtTcpServerListenCleanup(pServer); /* ignore rc */
+ return rc;
+ }
+ }
+}
+
+
+/**
+ * Clean up after listener.
+ */
+static int rtTcpServerListenCleanup(PRTTCPSERVER pServer)
+{
+ /*
+ * Close the server socket, the client one shouldn't be set.
+ */
+ rtTcpServerDestroySocket(&pServer->hServerSocket, "ListenCleanup", false /*fTryGracefulShutdown*/);
+ Assert(pServer->hClientSocket == NIL_RTSOCKET);
+
+ /*
+ * Figure the return code and make sure the state is OK.
+ */
+ RTTCPSERVERSTATE enmState = pServer->enmState;
+ switch (enmState)
+ {
+ case RTTCPSERVERSTATE_STOPPING:
+ case RTTCPSERVERSTATE_STOPPED:
+ return VERR_TCP_SERVER_SHUTDOWN;
+
+ case RTTCPSERVERSTATE_ACCEPTING:
+ rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_STOPPED, enmState);
+ return VERR_TCP_SERVER_DESTROYED;
+
+ case RTTCPSERVERSTATE_DESTROYING:
+ return VERR_TCP_SERVER_DESTROYED;
+
+ case RTTCPSERVERSTATE_STARTING:
+ case RTTCPSERVERSTATE_SERVING:
+ default:
+ AssertMsgFailedReturn(("pServer=%p enmState=%d\n", pServer, enmState), VERR_INTERNAL_ERROR_4);
+ }
+}
+
+
+RTR3DECL(int) RTTcpServerListen2(PRTTCPSERVER pServer, PRTSOCKET phClientSocket)
+{
+ /*
+ * Validate input and retain the instance.
+ */
+ AssertPtrReturn(phClientSocket, VERR_INVALID_HANDLE);
+ *phClientSocket = NIL_RTSOCKET;
+ AssertReturn(pServer->u32Magic == RTTCPSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRetain(pServer) != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ int rc = VERR_INVALID_STATE;
+ for (;;)
+ {
+ /*
+ * Change state, getting an extra reference to the socket so we can
+ * allow others to close it while we're stuck in rtSocketAccept.
+ */
+ RTTCPSERVERSTATE enmState = pServer->enmState;
+ RTSOCKET hServerSocket;
+ ASMAtomicXchgHandle(&pServer->hServerSocket, NIL_RTSOCKET, &hServerSocket);
+ if (hServerSocket != NIL_RTSOCKET)
+ {
+ RTSocketRetain(hServerSocket);
+ ASMAtomicWriteHandle(&pServer->hServerSocket, hServerSocket);
+ }
+ if ( enmState != RTTCPSERVERSTATE_SERVING
+ && enmState != RTTCPSERVERSTATE_CREATED)
+ {
+ RTSocketRelease(hServerSocket);
+ return rtTcpServerListenCleanup(pServer);
+ }
+ if (!rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_ACCEPTING, enmState))
+ {
+ RTSocketRelease(hServerSocket);
+ continue;
+ }
+ Assert(!pServer->pfnServe);
+ Assert(!pServer->pvUser);
+ Assert(pServer->Thread == NIL_RTTHREAD);
+ Assert(pServer->hClientSocket == NIL_RTSOCKET);
+
+ /*
+ * Accept connection.
+ */
+ struct sockaddr_in RemoteAddr;
+ size_t cbRemoteAddr = sizeof(RemoteAddr);
+ RTSOCKET hClientSocket;
+ RT_ZERO(RemoteAddr);
+ rc = rtSocketAccept(hServerSocket, &hClientSocket, (struct sockaddr *)&RemoteAddr, &cbRemoteAddr);
+ RTSocketRelease(hServerSocket);
+ if (RT_FAILURE(rc))
+ {
+ if (!rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_CREATED, RTTCPSERVERSTATE_ACCEPTING))
+ rc = rtTcpServerListenCleanup(pServer);
+ if (RT_FAILURE(rc))
+ break;
+ continue;
+ }
+ RTSocketSetInheritance(hClientSocket, false /*fInheritable*/);
+
+ /*
+ * Chance to the 'serving' state and return the socket.
+ */
+ if (rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_SERVING, RTTCPSERVERSTATE_ACCEPTING))
+ {
+ *phClientSocket = hClientSocket;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rtTcpClose(hClientSocket, "RTTcpServerListen2", true /*fTryGracefulShutdown*/);
+ rc = rtTcpServerListenCleanup(pServer);
+ }
+ break;
+ }
+
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return rc;
+}
+
+
+RTR3DECL(int) RTTcpServerDisconnectClient(PRTTCPSERVER pServer)
+{
+ /*
+ * Validate input and retain the instance.
+ */
+ AssertPtrReturn(pServer, VERR_INVALID_HANDLE);
+ AssertReturn(pServer->u32Magic == RTTCPSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRetain(pServer) != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ int rc = rtTcpServerDestroySocket(&pServer->hClientSocket, "DisconnectClient: client", true /*fTryGracefulShutdown*/);
+
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return rc;
+}
+
+
+RTR3DECL(int) RTTcpServerDisconnectClient2(RTSOCKET hClientSocket)
+{
+ return rtTcpClose(hClientSocket, "RTTcpServerDisconnectClient2", true /*fTryGracefulShutdown*/);
+}
+
+
+RTR3DECL(int) RTTcpServerShutdown(PRTTCPSERVER pServer)
+{
+ /*
+ * Validate input and retain the instance.
+ */
+ AssertPtrReturn(pServer, VERR_INVALID_HANDLE);
+ AssertReturn(pServer->u32Magic == RTTCPSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRetain(pServer) != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ /*
+ * Try change the state to stopping, then replace and destroy the server socket.
+ */
+ for (;;)
+ {
+ RTTCPSERVERSTATE enmState = pServer->enmState;
+ if ( enmState != RTTCPSERVERSTATE_ACCEPTING
+ && enmState != RTTCPSERVERSTATE_SERVING)
+ {
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ switch (enmState)
+ {
+ case RTTCPSERVERSTATE_CREATED:
+ case RTTCPSERVERSTATE_STARTING:
+ default:
+ AssertMsgFailed(("%d\n", enmState));
+ return VERR_INVALID_STATE;
+
+ case RTTCPSERVERSTATE_STOPPING:
+ case RTTCPSERVERSTATE_STOPPED:
+ return VINF_SUCCESS;
+
+ case RTTCPSERVERSTATE_DESTROYING:
+ return VERR_TCP_SERVER_DESTROYED;
+ }
+ }
+ if (rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_STOPPING, enmState))
+ {
+ rtTcpServerDestroySocket(&pServer->hServerSocket, "RTTcpServerShutdown", false /*fTryGracefulShutdown*/);
+ rtTcpServerSetState(pServer, RTTCPSERVERSTATE_STOPPED, RTTCPSERVERSTATE_STOPPING);
+
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return VINF_SUCCESS;
+ }
+ }
+}
+
+
+RTR3DECL(int) RTTcpServerDestroy(PRTTCPSERVER pServer)
+{
+ /*
+ * Validate input and retain the instance.
+ */
+ AssertPtrReturn(pServer, VERR_INVALID_HANDLE);
+ AssertReturn(pServer->u32Magic == RTTCPSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRetain(pServer) != UINT32_MAX, VERR_INVALID_HANDLE); /* paranoia */
+
+ /*
+ * Move the state along so the listener can figure out what's going on.
+ */
+ for (;;)
+ {
+ bool fDestroyable;
+ RTTCPSERVERSTATE enmState = pServer->enmState;
+ switch (enmState)
+ {
+ case RTTCPSERVERSTATE_STARTING:
+ case RTTCPSERVERSTATE_ACCEPTING:
+ case RTTCPSERVERSTATE_SERVING:
+ case RTTCPSERVERSTATE_CREATED:
+ case RTTCPSERVERSTATE_STOPPED:
+ fDestroyable = rtTcpServerTrySetState(pServer, RTTCPSERVERSTATE_DESTROYING, enmState);
+ break;
+
+ /* destroyable states */
+ case RTTCPSERVERSTATE_STOPPING:
+ fDestroyable = true;
+ break;
+
+ /*
+ * Everything else means user or internal misbehavior.
+ */
+ default:
+ AssertMsgFailed(("pServer=%p enmState=%d\n", pServer, enmState));
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return VERR_INTERNAL_ERROR;
+ }
+ if (fDestroyable)
+ break;
+ }
+
+ /*
+ * Destroy it.
+ */
+ ASMAtomicWriteU32(&pServer->u32Magic, ~RTTCPSERVER_MAGIC);
+ rtTcpServerDestroySocket(&pServer->hServerSocket, "Destroyer: server", false /*fTryGracefulShutdown*/);
+ rtTcpServerDestroySocket(&pServer->hClientSocket, "Destroyer: client", true /*fTryGracefulShutdown*/);
+
+ /*
+ * Release it.
+ */
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTcpClientConnect(const char *pszAddress, uint32_t uPort, PRTSOCKET pSock)
+{
+ return RTTcpClientConnectEx(pszAddress, uPort, pSock, RT_SOCKETCONNECT_DEFAULT_WAIT, NULL);
+}
+
+
+RTR3DECL(int) RTTcpClientConnectEx(const char *pszAddress, uint32_t uPort, PRTSOCKET pSock,
+ RTMSINTERVAL cMillies, PRTTCPCLIENTCONNECTCANCEL volatile *ppCancelCookie)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(uPort > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(ppCancelCookie, VERR_INVALID_POINTER);
+
+ /*
+ * Resolve the address.
+ */
+ RTNETADDR Addr;
+ int rc = RTSocketParseInetAddress(pszAddress, uPort, &Addr);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Create the socket and connect.
+ */
+ RTSOCKET Sock;
+ rc = rtSocketCreate(&Sock, PF_INET, SOCK_STREAM, 0, false /*fInheritable*/);
+ if (RT_SUCCESS(rc))
+ {
+ if (!ppCancelCookie)
+ rc = rtSocketConnect(Sock, &Addr, cMillies);
+ else
+ {
+ RTSocketRetain(Sock);
+ if (ASMAtomicCmpXchgPtr(ppCancelCookie, (PRTTCPCLIENTCONNECTCANCEL)Sock, NULL))
+ {
+ rc = rtSocketConnect(Sock, &Addr, cMillies);
+ if (ASMAtomicCmpXchgPtr(ppCancelCookie, NULL, (PRTTCPCLIENTCONNECTCANCEL)Sock))
+ RTSocketRelease(Sock);
+ else
+ rc = VERR_CANCELLED;
+ }
+ else
+ {
+ RTSocketRelease(Sock);
+ rc = VERR_CANCELLED;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ *pSock = Sock;
+ return VINF_SUCCESS;
+ }
+
+ rtTcpClose(Sock, "RTTcpClientConnect", false /*fTryGracefulShutdown*/);
+ }
+ if (ppCancelCookie)
+ *ppCancelCookie = NULL;
+ return rc;
+}
+
+
+RTR3DECL(int) RTTcpClientCancelConnect(PRTTCPCLIENTCONNECTCANCEL volatile *ppCancelCookie)
+{
+ AssertPtrReturn(ppCancelCookie, VERR_INVALID_POINTER);
+
+ RTSOCKET const hSockCancelled = (RTSOCKET)(uintptr_t)0xdead9999;
+
+ AssertCompile(NIL_RTSOCKET == NULL);
+ RTSOCKET hSock = (RTSOCKET)ASMAtomicXchgPtr((void * volatile *)ppCancelCookie, hSockCancelled);
+ if (hSock != NIL_RTSOCKET && hSock != hSockCancelled)
+ {
+ int rc = rtTcpClose(hSock, "RTTcpClientCancelConnect", false /*fTryGracefulShutdown*/);
+ AssertRCReturn(rc, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTcpClientClose(RTSOCKET Sock)
+{
+ return rtTcpClose(Sock, "RTTcpClientClose", true /*fTryGracefulShutdown*/);
+}
+
+
+RTR3DECL(int) RTTcpClientCloseEx(RTSOCKET Sock, bool fGracefulShutdown)
+{
+ return rtTcpClose(Sock, "RTTcpClientCloseEx", fGracefulShutdown);
+}
+
+
+#ifdef FIX_FOR_3_2
+/**
+ * Changes the blocking mode of the socket.
+ *
+ * @returns 0 on success, -1 on failure.
+ * @param hSocket The socket to work on.
+ * @param fBlocking The desired mode of operation.
+ */
+static int rtTcpSetBlockingMode(RTHCUINTPTR hSocket, bool fBlocking)
+{
+ int rc = VINF_SUCCESS;
+#ifdef RT_OS_WINDOWS
+ u_long uBlocking = fBlocking ? 0 : 1;
+ if (ioctlsocket(hSocket, FIONBIO, &uBlocking))
+ return -1;
+
+#else
+ int fFlags = fcntl(hSocket, F_GETFL, 0);
+ if (fFlags == -1)
+ return -1;
+
+ if (fBlocking)
+ fFlags &= ~O_NONBLOCK;
+ else
+ fFlags |= O_NONBLOCK;
+ if (fcntl(hSocket, F_SETFL, fFlags) == -1)
+ return -1;
+#endif
+
+ return 0;
+}
+#endif
+
+
+/**
+ * Internal close function which does all the proper bitching.
+ */
+static int rtTcpClose(RTSOCKET Sock, const char *pszMsg, bool fTryGracefulShutdown)
+{
+ NOREF(pszMsg); /** @todo drop this parameter? */
+
+ /* ignore nil handles. */
+ if (Sock == NIL_RTSOCKET)
+ return VINF_SUCCESS;
+
+ /*
+ * Try to gracefully shut it down.
+ */
+ int rc;
+ if (fTryGracefulShutdown)
+ {
+ rc = RTSocketShutdown(Sock, false /*fRead*/, true /*fWrite*/);
+#ifdef FIX_FOR_3_2
+ RTHCUINTPTR hNative = RTSocketToNative(Sock);
+ if (RT_SUCCESS(rc) && rtTcpSetBlockingMode(hNative, false /*fBlocking*/) == 0)
+#else
+ if (RT_SUCCESS(rc))
+#endif
+ {
+
+ size_t cbReceived = 0;
+ uint64_t u64Start = RTTimeMilliTS();
+ while ( cbReceived < _1G
+ && RTTimeMilliTS() - u64Start < 30000)
+ {
+#ifdef FIX_FOR_3_2
+ fd_set FdSetR;
+ FD_ZERO(&FdSetR);
+ FD_SET(hNative, &FdSetR);
+
+ fd_set FdSetE;
+ FD_ZERO(&FdSetE);
+ FD_SET(hNative, &FdSetE);
+
+ struct timeval TvTimeout;
+ TvTimeout.tv_sec = 1;
+ TvTimeout.tv_usec = 0;
+ rc = select(hNative + 1, &FdSetR, NULL, &FdSetE, &TvTimeout);
+ if (rc == 0)
+ continue;
+ if (rc < 0)
+ break;
+ if (FD_ISSET(hNative, &FdSetE))
+ break;
+#else
+ uint32_t fEvents;
+ rc = RTSocketSelectOneEx(Sock, RTSOCKET_EVT_READ | RTSOCKET_EVT_ERROR, &fEvents, 1000);
+ if (rc == VERR_TIMEOUT)
+ continue;
+ if (RT_FAILURE(rc))
+ break;
+ if (fEvents & RTSOCKET_EVT_ERROR)
+ break;
+#endif
+
+ char abBitBucket[16*_1K];
+#ifdef FIX_FOR_3_2
+ ssize_t cbRead = recv(hNative, &abBitBucket[0], sizeof(abBitBucket), MSG_NOSIGNAL);
+ if (cbRead == 0)
+ break; /* orderly shutdown in progress */
+ if (cbRead < 0 && errno != EAGAIN)
+ break; /* some kind of error, never mind which... */
+#else
+ size_t cbRead;
+ rc = RTSocketReadNB(Sock, &abBitBucket[0], sizeof(abBitBucket), &cbRead);
+ if (RT_FAILURE(rc))
+ break; /* some kind of error, never mind which... */
+ if (rc != VINF_TRY_AGAIN && !cbRead)
+ break; /* orderly shutdown in progress */
+#endif
+
+ cbReceived += cbRead;
+ }
+ }
+ }
+
+ /*
+ * Close the socket handle (drops our reference to it).
+ */
+ return RTSocketClose(Sock);
+}
+
+
+RTR3DECL(int) RTTcpCreatePair(PRTSOCKET phServer, PRTSOCKET phClient, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phServer, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phClient, VERR_INVALID_PARAMETER);
+ AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+
+ /*
+ * Do the job.
+ */
+ return rtSocketCreateTcpPair(phServer, phClient);
+}
+
+
+RTR3DECL(int) RTTcpRead(RTSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+ return RTSocketRead(Sock, pvBuffer, cbBuffer, pcbRead);
+}
+
+
+RTR3DECL(int) RTTcpWrite(RTSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
+{
+ return RTSocketWrite(Sock, pvBuffer, cbBuffer);
+}
+
+
+RTR3DECL(int) RTTcpFlush(RTSOCKET Sock)
+{
+ int fFlag = 1;
+ int rc = rtSocketSetOpt(Sock, IPPROTO_TCP, TCP_NODELAY, &fFlag, sizeof(fFlag));
+ if (RT_SUCCESS(rc))
+ {
+ fFlag = 0;
+ rc = rtSocketSetOpt(Sock, IPPROTO_TCP, TCP_NODELAY, &fFlag, sizeof(fFlag));
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTTcpSetSendCoalescing(RTSOCKET Sock, bool fEnable)
+{
+ int fFlag = fEnable ? 0 : 1;
+ return rtSocketSetOpt(Sock, IPPROTO_TCP, TCP_NODELAY, &fFlag, sizeof(fFlag));
+}
+
+
+RTR3DECL(int) RTTcpSetBufferSize(RTSOCKET hSocket, uint32_t cbSize)
+{
+ int cbIntSize = (int)cbSize;
+ AssertReturn(cbIntSize >= 0, VERR_OUT_OF_RANGE);
+ int rc = rtSocketSetOpt(hSocket, SOL_SOCKET, SO_SNDBUF, &cbIntSize, sizeof(cbIntSize));
+ if (RT_SUCCESS(rc))
+ rc = rtSocketSetOpt(hSocket, SOL_SOCKET, SO_RCVBUF, &cbIntSize, sizeof(cbIntSize));
+ return rc;
+}
+
+
+RTR3DECL(int) RTTcpSelectOne(RTSOCKET Sock, RTMSINTERVAL cMillies)
+{
+ return RTSocketSelectOne(Sock, cMillies);
+}
+
+
+RTR3DECL(int) RTTcpSelectOneEx(RTSOCKET Sock, uint32_t fEvents, uint32_t *pfEvents,
+ RTMSINTERVAL cMillies)
+{
+ return RTSocketSelectOneEx(Sock, fEvents, pfEvents, cMillies);
+}
+
+
+RTR3DECL(int) RTTcpGetLocalAddress(RTSOCKET Sock, PRTNETADDR pAddr)
+{
+ return RTSocketGetLocalAddress(Sock, pAddr);
+}
+
+
+RTR3DECL(int) RTTcpGetPeerAddress(RTSOCKET Sock, PRTNETADDR pAddr)
+{
+ return RTSocketGetPeerAddress(Sock, pAddr);
+}
+
+
+RTR3DECL(int) RTTcpSgWrite(RTSOCKET Sock, PCRTSGBUF pSgBuf)
+{
+ return RTSocketSgWrite(Sock, pSgBuf);
+}
+
+
+RTR3DECL(int) RTTcpSgWriteL(RTSOCKET hSocket, size_t cSegs, ...)
+{
+ va_list va;
+ va_start(va, cSegs);
+ int rc = RTSocketSgWriteLV(hSocket, cSegs, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTR3DECL(int) RTTcpSgWriteLV(RTSOCKET hSocket, size_t cSegs, va_list va)
+{
+ return RTSocketSgWriteLV(hSocket, cSegs, va);
+}
+
+
+RTR3DECL(int) RTTcpReadNB(RTSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
+{
+ return RTSocketReadNB(Sock, pvBuffer, cbBuffer, pcbRead);
+}
+
+
+RTR3DECL(int) RTTcpWriteNB(RTSOCKET Sock, const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
+{
+ return RTSocketWriteNB(Sock, pvBuffer, cbBuffer, pcbWritten);
+}
+
+
+RTR3DECL(int) RTTcpSgWriteNB(RTSOCKET Sock, PCRTSGBUF pSgBuf, size_t *pcbWritten)
+{
+ return RTSocketSgWriteNB(Sock, pSgBuf, pcbWritten);
+}
+
+
+RTR3DECL(int) RTTcpSgWriteLNB(RTSOCKET hSocket, size_t cSegs, size_t *pcbWritten, ...)
+{
+ va_list va;
+ va_start(va, pcbWritten);
+ int rc = RTSocketSgWriteLVNB(hSocket, cSegs, pcbWritten, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTR3DECL(int) RTTcpSgWriteLVNB(RTSOCKET hSocket, size_t cSegs, size_t *pcbWritten, va_list va)
+{
+ return RTSocketSgWriteLVNB(hSocket, cSegs, pcbWritten, va);
+}
+
diff --git a/src/VBox/Runtime/r3/test.cpp b/src/VBox/Runtime/r3/test.cpp
new file mode 100644
index 00000000..a0b90870
--- /dev/null
+++ b/src/VBox/Runtime/r3/test.cpp
@@ -0,0 +1,1662 @@
+/* $Id: test.cpp $ */
+/** @file
+ * IPRT - Testcase Framework.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/test.h>
+
+#include <iprt/asm.h>
+#include <iprt/critsect.h>
+#include <iprt/env.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/param.h>
+#include <iprt/pipe.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Guarded memory allocation record.
+ */
+typedef struct RTTESTGUARDEDMEM
+{
+ /** Pointer to the next record. */
+ struct RTTESTGUARDEDMEM *pNext;
+ /** The address we return to the user. */
+ void *pvUser;
+ /** The base address of the allocation. */
+ void *pvAlloc;
+ /** The size of the allocation. */
+ size_t cbAlloc;
+ /** Guards. */
+ struct
+ {
+ /** The guard address. */
+ void *pv;
+ /** The guard size. */
+ size_t cb;
+ } aGuards[2];
+} RTTESTGUARDEDMEM;
+/** Pointer to an guarded memory allocation. */
+typedef RTTESTGUARDEDMEM *PRTTESTGUARDEDMEM;
+
+/**
+ * Test instance structure.
+ */
+typedef struct RTTESTINT
+{
+ /** Magic. */
+ uint32_t u32Magic;
+ /** The number of errors. */
+ volatile uint32_t cErrors;
+ /** The test name. */
+ const char *pszTest;
+ /** The length of the test name. */
+ size_t cchTest;
+ /** The size of a guard. Multiple of PAGE_SIZE. */
+ uint32_t cbGuard;
+ /** The verbosity level. */
+ RTTESTLVL enmMaxLevel;
+ /** The creation flags. */
+ uint32_t fFlags;
+
+
+ /** Critical section serializing output. */
+ RTCRITSECT OutputLock;
+ /** The output stream. */
+ PRTSTREAM pOutStrm;
+ /** Whether we're currently at a newline. */
+ bool fNewLine;
+
+
+ /** Critical section serializing access to the members following it. */
+ RTCRITSECT Lock;
+
+ /** The list of guarded memory allocations. */
+ PRTTESTGUARDEDMEM pGuardedMem;
+
+ /** The current sub-test. */
+ const char *pszSubTest;
+ /** The length of the sub-test name. */
+ size_t cchSubTest;
+ /** Whether the current subtest should figure as 'SKIPPED'. */
+ bool fSubTestSkipped;
+ /** Whether we've reported the sub-test result or not. */
+ bool fSubTestReported;
+ /** The start error count of the current subtest. */
+ uint32_t cSubTestAtErrors;
+
+ /** The number of sub tests. */
+ uint32_t cSubTests;
+ /** The number of sub tests that failed. */
+ uint32_t cSubTestsFailed;
+
+ /** Error context message. */
+ char *pszErrCtx;
+
+ /** Set if XML output is enabled. */
+ bool fXmlEnabled;
+ /** Set if we omit the top level test in the XML report. */
+ bool fXmlOmitTopTest;
+ /** Set if we've reported the top test (for RTTEST_C_XML_DELAY_TOP_TEST). */
+ bool fXmlTopTestDone;
+ enum {
+ kXmlPos_ValueStart,
+ kXmlPos_Value,
+ kXmlPos_ElementEnd
+ } eXmlState;
+ /** Test pipe for the XML output stream going to the server. */
+ RTPIPE hXmlPipe;
+ /** File where the XML output stream might be directed. */
+ RTFILE hXmlFile;
+ /** The number of XML elements on the stack. */
+ size_t cXmlElements;
+ /** XML element stack. */
+ const char *apszXmlElements[10];
+
+ /** Number of times assertions has been disabled and quieted. */
+ uint32_t volatile cAssertionsDisabledAndQuieted;
+ /** Saved RTAssertSetQuiet return code. */
+ bool fAssertSavedQuiet;
+ /** Saved RTAssertSetMayPanic return code. */
+ bool fAssertSavedMayPanic;
+} RTTESTINT;
+/** Pointer to a test instance. */
+typedef RTTESTINT *PRTTESTINT;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Validate a test instance. */
+#define RTTEST_VALID_RETURN(pTest) \
+ do { \
+ AssertPtrReturn(pTest, VERR_INVALID_HANDLE); \
+ AssertReturn(pTest->u32Magic == RTTESTINT_MAGIC, VERR_INVALID_HANDLE); \
+ } while (0)
+
+/** Gets and validates a test instance.
+ * If the handle is nil, we will try retrieve it from the test TLS entry.
+ */
+#define RTTEST_GET_VALID_RETURN(pTest) \
+ do { \
+ if (pTest == NIL_RTTEST) \
+ pTest = (PRTTESTINT)RTTlsGet(g_iTestTls); \
+ AssertPtrReturn(pTest, VERR_INVALID_HANDLE); \
+ AssertReturn(pTest->u32Magic == RTTESTINT_MAGIC, VERR_INVALID_MAGIC); \
+ } while (0)
+
+
+/** Gets and validates a test instance.
+ * If the handle is nil, we will try retrieve it from the test TLS entry.
+ */
+#define RTTEST_GET_VALID_RETURN_RC(pTest, rc) \
+ do { \
+ if (pTest == NIL_RTTEST) \
+ pTest = (PRTTESTINT)RTTlsGet(g_iTestTls); \
+ AssertPtrReturn(pTest, (rc)); \
+ AssertReturn(pTest->u32Magic == RTTESTINT_MAGIC, (rc)); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtTestGuardedFreeOne(PRTTESTGUARDEDMEM pMem);
+static int rtTestPrintf(PRTTESTINT pTest, const char *pszFormat, ...);
+static void rtTestXmlStart(PRTTESTINT pTest, const char *pszTest);
+static void rtTestXmlElemV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va);
+static void rtTestXmlElem(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, ...);
+static void rtTestXmlElemStartV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va);
+static void rtTestXmlElemStart(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, ...);
+static void rtTestXmlElemEnd(PRTTESTINT pTest, const char *pszTag);
+static void rtTestXmlEnd(PRTTESTINT pTest);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** For serializing TLS init. */
+static RTONCE g_TestInitOnce = RTONCE_INITIALIZER;
+/** Our TLS entry. */
+static RTTLS g_iTestTls = NIL_RTTLS;
+
+
+
+/**
+ * Init TLS index once.
+ *
+ * @returns IPRT status code.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int32_t) rtTestInitOnce(void *pvUser)
+{
+ NOREF(pvUser);
+ return RTTlsAllocEx(&g_iTestTls, NULL);
+}
+
+
+RTR3DECL(int) RTTestCreateEx(const char *pszTest, uint32_t fFlags, RTTESTLVL enmMaxLevel,
+ RTHCINTPTR iNativeTestPipe, const char *pszXmlFile, PRTTEST phTest)
+{
+ AssertReturn(!(fFlags & ~RTTEST_C_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertPtrNull(phTest);
+ AssertPtrNull(pszXmlFile);
+ /* RTTESTLVL_INVALID is valid! */
+ AssertReturn(enmMaxLevel >= RTTESTLVL_INVALID && enmMaxLevel < RTTESTLVL_END, VERR_INVALID_PARAMETER);
+
+ /*
+ * Global init.
+ */
+ int rc = RTOnce(&g_TestInitOnce, rtTestInitOnce, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Create the instance.
+ */
+ PRTTESTINT pTest = (PRTTESTINT)RTMemAllocZ(sizeof(*pTest));
+ if (!pTest)
+ return VERR_NO_MEMORY;
+ pTest->u32Magic = RTTESTINT_MAGIC;
+ pTest->pszTest = RTStrDup(pszTest);
+ pTest->cchTest = strlen(pszTest);
+ pTest->cbGuard = PAGE_SIZE * 7;
+ pTest->enmMaxLevel = enmMaxLevel == RTTESTLVL_INVALID ? RTTESTLVL_INFO : enmMaxLevel;
+ pTest->fFlags = fFlags;
+
+ pTest->pOutStrm = g_pStdOut;
+ pTest->fNewLine = true;
+
+ pTest->pGuardedMem = NULL;
+
+ pTest->pszSubTest = NULL;
+ pTest->cchSubTest = 0;
+ pTest->fSubTestSkipped = false;
+ pTest->fSubTestReported = true;
+ pTest->cSubTestAtErrors = 0;
+ pTest->cSubTests = 0;
+ pTest->cSubTestsFailed = 0;
+
+ pTest->fXmlEnabled = false;
+ pTest->fXmlTopTestDone = false;
+ pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
+ pTest->hXmlPipe = NIL_RTPIPE;
+ pTest->hXmlFile = NIL_RTFILE;
+ pTest->cXmlElements = 0;
+ pTest->cAssertionsDisabledAndQuieted = 0;
+ pTest->fAssertSavedMayPanic = true;
+ pTest->fAssertSavedQuiet = false;
+
+ rc = RTCritSectInit(&pTest->Lock);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&pTest->OutputLock);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Associate it with our TLS entry unless there is already
+ * an instance there.
+ */
+ if ( !(fFlags & RTTEST_C_NO_TLS)
+ && !RTTlsGet(g_iTestTls))
+ rc = RTTlsSet(g_iTestTls, pTest);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Output level override?
+ */
+ char szEnvVal[RTPATH_MAX];
+ if ((fFlags & RTTEST_C_USE_ENV) && enmMaxLevel == RTTESTLVL_INVALID)
+ {
+ rc = RTEnvGetEx(RTENV_DEFAULT, "IPRT_TEST_MAX_LEVEL", szEnvVal, sizeof(szEnvVal), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszMaxLevel = RTStrStrip(szEnvVal);
+ if (!strcmp(pszMaxLevel, "all"))
+ pTest->enmMaxLevel = RTTESTLVL_DEBUG;
+ if (!strcmp(pszMaxLevel, "quiet"))
+ pTest->enmMaxLevel = RTTESTLVL_FAILURE;
+ else if (!strcmp(pszMaxLevel, "debug"))
+ pTest->enmMaxLevel = RTTESTLVL_DEBUG;
+ else if (!strcmp(pszMaxLevel, "info"))
+ pTest->enmMaxLevel = RTTESTLVL_INFO;
+ else if (!strcmp(pszMaxLevel, "sub_test"))
+ pTest->enmMaxLevel = RTTESTLVL_SUB_TEST;
+ else if (!strcmp(pszMaxLevel, "failure"))
+ pTest->enmMaxLevel = RTTESTLVL_FAILURE;
+ }
+ else if (rc != VERR_ENV_VAR_NOT_FOUND)
+ RTStrmPrintf(g_pStdErr, "%s: test pipe error: RTEnvGetEx(IPRT_TEST_MAX_LEVEL) -> %Rrc\n", pszTest, rc);
+ }
+
+ /*
+ * Any test driver we are connected or should connect to?
+ */
+ if (!(fFlags & RTTEST_C_NO_XML_REPORTING_PIPE))
+ {
+ if ( (fFlags & RTTEST_C_USE_ENV)
+ && iNativeTestPipe == -1)
+ {
+ rc = RTEnvGetEx(RTENV_DEFAULT, "IPRT_TEST_PIPE", szEnvVal, sizeof(szEnvVal), NULL);
+ if (RT_SUCCESS(rc))
+ {
+#if ARCH_BITS == 64
+ rc = RTStrToInt64Full(szEnvVal, 0, &iNativeTestPipe);
+#else
+ rc = RTStrToInt32Full(szEnvVal, 0, &iNativeTestPipe);
+#endif
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "%s: test pipe error: RTStrToInt32Full(\"%s\") -> %Rrc\n",
+ pszTest, szEnvVal, rc);
+ iNativeTestPipe = -1;
+ }
+ }
+ else if (rc != VERR_ENV_VAR_NOT_FOUND)
+ RTStrmPrintf(g_pStdErr, "%s: test pipe error: RTEnvGetEx(IPRT_TEST_PIPE) -> %Rrc\n", pszTest, rc);
+ }
+ if (iNativeTestPipe != -1)
+ {
+ rc = RTPipeFromNative(&pTest->hXmlPipe, iNativeTestPipe, RTPIPE_N_WRITE);
+ if (RT_SUCCESS(rc))
+ pTest->fXmlEnabled = true;
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "%s: test pipe error: RTPipeFromNative(,%p,WRITE) -> %Rrc\n",
+ pszTest, iNativeTestPipe, rc);
+ pTest->hXmlPipe = NIL_RTPIPE;
+ }
+ }
+ }
+
+ /*
+ * Any test file we should write the test report to?
+ */
+ if (!(fFlags & RTTEST_C_NO_XML_REPORTING_FILE))
+ {
+ if ((fFlags & RTTEST_C_USE_ENV) && pszXmlFile == NULL)
+ {
+ rc = RTEnvGetEx(RTENV_DEFAULT, "IPRT_TEST_FILE", szEnvVal, sizeof(szEnvVal), NULL);
+ if (RT_SUCCESS(rc))
+ pszXmlFile = szEnvVal;
+ else if (rc != VERR_ENV_VAR_NOT_FOUND)
+ RTStrmPrintf(g_pStdErr, "%s: test file error: RTEnvGetEx(IPRT_TEST_MAX_LEVEL) -> %Rrc\n", pszTest, rc);
+ }
+ if (pszXmlFile && *pszXmlFile)
+ {
+ rc = RTFileOpen(&pTest->hXmlFile, pszXmlFile,
+ RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE);
+ if (RT_SUCCESS(rc))
+ pTest->fXmlEnabled = true;
+ else
+ {
+ RTStrmPrintf(g_pStdErr, "%s: test file error: RTFileOpen(,\"%s\",) -> %Rrc\n",
+ pszTest, pszXmlFile, rc);
+ pTest->hXmlFile = NIL_RTFILE;
+ }
+ }
+ }
+
+ /*
+ * What do we report in the XML stream/file.?
+ */
+ pTest->fXmlOmitTopTest = (fFlags & RTTEST_C_XML_OMIT_TOP_TEST)
+ || ( (fFlags & RTTEST_C_USE_ENV)
+ && RTEnvExistEx(RTENV_DEFAULT, "IPRT_TEST_OMIT_TOP_TEST"));
+
+ /*
+ * Tell the test driver that we're up to.
+ */
+ rtTestXmlStart(pTest, pszTest);
+
+ *phTest = pTest;
+ return VINF_SUCCESS;
+ }
+
+ /* bail out. */
+ RTCritSectDelete(&pTest->OutputLock);
+ }
+ RTCritSectDelete(&pTest->Lock);
+ }
+ pTest->u32Magic = 0;
+ RTStrFree((char *)pTest->pszTest);
+ RTMemFree(pTest);
+ return rc;
+}
+
+
+RTR3DECL(int) RTTestCreate(const char *pszTest, PRTTEST phTest)
+{
+ return RTTestCreateEx(pszTest, RTTEST_C_USE_ENV, RTTESTLVL_INVALID, -1 /*iNativeTestPipe*/, NULL /*pszXmlFile*/, phTest);
+}
+
+
+RTR3DECL(int) RTTestCreateChild(const char *pszTest, PRTTEST phTest)
+{
+ return RTTestCreateEx(pszTest, RTTEST_C_USE_ENV | RTTEST_C_NO_XML_REPORTING,
+ RTTESTLVL_INVALID, -1 /*iNativeTestPipe*/, NULL /*pszXmlFile*/, phTest);
+}
+
+
+RTR3DECL(RTEXITCODE) RTTestInitAndCreate(const char *pszTest, PRTTEST phTest)
+{
+ int rc = RTR3InitExeNoArguments(0);
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "%s: fatal error: RTR3InitExeNoArguments failed with rc=%Rrc\n", pszTest, rc);
+ return RTEXITCODE_INIT;
+ }
+
+ rc = RTTestCreate(pszTest, phTest);
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "%s: fatal error: RTTestCreate failed with rc=%Rrc\n", pszTest, rc);
+ return RTEXITCODE_INIT;
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+
+RTR3DECL(RTEXITCODE) RTTestInitExAndCreate(int cArgs, char ***ppapszArgs, uint32_t fRtInit, const char *pszTest, PRTTEST phTest)
+{
+ int rc;
+ if (cArgs <= 0 && ppapszArgs == NULL)
+ rc = RTR3InitExeNoArguments(fRtInit);
+ else
+ rc = RTR3InitExe(cArgs, ppapszArgs, fRtInit);
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "%s: fatal error: RTR3InitExe(,,%#x) failed with rc=%Rrc\n", pszTest, fRtInit, rc);
+ return RTEXITCODE_INIT;
+ }
+
+ rc = RTTestCreate(pszTest, phTest);
+ if (RT_FAILURE(rc))
+ {
+ RTStrmPrintf(g_pStdErr, "%s: fatal error: RTTestCreate failed with rc=%Rrc\n", pszTest, rc);
+ return RTEXITCODE_INIT;
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTestDestroy(RTTEST hTest)
+{
+ /*
+ * Validate
+ */
+ if (hTest == NIL_RTTEST)
+ return VINF_SUCCESS;
+ RTTESTINT *pTest = hTest;
+ RTTEST_VALID_RETURN(pTest);
+
+ /*
+ * Make sure we end with a new line and have finished up the XML.
+ */
+ if (!pTest->fNewLine)
+ rtTestPrintf(pTest, "\n");
+ rtTestXmlEnd(pTest);
+
+ /*
+ * Clean up.
+ */
+ if ((RTTESTINT *)RTTlsGet(g_iTestTls) == pTest)
+ RTTlsSet(g_iTestTls, NULL);
+
+ ASMAtomicWriteU32(&pTest->u32Magic, ~RTTESTINT_MAGIC);
+ RTCritSectDelete(&pTest->Lock);
+ RTCritSectDelete(&pTest->OutputLock);
+
+ /* free guarded memory. */
+ PRTTESTGUARDEDMEM pMem = pTest->pGuardedMem;
+ pTest->pGuardedMem = NULL;
+ while (pMem)
+ {
+ PRTTESTGUARDEDMEM pFree = pMem;
+ pMem = pMem->pNext;
+ rtTestGuardedFreeOne(pFree);
+ }
+
+ RTStrFree((char *)pTest->pszSubTest);
+ pTest->pszSubTest = NULL;
+ RTStrFree((char *)pTest->pszTest);
+ pTest->pszTest = NULL;
+ RTStrFree(pTest->pszErrCtx);
+ pTest->pszErrCtx = NULL;
+ RTMemFree(pTest);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTestSetDefault(RTTEST hNewDefaultTest, PRTTEST phOldTest)
+{
+ if (phOldTest)
+ *phOldTest = (RTTEST)RTTlsGet(g_iTestTls);
+ return RTTlsSet(g_iTestTls, hNewDefaultTest);
+}
+
+
+RTR3DECL(int) RTTestChangeName(RTTEST hTest, const char *pszName)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN(pTest);
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(*pszName, VERR_INVALID_PARAMETER);
+
+ size_t cchName = strlen(pszName);
+ AssertReturn(cchName < 128, VERR_INVALID_PARAMETER);
+ char *pszDupName = RTStrDup(pszName);
+ if (!pszDupName)
+ return VERR_NO_STR_MEMORY;
+
+ RTCritSectEnter(&pTest->Lock);
+ RTCritSectEnter(&pTest->OutputLock);
+
+ char *pszOldName = (char *)pTest->pszTest;
+ pTest->pszTest = pszDupName;
+ pTest->cchTest = cchName;
+
+ RTCritSectLeave(&pTest->OutputLock);
+ RTCritSectLeave(&pTest->Lock);
+
+ RTStrFree(pszOldName);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTestGuardedAlloc(RTTEST hTest, size_t cb, uint32_t cbAlign, bool fHead, void **ppvUser)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN(pTest);
+ if (cbAlign == 0)
+ cbAlign = 1;
+ AssertReturn(cbAlign <= PAGE_SIZE, VERR_INVALID_PARAMETER);
+ AssertReturn(cbAlign == (UINT32_C(1) << (ASMBitFirstSetU32(cbAlign) - 1)), VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate the record and block and initialize them.
+ */
+ int rc = VERR_NO_MEMORY;
+ PRTTESTGUARDEDMEM pMem = (PRTTESTGUARDEDMEM)RTMemAlloc(sizeof(*pMem));
+ if (RT_LIKELY(pMem))
+ {
+ size_t const cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ pMem->aGuards[0].cb = pMem->aGuards[1].cb = pTest->cbGuard;
+ pMem->cbAlloc = pMem->aGuards[0].cb + pMem->aGuards[1].cb + cbAligned;
+ pMem->pvAlloc = RTMemPageAlloc(pMem->cbAlloc);
+ if (pMem->pvAlloc)
+ {
+ pMem->aGuards[0].pv = pMem->pvAlloc;
+ pMem->pvUser = (uint8_t *)pMem->pvAlloc + pMem->aGuards[0].cb;
+ pMem->aGuards[1].pv = (uint8_t *)pMem->pvUser + cbAligned;
+ if (!fHead)
+ {
+ size_t off = cb & PAGE_OFFSET_MASK;
+ if (off)
+ {
+ off = PAGE_SIZE - RT_ALIGN_Z(off, cbAlign);
+ pMem->pvUser = (uint8_t *)pMem->pvUser + off;
+ }
+ }
+
+ /*
+ * Set up the guards and link the record.
+ */
+ ASMMemFill32(pMem->aGuards[0].pv, pMem->aGuards[0].cb, 0xdeadbeef);
+ ASMMemFill32(pMem->aGuards[1].pv, pMem->aGuards[1].cb, 0xdeadbeef);
+ rc = RTMemProtect(pMem->aGuards[0].pv, pMem->aGuards[0].cb, RTMEM_PROT_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTMemProtect(pMem->aGuards[1].pv, pMem->aGuards[1].cb, RTMEM_PROT_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ *ppvUser = pMem->pvUser;
+
+ RTCritSectEnter(&pTest->Lock);
+ pMem->pNext = pTest->pGuardedMem;
+ pTest->pGuardedMem = pMem;
+ RTCritSectLeave(&pTest->Lock);
+
+ return VINF_SUCCESS;
+ }
+
+ RTMemProtect(pMem->aGuards[0].pv, pMem->aGuards[0].cb, RTMEM_PROT_WRITE | RTMEM_PROT_READ);
+ }
+
+ RTMemPageFree(pMem->pvAlloc, pMem->cbAlloc);
+ }
+ RTMemFree(pMem);
+ }
+ return rc;
+}
+
+
+RTR3DECL(void *) RTTestGuardedAllocTail(RTTEST hTest, size_t cb)
+{
+ void *pvUser;
+ int rc = RTTestGuardedAlloc(hTest, cb, 1 /* cbAlign */, false /* fHead */, &pvUser);
+ if (RT_SUCCESS(rc))
+ return pvUser;
+ return NULL;
+}
+
+
+RTR3DECL(void *) RTTestGuardedAllocHead(RTTEST hTest, size_t cb)
+{
+ void *pvUser;
+ int rc = RTTestGuardedAlloc(hTest, cb, 1 /* cbAlign */, true /* fHead */, &pvUser);
+ if (RT_SUCCESS(rc))
+ return pvUser;
+ return NULL;
+}
+
+
+/**
+ * Frees one block of guarded memory.
+ *
+ * The caller is responsible for unlinking it.
+ *
+ * @param pMem The memory record.
+ */
+static void rtTestGuardedFreeOne(PRTTESTGUARDEDMEM pMem)
+{
+ int rc;
+ rc = RTMemProtect(pMem->aGuards[0].pv, pMem->aGuards[0].cb, RTMEM_PROT_WRITE | RTMEM_PROT_READ); AssertRC(rc);
+ rc = RTMemProtect(pMem->aGuards[1].pv, pMem->aGuards[1].cb, RTMEM_PROT_WRITE | RTMEM_PROT_READ); AssertRC(rc);
+ RTMemPageFree(pMem->pvAlloc, pMem->cbAlloc);
+ RTMemFree(pMem);
+}
+
+
+RTR3DECL(int) RTTestGuardedFree(RTTEST hTest, void *pv)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN(pTest);
+ if (!pv)
+ return VINF_SUCCESS;
+
+ /*
+ * Find it.
+ */
+ int rc = VERR_INVALID_POINTER;
+ PRTTESTGUARDEDMEM pPrev = NULL;
+
+ RTCritSectEnter(&pTest->Lock);
+ for (PRTTESTGUARDEDMEM pMem = pTest->pGuardedMem; pMem; pMem = pMem->pNext)
+ {
+ if (pMem->pvUser == pv)
+ {
+ if (pPrev)
+ pPrev->pNext = pMem->pNext;
+ else
+ pTest->pGuardedMem = pMem->pNext;
+ rtTestGuardedFreeOne(pMem);
+ rc = VINF_SUCCESS;
+ break;
+ }
+ pPrev = pMem;
+ }
+ RTCritSectLeave(&pTest->Lock);
+
+ return rc;
+}
+
+
+/**
+ * Outputs the formatted XML.
+ *
+ * @param pTest The test instance.
+ * @param pszFormat The format string.
+ * @param va The format arguments.
+ */
+static void rtTestXmlOutputV(PRTTESTINT pTest, const char *pszFormat, va_list va)
+{
+ if (pTest->fXmlEnabled)
+ {
+ char *pszStr;
+ ssize_t cchStr = RTStrAPrintfV(&pszStr, pszFormat, va);
+ if (pszStr)
+ {
+ if (pTest->hXmlPipe != NIL_RTPIPE)
+ RTPipeWriteBlocking(pTest->hXmlPipe, pszStr, cchStr, NULL);
+ if (pTest->hXmlFile != NIL_RTFILE)
+ RTFileWrite(pTest->hXmlFile, pszStr, cchStr, NULL);
+ RTStrFree(pszStr);
+ }
+ }
+}
+
+
+/**
+ * Outputs the formatted XML.
+ *
+ * @param pTest The test instance.
+ * @param pszFormat The format string.
+ * @param ... The format arguments.
+ */
+static void rtTestXmlOutput(PRTTESTINT pTest, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ rtTestXmlOutputV(pTest, pszFormat, va);
+ va_end(va);
+}
+
+
+/**
+ * Starts the XML stream.
+ *
+ * @param pTest The test instance.
+ * @param pszTest The test name.
+ */
+static void rtTestXmlStart(PRTTESTINT pTest, const char *pszTest)
+{
+ pTest->cXmlElements = 0;
+ if (pTest->fXmlEnabled)
+ {
+ rtTestXmlOutput(pTest, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
+ pTest->fXmlTopTestDone = !(pTest->fFlags & RTTEST_C_XML_DELAY_TOP_TEST) || pTest->fXmlOmitTopTest;
+ if (pTest->fXmlTopTestDone && !pTest->fXmlOmitTopTest)
+ rtTestXmlElemStart(pTest, "Test", "name=%RMas", pszTest);
+ }
+}
+
+
+/**
+ * Emit an XML element that doesn't have any value and instead ends immediately.
+ *
+ * The caller must own the instance lock.
+ *
+ * @param pTest The test instance.
+ * @param pszTag The element tag.
+ * @param pszAttrFmt The element attributes as a format string. Use
+ * NULL if none.
+ * @param va Format string arguments.
+ */
+static void rtTestXmlElemV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va)
+{
+ if (pTest->fXmlEnabled)
+ {
+ RTTIMESPEC TimeSpec;
+ RTTIME Time;
+ char szTS[80];
+ RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&TimeSpec)), szTS, sizeof(szTS));
+
+ if (pTest->eXmlState != RTTESTINT::kXmlPos_ElementEnd)
+ rtTestXmlOutput(pTest, "\n");
+
+ if (!pszAttrFmt || !*pszAttrFmt)
+ rtTestXmlOutput(pTest, "%*s<%s timestamp=%RMas/>\n",
+ pTest->cXmlElements * 2, "", pszTag, szTS);
+ else
+ {
+ va_list va2;
+ va_copy(va2, va);
+ rtTestXmlOutput(pTest, "%*s<%s timestamp=%RMas %N/>\n",
+ pTest->cXmlElements * 2, "", pszTag, szTS, pszAttrFmt, &va2);
+ va_end(va2);
+ }
+ pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
+ }
+}
+
+
+/**
+ * Wrapper around rtTestXmlElemV.
+ */
+static void rtTestXmlElem(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, ...)
+{
+ va_list va;
+ va_start(va, pszAttrFmt);
+ rtTestXmlElemV(pTest, pszTag, pszAttrFmt, va);
+ va_end(va);
+}
+
+
+/**
+ * Starts a new XML element.
+ *
+ * The caller must own the instance lock.
+ *
+ * @param pTest The test instance.
+ * @param pszTag The element tag.
+ * @param pszAttrFmt The element attributes as a format string. Use
+ * NULL if none.
+ * @param va Format string arguments.
+ */
+static void rtTestXmlElemStartV(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, va_list va)
+{
+ /* Push it onto the stack. */
+ size_t i = pTest->cXmlElements;
+ AssertReturnVoid(i < RT_ELEMENTS(pTest->apszXmlElements));
+ pTest->apszXmlElements[i] = pszTag;
+ pTest->cXmlElements = i + 1;
+
+ if (pTest->fXmlEnabled)
+ {
+ RTTIMESPEC TimeSpec;
+ RTTIME Time;
+ char szTS[80];
+ RTTimeToString(RTTimeExplode(&Time, RTTimeNow(&TimeSpec)), szTS, sizeof(szTS));
+
+ if (pTest->eXmlState != RTTESTINT::kXmlPos_ElementEnd)
+ rtTestXmlOutput(pTest, "\n");
+
+ if (!pszAttrFmt || !*pszAttrFmt)
+ rtTestXmlOutput(pTest, "%*s<%s timestamp=%RMas>",
+ i * 2, "", pszTag, szTS);
+ else
+ {
+ va_list va2;
+ va_copy(va2, va);
+ rtTestXmlOutput(pTest, "%*s<%s timestamp=%RMas %N>",
+ i * 2, "", pszTag, szTS, pszAttrFmt, &va2);
+ va_end(va2);
+ }
+ pTest->eXmlState = RTTESTINT::kXmlPos_ValueStart;
+ }
+}
+
+
+/**
+ * Wrapper around rtTestXmlElemStartV.
+ */
+static void rtTestXmlElemStart(PRTTESTINT pTest, const char *pszTag, const char *pszAttrFmt, ...)
+{
+ va_list va;
+ va_start(va, pszAttrFmt);
+ rtTestXmlElemStartV(pTest, pszTag, pszAttrFmt, va);
+ va_end(va);
+}
+
+
+/**
+ * Ends the current element.
+ *
+ * The caller must own the instance lock.
+ *
+ * @param pTest The test instance.
+ * @param pszTag The tag we're ending (chiefly for sanity
+ * checking).
+ */
+static void rtTestXmlElemEnd(PRTTESTINT pTest, const char *pszTag)
+{
+ /* pop the element */
+ size_t i = pTest->cXmlElements;
+ AssertReturnVoid(i > 0);
+ i--;
+ AssertReturnVoid(!strcmp(pszTag, pTest->apszXmlElements[i]));
+ pTest->cXmlElements = i;
+
+ /* Do the closing. */
+ if (pTest->fXmlEnabled)
+ {
+ if (pTest->eXmlState == RTTESTINT::kXmlPos_ValueStart)
+ rtTestXmlOutput(pTest, "\n%*s</%s>\n", i * 2, "", pszTag);
+ else if (pTest->eXmlState == RTTESTINT::kXmlPos_ElementEnd)
+ rtTestXmlOutput(pTest, "%*s</%s>\n", i * 2, "", pszTag);
+ else
+ rtTestXmlOutput(pTest, "</%s>\n", pszTag);
+ pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
+ }
+}
+
+
+/**
+ * Ends the XML stream, closing all open elements.
+ *
+ * The caller must own the instance lock.
+ *
+ * @param pTest The test instance.
+ */
+static void rtTestXmlEnd(PRTTESTINT pTest)
+{
+ if (pTest->fXmlEnabled)
+ {
+ /*
+ * Close all the elements and add the final TestEnd one to get a
+ * final timestamp and some certainty that the XML is valid.
+ */
+ size_t i = pTest->cXmlElements;
+ AssertReturnVoid(i > 0 || pTest->fXmlOmitTopTest || !pTest->fXmlTopTestDone);
+ while (i-- > 1)
+ {
+ const char *pszTag = pTest->apszXmlElements[pTest->cXmlElements];
+ if (pTest->eXmlState == RTTESTINT::kXmlPos_ValueStart)
+ rtTestXmlOutput(pTest, "\n%*s</%s>\n", i * 2, "", pszTag);
+ else if (pTest->eXmlState == RTTESTINT::kXmlPos_ElementEnd)
+ rtTestXmlOutput(pTest, "%*s</%s>\n", i * 2, "", pszTag);
+ else
+ rtTestXmlOutput(pTest, "</%s>\n", pszTag);
+ pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
+ }
+
+ if (!pTest->fXmlOmitTopTest && pTest->fXmlTopTestDone)
+ {
+ rtTestXmlElem(pTest, "End", "SubTests=\"%u\" SubTestsFailed=\"%u\" errors=\"%u\"",
+ pTest->cSubTests, pTest->cSubTestsFailed, pTest->cErrors);
+ rtTestXmlOutput(pTest, "</Test>\n");
+ }
+
+ /*
+ * Close the XML outputs.
+ */
+ if (pTest->hXmlPipe != NIL_RTPIPE)
+ {
+ RTPipeClose(pTest->hXmlPipe);
+ pTest->hXmlPipe = NIL_RTPIPE;
+ }
+ if (pTest->hXmlFile != NIL_RTFILE)
+ {
+ RTFileClose(pTest->hXmlFile);
+ pTest->hXmlFile = NIL_RTFILE;
+ }
+ pTest->fXmlEnabled = false;
+ pTest->eXmlState = RTTESTINT::kXmlPos_ElementEnd;
+ }
+ pTest->cXmlElements = 0;
+}
+
+/**
+ * Output callback.
+ *
+ * @returns number of bytes written.
+ * @param pvArg User argument.
+ * @param pachChars Pointer to an array of utf-8 characters.
+ * @param cbChars Number of bytes in the character array pointed to by pachChars.
+ */
+static DECLCALLBACK(size_t) rtTestPrintfOutput(void *pvArg, const char *pachChars, size_t cbChars)
+{
+ size_t cch = 0;
+ PRTTESTINT pTest = (PRTTESTINT)pvArg;
+ if (cbChars)
+ {
+ do
+ {
+ /* insert prefix if at a newline. */
+ if (pTest->fNewLine)
+ {
+ RTStrmWrite(pTest->pOutStrm, pTest->pszTest, pTest->cchTest);
+ RTStrmWrite(pTest->pOutStrm, ": ", 2);
+ cch += 2 + pTest->cchTest;
+ }
+
+ /* look for newline and write the stuff. */
+ const char *pchEnd = (const char *)memchr(pachChars, '\n', cbChars);
+ if (!pchEnd)
+ {
+ pTest->fNewLine = false;
+ RTStrmWrite(pTest->pOutStrm, pachChars, cbChars);
+ cch += cbChars;
+ break;
+ }
+
+ pTest->fNewLine = true;
+ size_t const cchPart = pchEnd - pachChars + 1;
+ RTStrmWrite(pTest->pOutStrm, pachChars, cchPart);
+ cch += cchPart;
+ pachChars += cchPart;
+ cbChars -= cchPart;
+ } while (cbChars);
+ }
+ else
+ RTStrmFlush(pTest->pOutStrm);
+ return cch;
+}
+
+
+/**
+ * Internal output worker.
+ *
+ * Caller takes the lock.
+ *
+ * @returns Number of chars printed.
+ * @param pTest The test instance.
+ * @param pszFormat The message.
+ * @param va The arguments.
+ */
+static int rtTestPrintfV(PRTTESTINT pTest, const char *pszFormat, va_list va)
+{
+ return (int)RTStrFormatV(rtTestPrintfOutput, pTest, NULL, NULL, pszFormat, va);
+}
+
+
+/**
+ * Internal output worker.
+ *
+ * Caller takes the lock.
+ *
+ * @returns Number of chars printed.
+ * @param pTest The test instance.
+ * @param pszFormat The message.
+ * @param ... The arguments.
+ */
+static int rtTestPrintf(PRTTESTINT pTest, const char *pszFormat, ...)
+{
+ va_list va;
+
+ va_start(va, pszFormat);
+ int cch = rtTestPrintfV(pTest, pszFormat, va);
+ va_end(va);
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestPrintfNlV(RTTEST hTest, RTTESTLVL enmLevel, const char *pszFormat, va_list va)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN_RC(pTest, -1);
+
+ RTCritSectEnter(&pTest->OutputLock);
+
+ int cch = 0;
+ if (enmLevel <= pTest->enmMaxLevel)
+ {
+ if (!pTest->fNewLine)
+ cch += rtTestPrintf(pTest, "\n");
+ cch += rtTestPrintfV(pTest, pszFormat, va);
+ }
+
+ RTCritSectLeave(&pTest->OutputLock);
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestPrintfNl(RTTEST hTest, RTTESTLVL enmLevel, const char *pszFormat, ...)
+{
+ va_list va;
+
+ va_start(va, pszFormat);
+ int cch = RTTestPrintfNlV(hTest, enmLevel, pszFormat, va);
+ va_end(va);
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestPrintfV(RTTEST hTest, RTTESTLVL enmLevel, const char *pszFormat, va_list va)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN_RC(pTest, -1);
+
+ RTCritSectEnter(&pTest->OutputLock);
+ int cch = 0;
+ if (enmLevel <= pTest->enmMaxLevel)
+ cch += rtTestPrintfV(pTest, pszFormat, va);
+ RTCritSectLeave(&pTest->OutputLock);
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestPrintf(RTTEST hTest, RTTESTLVL enmLevel, const char *pszFormat, ...)
+{
+ va_list va;
+
+ va_start(va, pszFormat);
+ int cch = RTTestPrintfV(hTest, enmLevel, pszFormat, va);
+ va_end(va);
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestBanner(RTTEST hTest)
+{
+ return RTTestPrintfNl(hTest, RTTESTLVL_ALWAYS, "TESTING...\n");
+}
+
+
+/**
+ * Prints the result of a sub-test if necessary.
+ *
+ * @returns Number of chars printed.
+ * @param pTest The test instance.
+ * @remarks Caller own the test Lock.
+ */
+static int rtTestSubTestReport(PRTTESTINT pTest)
+{
+ int cch = 0;
+ if ( !pTest->fSubTestReported
+ && pTest->pszSubTest)
+ {
+ pTest->fSubTestReported = true;
+ uint32_t cErrors = ASMAtomicUoReadU32(&pTest->cErrors) - pTest->cSubTestAtErrors;
+ if (!cErrors)
+ {
+ if (!pTest->fSubTestSkipped)
+ {
+ rtTestXmlElem(pTest, "Passed", NULL);
+ rtTestXmlElemEnd(pTest, "Test");
+ cch += RTTestPrintfNl(pTest, RTTESTLVL_SUB_TEST, "%-60s: PASSED\n", pTest->pszSubTest);
+ }
+ else
+ {
+ rtTestXmlElem(pTest, "Skipped", NULL);
+ rtTestXmlElemEnd(pTest, "Test");
+ cch += RTTestPrintfNl(pTest, RTTESTLVL_SUB_TEST, "%-60s: SKIPPED\n", pTest->pszSubTest);
+ }
+ }
+ else
+ {
+ pTest->cSubTestsFailed++;
+ rtTestXmlElem(pTest, "Failed", "errors=\"%u\"", cErrors);
+ rtTestXmlElemEnd(pTest, "Test");
+ cch += RTTestPrintfNl(pTest, RTTESTLVL_SUB_TEST, "%-60s: FAILED (%u errors)\n",
+ pTest->pszSubTest, cErrors);
+ }
+ }
+ return cch;
+}
+
+
+/**
+ * RTTestSub and RTTestSubDone worker that cleans up the current (if any)
+ * sub test.
+ *
+ * @returns Number of chars printed.
+ * @param pTest The test instance.
+ * @remarks Caller own the test Lock.
+ */
+static int rtTestSubCleanup(PRTTESTINT pTest)
+{
+ int cch = 0;
+ if (pTest->pszSubTest)
+ {
+ cch += rtTestSubTestReport(pTest);
+
+ RTStrFree((char *)pTest->pszSubTest);
+ pTest->pszSubTest = NULL;
+ pTest->fSubTestReported = true;
+ }
+ RTStrFree(pTest->pszErrCtx);
+ pTest->pszErrCtx = NULL;
+ return cch;
+}
+
+
+RTR3DECL(RTEXITCODE) RTTestSummaryAndDestroy(RTTEST hTest)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN_RC(pTest, RTEXITCODE_FAILURE);
+
+ RTCritSectEnter(&pTest->Lock);
+ rtTestSubTestReport(pTest);
+ RTCritSectLeave(&pTest->Lock);
+
+ RTEXITCODE enmExitCode;
+ if (!pTest->cErrors)
+ {
+ RTTestPrintfNl(hTest, RTTESTLVL_ALWAYS, "SUCCESS\n");
+ enmExitCode = RTEXITCODE_SUCCESS;
+ }
+ else
+ {
+ RTTestPrintfNl(hTest, RTTESTLVL_ALWAYS, "FAILURE - %u errors\n", pTest->cErrors);
+ enmExitCode = RTEXITCODE_FAILURE;
+ }
+
+ RTTestDestroy(pTest);
+ return enmExitCode;
+}
+
+
+RTR3DECL(RTEXITCODE) RTTestSkipAndDestroyV(RTTEST hTest, const char *pszReasonFmt, va_list va)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN_RC(pTest, RTEXITCODE_SKIPPED);
+
+ RTCritSectEnter(&pTest->Lock);
+ rtTestSubTestReport(pTest);
+ RTCritSectLeave(&pTest->Lock);
+
+ RTEXITCODE enmExitCode;
+ if (!pTest->cErrors)
+ {
+ if (pszReasonFmt)
+ RTTestPrintfNlV(hTest, RTTESTLVL_FAILURE, pszReasonFmt, va);
+ RTTestPrintfNl(hTest, RTTESTLVL_ALWAYS, "SKIPPED\n");
+ enmExitCode = RTEXITCODE_SKIPPED;
+ }
+ else
+ {
+ RTTestPrintfNl(hTest, RTTESTLVL_ALWAYS, "FAILURE - %u errors\n", pTest->cErrors);
+ enmExitCode = RTEXITCODE_FAILURE;
+ }
+
+ RTTestDestroy(pTest);
+ return enmExitCode;
+}
+
+
+RTR3DECL(RTEXITCODE) RTTestSkipAndDestroy(RTTEST hTest, const char *pszReasonFmt, ...)
+{
+ va_list va;
+ va_start(va, pszReasonFmt);
+ RTEXITCODE enmExitCode = RTTestSkipAndDestroyV(hTest, pszReasonFmt, va);
+ va_end(va);
+ return enmExitCode;
+}
+
+
+RTR3DECL(int) RTTestSub(RTTEST hTest, const char *pszSubTest)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN_RC(pTest, -1);
+
+ RTCritSectEnter(&pTest->Lock);
+
+ /* Cleanup, reporting if necessary previous sub test. */
+ rtTestSubCleanup(pTest);
+
+ /* Start new sub test. */
+ pTest->cSubTests++;
+ pTest->cSubTestAtErrors = ASMAtomicUoReadU32(&pTest->cErrors);
+ pTest->pszSubTest = RTStrDup(pszSubTest);
+ pTest->cchSubTest = strlen(pszSubTest);
+ AssertMsg(pTest->cchSubTest < 64 /* See g_kcchMaxTestResultName in testmanager/config.py. */,
+ ("cchSubTest=%u: '%s'\n", pTest->cchSubTest, pTest->pszSubTest));
+ pTest->fSubTestSkipped = false;
+ pTest->fSubTestReported = false;
+
+ int cch = 0;
+ if (pTest->enmMaxLevel >= RTTESTLVL_DEBUG)
+ cch = RTTestPrintfNl(hTest, RTTESTLVL_DEBUG, "debug: Starting sub-test '%s'\n", pszSubTest);
+
+ if (!pTest->fXmlTopTestDone)
+ {
+ pTest->fXmlTopTestDone = true;
+ rtTestXmlElemStart(pTest, "Test", "name=%RMas", pTest->pszTest);
+ }
+
+ rtTestXmlElemStart(pTest, "Test", "name=%RMas", pszSubTest);
+
+ RTCritSectLeave(&pTest->Lock);
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestSubF(RTTEST hTest, const char *pszSubTestFmt, ...)
+{
+ va_list va;
+ va_start(va, pszSubTestFmt);
+ int cch = RTTestSubV(hTest, pszSubTestFmt, va);
+ va_end(va);
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestSubV(RTTEST hTest, const char *pszSubTestFmt, va_list va)
+{
+ char *pszSubTest;
+ RTStrAPrintfV(&pszSubTest, pszSubTestFmt, va);
+ if (pszSubTest)
+ {
+ int cch = RTTestSub(hTest, pszSubTest);
+ RTStrFree(pszSubTest);
+ return cch;
+ }
+ return 0;
+}
+
+
+RTR3DECL(int) RTTestSubDone(RTTEST hTest)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN_RC(pTest, VERR_INVALID_HANDLE);
+
+ RTCritSectEnter(&pTest->Lock);
+ int cch = rtTestSubCleanup(pTest);
+ RTCritSectLeave(&pTest->Lock);
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestPassedV(RTTEST hTest, const char *pszFormat, va_list va)
+{
+ PRTTESTINT pTest = hTest;
+ AssertPtr(pszFormat);
+ RTTEST_GET_VALID_RETURN_RC(pTest, VERR_INVALID_HANDLE);
+
+ int cch = 0;
+ if (pTest->enmMaxLevel >= RTTESTLVL_INFO)
+ {
+ va_list va2;
+ va_copy(va2, va);
+
+ RTCritSectEnter(&pTest->OutputLock);
+ cch += rtTestPrintf(pTest, "%N\n", pszFormat, &va2);
+ RTCritSectLeave(&pTest->OutputLock);
+
+ va_end(va2);
+ }
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestPassed(RTTEST hTest, const char *pszFormat, ...)
+{
+ va_list va;
+
+ va_start(va, pszFormat);
+ int cch = RTTestPassedV(hTest, pszFormat, va);
+ va_end(va);
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestSkippedV(RTTEST hTest, const char *pszFormat, va_list va)
+{
+ PRTTESTINT pTest = hTest;
+ AssertPtrNull(pszFormat);
+ RTTEST_GET_VALID_RETURN_RC(pTest, VERR_INVALID_HANDLE);
+
+ pTest->fSubTestSkipped = true;
+
+ int cch = 0;
+ if (pszFormat && *pszFormat && pTest->enmMaxLevel >= RTTESTLVL_INFO)
+ {
+ va_list va2;
+ va_copy(va2, va);
+
+ RTCritSectEnter(&pTest->OutputLock);
+ cch += rtTestPrintf(pTest, "%N\n", pszFormat, &va2);
+ RTCritSectLeave(&pTest->OutputLock);
+
+ va_end(va2);
+ }
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestSkipped(RTTEST hTest, const char *pszFormat, ...)
+{
+ va_list va;
+
+ va_start(va, pszFormat);
+ int cch = RTTestSkippedV(hTest, pszFormat, va);
+ va_end(va);
+
+ return cch;
+}
+
+
+
+/**
+ * Gets the unit name.
+ *
+ * @returns Unit name.
+ * @param enmUnit The unit.
+ */
+static const char *rtTestUnitName(RTTESTUNIT enmUnit)
+{
+ switch (enmUnit)
+ {
+ case RTTESTUNIT_PCT: return "%";
+ case RTTESTUNIT_BYTES: return "bytes";
+ case RTTESTUNIT_BYTES_PER_SEC: return "bytes/s";
+ case RTTESTUNIT_KILOBYTES: return "KB";
+ case RTTESTUNIT_KILOBYTES_PER_SEC: return "KB/s";
+ case RTTESTUNIT_MEGABYTES: return "MB";
+ case RTTESTUNIT_MEGABYTES_PER_SEC: return "MB/s";
+ case RTTESTUNIT_PACKETS: return "packets";
+ case RTTESTUNIT_PACKETS_PER_SEC: return "packets/s";
+ case RTTESTUNIT_FRAMES: return "frames";
+ case RTTESTUNIT_FRAMES_PER_SEC: return "frames/s";
+ case RTTESTUNIT_OCCURRENCES: return "occurrences";
+ case RTTESTUNIT_OCCURRENCES_PER_SEC: return "occurrences/s";
+ case RTTESTUNIT_ROUND_TRIP: return "roundtrips";
+ case RTTESTUNIT_CALLS: return "calls";
+ case RTTESTUNIT_CALLS_PER_SEC: return "calls/s";
+ case RTTESTUNIT_SECS: return "s";
+ case RTTESTUNIT_MS: return "ms";
+ case RTTESTUNIT_NS: return "ns";
+ case RTTESTUNIT_NS_PER_CALL: return "ns/call";
+ case RTTESTUNIT_NS_PER_FRAME: return "ns/frame";
+ case RTTESTUNIT_NS_PER_OCCURRENCE: return "ns/occurrence";
+ case RTTESTUNIT_NS_PER_PACKET: return "ns/packet";
+ case RTTESTUNIT_NS_PER_ROUND_TRIP: return "ns/roundtrip";
+ case RTTESTUNIT_INSTRS: return "ins";
+ case RTTESTUNIT_INSTRS_PER_SEC: return "ins/sec";
+ case RTTESTUNIT_NONE: return "";
+ case RTTESTUNIT_PP1K: return "pp1k";
+ case RTTESTUNIT_PP10K: return "pp10k";
+ case RTTESTUNIT_PPM: return "ppm";
+ case RTTESTUNIT_PPB: return "ppb";
+ case RTTESTUNIT_TICKS: return "ticks";
+ case RTTESTUNIT_TICKS_PER_CALL: return "ticks/call";
+ case RTTESTUNIT_TICKS_PER_OCCURENCE: return "ticks/occ";
+ case RTTESTUNIT_PAGES: return "pages";
+ case RTTESTUNIT_PAGES_PER_SEC: return "pages/s";
+ case RTTESTUNIT_TICKS_PER_PAGE: return "ticks/page";
+ case RTTESTUNIT_NS_PER_PAGE: return "ns/page";
+ case RTTESTUNIT_PS: return "ps";
+ case RTTESTUNIT_PS_PER_CALL: return "ps/call";
+ case RTTESTUNIT_PS_PER_FRAME: return "ps/frame";
+ case RTTESTUNIT_PS_PER_OCCURRENCE: return "ps/occurrence";
+ case RTTESTUNIT_PS_PER_PACKET: return "ps/packet";
+ case RTTESTUNIT_PS_PER_ROUND_TRIP: return "ps/roundtrip";
+ case RTTESTUNIT_PS_PER_PAGE: return "ps/page";
+
+ /* No default so gcc helps us keep this up to date. */
+ case RTTESTUNIT_INVALID:
+ case RTTESTUNIT_END:
+ break;
+ }
+ AssertMsgFailed(("%d\n", enmUnit));
+ return "unknown";
+}
+
+
+RTR3DECL(int) RTTestValue(RTTEST hTest, const char *pszName, uint64_t u64Value, RTTESTUNIT enmUnit)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN(pTest);
+
+ Assert(strlen(pszName) < 56 /* See g_kcchMaxTestValueName in testmanager/config.py. */);
+
+ const char *pszUnit = rtTestUnitName(enmUnit);
+
+ RTCritSectEnter(&pTest->Lock);
+ rtTestXmlElem(pTest, "Value", "name=%RMas unit=%RMas value=\"%llu\"", pszName, pszUnit, u64Value);
+ RTCritSectLeave(&pTest->Lock);
+
+ RTCritSectEnter(&pTest->OutputLock);
+ rtTestPrintf(pTest, " %-58s: %'16llu %s\n", pszName, u64Value, pszUnit);
+ RTCritSectLeave(&pTest->OutputLock);
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTestValueF(RTTEST hTest, uint64_t u64Value, RTTESTUNIT enmUnit, const char *pszNameFmt, ...)
+{
+ va_list va;
+ va_start(va, pszNameFmt);
+ int rc = RTTestValueV(hTest, u64Value, enmUnit, pszNameFmt, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTR3DECL(int) RTTestValueV(RTTEST hTest, uint64_t u64Value, RTTESTUNIT enmUnit, const char *pszNameFmt, va_list va)
+{
+ char *pszName;
+ RTStrAPrintfV(&pszName, pszNameFmt, va);
+ if (!pszName)
+ return VERR_NO_MEMORY;
+ int rc = RTTestValue(hTest, pszName, u64Value, enmUnit);
+ RTStrFree(pszName);
+ return rc;
+}
+
+
+RTR3DECL(int) RTTestErrorInc(RTTEST hTest)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN(pTest);
+
+ ASMAtomicIncU32(&pTest->cErrors);
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(uint32_t) RTTestErrorCount(RTTEST hTest)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN_RC(pTest, UINT32_MAX);
+
+ return ASMAtomicReadU32(&pTest->cErrors);
+}
+
+
+RTR3DECL(uint32_t) RTTestSubErrorCount(RTTEST hTest)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN_RC(pTest, UINT32_MAX);
+
+ return ASMAtomicReadU32(&pTest->cErrors) - pTest->cSubTestAtErrors;
+}
+
+
+RTR3DECL(int) RTTestFailedV(RTTEST hTest, const char *pszFormat, va_list va)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN_RC(pTest, VERR_INVALID_HANDLE);
+
+ RTTestErrorInc(pTest);
+
+ int cch = 0;
+ if (pTest->enmMaxLevel >= RTTESTLVL_FAILURE)
+ {
+ va_list va2;
+ va_copy(va2, va);
+
+ const char *pszEnd = strchr(pszFormat, '\0');
+ bool fHasNewLine = pszFormat != pszEnd
+ && pszEnd[-1] == '\n';
+
+ RTCritSectEnter(&pTest->OutputLock);
+ cch += rtTestPrintf(pTest, fHasNewLine ? "%N" : "%N\n", pszFormat, &va2);
+ if (pTest->pszErrCtx)
+ {
+ cch += rtTestPrintf(pTest, "context: %s\n", pTest->pszErrCtx);
+ RTStrFree(pTest->pszErrCtx);
+ pTest->pszErrCtx = NULL;
+ }
+ RTCritSectLeave(&pTest->OutputLock);
+
+ va_end(va2);
+ }
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestFailed(RTTEST hTest, const char *pszFormat, ...)
+{
+ va_list va;
+
+ va_start(va, pszFormat);
+ int cch = RTTestFailedV(hTest, pszFormat, va);
+ va_end(va);
+
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestFailureDetailsV(RTTEST hTest, const char *pszFormat, va_list va)
+{
+ return RTTestPrintfV(hTest, RTTESTLVL_FAILURE, pszFormat, va);
+}
+
+
+RTR3DECL(int) RTTestFailureDetails(RTTEST hTest, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int cch = RTTestFailureDetailsV(hTest, pszFormat, va);
+ va_end(va);
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestErrContextV(RTTEST hTest, const char *pszFormat, va_list va)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN(pTest);
+
+ RTStrFree(pTest->pszErrCtx);
+ pTest->pszErrCtx = NULL;
+
+ if (pszFormat && *pszFormat)
+ {
+ pTest->pszErrCtx = RTStrAPrintf2V(pszFormat, va);
+ AssertReturn(pTest->pszErrCtx, VERR_NO_STR_MEMORY);
+ RTStrStripR(pTest->pszErrCtx);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTestErrContext(RTTEST hTest, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTTestErrContextV(hTest, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTR3DECL(int) RTTestDisableAssertions(RTTEST hTest)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN(pTest);
+
+ uint32_t cTimes = ASMAtomicIncU32(&pTest->cAssertionsDisabledAndQuieted);
+ if (cTimes >= 2 && cTimes <= 8)
+ return VINF_SUCCESS;
+ if (cTimes > 8)
+ {
+ RTAssertSetMayPanic(pTest->fAssertSavedMayPanic);
+ RTAssertSetQuiet(pTest->fAssertSavedQuiet);
+ Assert(cTimes <= 8);
+ }
+ pTest->fAssertSavedMayPanic = RTAssertSetMayPanic(false);
+ pTest->fAssertSavedQuiet = RTAssertSetQuiet(true);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTTestRestoreAssertions(RTTEST hTest)
+{
+ PRTTESTINT pTest = hTest;
+ RTTEST_GET_VALID_RETURN(pTest);
+
+ uint32_t cTimes = ASMAtomicDecU32(&pTest->cAssertionsDisabledAndQuieted);
+ if (cTimes == 0)
+ {
+ RTAssertSetMayPanic(pTest->fAssertSavedMayPanic);
+ RTAssertSetQuiet(pTest->fAssertSavedQuiet);
+ }
+ else
+ AssertStmt(cTimes < UINT32_MAX / 2, ASMAtomicIncU32(&pTest->cAssertionsDisabledAndQuieted));
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/testi.cpp b/src/VBox/Runtime/r3/testi.cpp
new file mode 100644
index 00000000..28daa7bc
--- /dev/null
+++ b/src/VBox/Runtime/r3/testi.cpp
@@ -0,0 +1,214 @@
+/* $Id: testi.cpp $ */
+/** @file
+ * IPRT - Testcase Framework, the implicit test handle API variation.
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/test.h>
+#include <iprt/stdarg.h>
+
+
+RTR3DECL(int) RTTestIPrintfV(RTTESTLVL enmLevel, const char *pszFormat, va_list va)
+{
+ return RTTestPrintfV(NIL_RTTEST, enmLevel, pszFormat, va);
+}
+
+
+RTR3DECL(int) RTTestIPrintf(RTTESTLVL enmLevel, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int cch = RTTestPrintfV(NIL_RTTEST, enmLevel, pszFormat, va);
+ va_end(va);
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestISub(const char *pszSubTest)
+{
+ return RTTestSub(NIL_RTTEST, pszSubTest);
+}
+
+
+RTR3DECL(int) RTTestISubF(const char *pszSubTestFmt, ...)
+{
+ va_list va;
+ va_start(va, pszSubTestFmt);
+ int cch = RTTestSubV(NIL_RTTEST, pszSubTestFmt, va);
+ va_end(va);
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestISubV(const char *pszSubTestFmt, va_list va)
+{
+ return RTTestSubV(NIL_RTTEST, pszSubTestFmt, va);
+}
+
+
+RTR3DECL(int) RTTestISubDone(void)
+{
+ return RTTestSubDone(NIL_RTTEST);
+}
+
+
+RTR3DECL(int) RTTestIPassedV(const char *pszFormat, va_list va)
+{
+ return RTTestPassedV(NIL_RTTEST, pszFormat, va);
+}
+
+
+RTR3DECL(int) RTTestIPassed(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int cch = RTTestPassedV(NIL_RTTEST, pszFormat, va);
+ va_end(va);
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestIValue(const char *pszName, uint64_t u64Value, RTTESTUNIT enmUnit)
+{
+ return RTTestValue(NIL_RTTEST, pszName, u64Value, enmUnit);
+}
+
+
+RTR3DECL(int) RTTestIValueF(uint64_t u64Value, RTTESTUNIT enmUnit, const char *pszNameFmt, ...)
+{
+ va_list va;
+ va_start(va, pszNameFmt);
+ int rc = RTTestValueV(NIL_RTTEST, u64Value, enmUnit, pszNameFmt, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTR3DECL(int) RTTestIValueV(uint64_t u64Value, RTTESTUNIT enmUnit, const char *pszNameFmt, va_list va)
+{
+ return RTTestValueV(NIL_RTTEST, u64Value, enmUnit, pszNameFmt, va);
+}
+
+
+RTR3DECL(int) RTTestIErrorInc(void)
+{
+ return RTTestErrorInc(NIL_RTTEST);
+}
+
+
+RTR3DECL(uint32_t) RTTestIErrorCount(void)
+{
+ return RTTestErrorCount(NIL_RTTEST);
+}
+
+
+RTR3DECL(int) RTTestIFailedV(const char *pszFormat, va_list va)
+{
+ return RTTestFailedV(NIL_RTTEST, pszFormat, va);
+}
+
+
+RTR3DECL(int) RTTestIFailed(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int cch = RTTestFailedV(NIL_RTTEST, pszFormat, va);
+ va_end(va);
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestIFailedRcV(int rcRet, const char *pszFormat, va_list va)
+{
+ RTTestFailedV(NIL_RTTEST, pszFormat, va);
+ return rcRet;
+}
+
+
+RTR3DECL(int) RTTestIFailedRc(int rcRet, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ RTTestFailedV(NIL_RTTEST, pszFormat, va);
+ va_end(va);
+ return rcRet;
+}
+
+
+RTR3DECL(int) RTTestIFailureDetailsV(const char *pszFormat, va_list va)
+{
+ return RTTestFailureDetails(NIL_RTTEST, pszFormat, va);
+}
+
+
+RTR3DECL(int) RTTestIFailureDetails(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int cch = RTTestFailureDetailsV(NIL_RTTEST, pszFormat, va);
+ va_end(va);
+ return cch;
+}
+
+
+RTR3DECL(int) RTTestIErrContextV(const char *pszFormat, va_list va)
+{
+ return RTTestErrContextV(NIL_RTTEST, pszFormat, va);
+}
+
+
+RTR3DECL(int) RTTestIErrContext(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTTestErrContextV(NIL_RTTEST, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTR3DECL(int) RTTestIDisableAssertions(void)
+{
+ return RTTestDisableAssertions(NIL_RTTEST);
+}
+
+
+RTR3DECL(int) RTTestIRestoreAssertions(void)
+{
+ return RTTestRestoreAssertions(NIL_RTTEST);
+}
+
diff --git a/src/VBox/Runtime/r3/udp.cpp b/src/VBox/Runtime/r3/udp.cpp
new file mode 100644
index 00000000..2ed12047
--- /dev/null
+++ b/src/VBox/Runtime/r3/udp.cpp
@@ -0,0 +1,726 @@
+/* $Id: udp.cpp $ */
+/** @file
+ * IPRT - UDP/IP.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/winsock2.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <errno.h>
+# include <netinet/in.h>
+# include <netinet/udp.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+#endif
+#include <limits.h>
+
+#include "internal/iprt.h"
+#include <iprt/udp.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mempool.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/socket.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "internal/magics.h"
+#include "internal/socket.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* fixup backlevel OSes. */
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+# define socklen_t int
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * UDP Server state.
+ */
+typedef enum RTUDPSERVERSTATE
+{
+ /** Invalid. */
+ RTUDPSERVERSTATE_INVALID = 0,
+ /** Created. */
+ RTUDPSERVERSTATE_CREATED,
+ /** Thread for incoming datagrams is starting up. */
+ RTUDPSERVERSTATE_STARTING,
+ /** Waiting for incoming datagrams. */
+ RTUDPSERVERSTATE_WAITING,
+ /** Handling an incoming datagram. */
+ RTUDPSERVERSTATE_RECEIVING,
+ /** Thread terminating. */
+ RTUDPSERVERSTATE_STOPPING,
+ /** Thread terminated. */
+ RTUDPSERVERSTATE_STOPPED,
+ /** Final cleanup before being unusable. */
+ RTUDPSERVERSTATE_DESTROYING
+} RTUDPSERVERSTATE;
+
+/*
+ * Internal representation of the UDP Server handle.
+ */
+typedef struct RTUDPSERVER
+{
+ /** The magic value (RTUDPSERVER_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The server state. */
+ RTUDPSERVERSTATE volatile enmState;
+ /** The server thread. */
+ RTTHREAD Thread;
+ /** The server socket. */
+ RTSOCKET volatile hSocket;
+ /** The datagram receiver function. */
+ PFNRTUDPSERVE pfnServe;
+ /** Argument to pfnServer. */
+ void *pvUser;
+} RTUDPSERVER;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) rtUdpServerThread(RTTHREAD ThreadSelf, void *pvServer);
+static int rtUdpServerListen(PRTUDPSERVER pServer);
+static int rtUdpServerListenCleanup(PRTUDPSERVER pServer);
+static int rtUdpServerDestroySocket(RTSOCKET volatile *pSock, const char *pszMsg);
+static int rtUdpClose(RTSOCKET Sock, const char *pszMsg);
+
+
+/**
+ * Atomicly updates a socket variable.
+ * @returns The old handle value.
+ * @param phSock The socket handle variable to update.
+ * @param hNew The new socket handle value.
+ */
+DECLINLINE(RTSOCKET) rtUdpAtomicXchgSock(RTSOCKET volatile *phSock, const RTSOCKET hNew)
+{
+ RTSOCKET hRet;
+ ASMAtomicXchgHandle(phSock, hNew, &hRet);
+ return hRet;
+}
+
+
+/**
+ * Tries to change the UDP server state.
+ */
+DECLINLINE(bool) rtUdpServerTrySetState(PRTUDPSERVER pServer, RTUDPSERVERSTATE enmStateNew, RTUDPSERVERSTATE enmStateOld)
+{
+ bool fRc;
+ ASMAtomicCmpXchgSize(&pServer->enmState, enmStateNew, enmStateOld, fRc);
+ return fRc;
+}
+
+/**
+ * Changes the UDP server state.
+ */
+DECLINLINE(void) rtUdpServerSetState(PRTUDPSERVER pServer, RTUDPSERVERSTATE enmStateNew, RTUDPSERVERSTATE enmStateOld)
+{
+ bool fRc;
+ ASMAtomicCmpXchgSize(&pServer->enmState, enmStateNew, enmStateOld, fRc);
+ Assert(fRc); NOREF(fRc);
+}
+
+
+/**
+ * Closes a socket.
+ *
+ * @returns IPRT status code.
+ */
+static int rtUdpServerDestroySocket(RTSOCKET volatile *pSock, const char *pszMsg)
+{
+ RTSOCKET hSocket = rtUdpAtomicXchgSock(pSock, NIL_RTSOCKET);
+ if (hSocket != NIL_RTSOCKET)
+ {
+ return rtUdpClose(hSocket, pszMsg);
+ }
+ return VINF_UDP_SERVER_NO_CLIENT;
+}
+
+
+RTR3DECL(int) RTUdpServerCreate(const char *pszAddress, unsigned uPort, RTTHREADTYPE enmType, const char *pszThrdName,
+ PFNRTUDPSERVE pfnServe, void *pvUser, PPRTUDPSERVER ppServer)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(uPort > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfnServe, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszThrdName, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppServer, VERR_INVALID_POINTER);
+
+ /*
+ * Create the server.
+ */
+ PRTUDPSERVER pServer;
+ int rc = RTUdpServerCreateEx(pszAddress, uPort, &pServer);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the listener thread.
+ */
+ RTMemPoolRetain(pServer);
+ pServer->enmState = RTUDPSERVERSTATE_STARTING;
+ pServer->pvUser = pvUser;
+ pServer->pfnServe = pfnServe;
+ rc = RTThreadCreate(&pServer->Thread, rtUdpServerThread, pServer, 0, enmType, /*RTTHREADFLAGS_WAITABLE*/0, pszThrdName);
+ if (RT_SUCCESS(rc))
+ {
+ /* done */
+ if (ppServer)
+ *ppServer = pServer;
+ else
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return rc;
+ }
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+
+ /*
+ * Destroy the server.
+ */
+ rtUdpServerSetState(pServer, RTUDPSERVERSTATE_CREATED, RTUDPSERVERSTATE_STARTING);
+ RTUdpServerDestroy(pServer);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Server thread, loops waiting for datagrams until it's terminated.
+ *
+ * @returns iprt status code. (ignored).
+ * @param ThreadSelf Thread handle.
+ * @param pvServer Server handle.
+ */
+static DECLCALLBACK(int) rtUdpServerThread(RTTHREAD ThreadSelf, void *pvServer)
+{
+ PRTUDPSERVER pServer = (PRTUDPSERVER)pvServer;
+ int rc;
+ if (rtUdpServerTrySetState(pServer, RTUDPSERVERSTATE_WAITING, RTUDPSERVERSTATE_STARTING))
+ rc = rtUdpServerListen(pServer);
+ else
+ rc = rtUdpServerListenCleanup(pServer);
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ NOREF(ThreadSelf);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTUdpServerCreateEx(const char *pszAddress, uint32_t uPort, PPRTUDPSERVER ppServer)
+{
+
+ /*
+ * Validate input.
+ */
+ AssertReturn(uPort > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppServer, VERR_INVALID_PARAMETER);
+
+ /*
+ * Resolve the address.
+ */
+ RTNETADDR LocalAddr;
+ int rc = RTSocketParseInetAddress(pszAddress, uPort, &LocalAddr);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Setting up socket.
+ */
+ RTSOCKET Sock;
+ rc = rtSocketCreate(&Sock, AF_INET, SOCK_DGRAM, IPPROTO_UDP, false /*fInheritable*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set socket options.
+ */
+ int fFlag = 1;
+ if (!rtSocketSetOpt(Sock, SOL_SOCKET, SO_REUSEADDR, &fFlag, sizeof(fFlag)))
+ {
+ /*
+ * Bind a name to the socket.
+ */
+ rc = rtSocketBind(Sock, &LocalAddr);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the server handle.
+ */
+ PRTUDPSERVER pServer = (PRTUDPSERVER)RTMemPoolAlloc(RTMEMPOOL_DEFAULT, sizeof(*pServer));
+ if (pServer)
+ {
+ pServer->u32Magic = RTUDPSERVER_MAGIC;
+ pServer->enmState = RTUDPSERVERSTATE_CREATED;
+ pServer->Thread = NIL_RTTHREAD;
+ pServer->hSocket = Sock;
+ pServer->pfnServe = NULL;
+ pServer->pvUser = NULL;
+ *ppServer = pServer;
+ return VINF_SUCCESS;
+ }
+
+ /* bail out */
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ AssertMsgFailed(("rtSocketSetOpt: %Rrc\n", rc));
+ rtUdpClose(Sock, "RTServerCreateEx");
+ }
+
+ return rc;
+}
+
+
+RTR3DECL(int) RTUdpServerListen(PRTUDPSERVER pServer, PFNRTUDPSERVE pfnServe, void *pvUser)
+{
+ /*
+ * Validate input and retain the instance.
+ */
+ AssertPtrReturn(pfnServe, VERR_INVALID_POINTER);
+ AssertPtrReturn(pServer, VERR_INVALID_HANDLE);
+ AssertReturn(pServer->u32Magic == RTUDPSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRetain(pServer) != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ int rc = VERR_INVALID_STATE;
+ if (rtUdpServerTrySetState(pServer, RTUDPSERVERSTATE_WAITING, RTUDPSERVERSTATE_CREATED))
+ {
+ Assert(!pServer->pfnServe);
+ Assert(!pServer->pvUser);
+ Assert(pServer->Thread == NIL_RTTHREAD);
+
+ pServer->pfnServe = pfnServe;
+ pServer->pvUser = pvUser;
+ pServer->Thread = RTThreadSelf();
+ Assert(pServer->Thread != NIL_RTTHREAD);
+ rc = rtUdpServerListen(pServer);
+ }
+ else
+ {
+ AssertMsgFailed(("enmState=%d\n", pServer->enmState));
+ rc = VERR_INVALID_STATE;
+ }
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return rc;
+}
+
+
+/**
+ * Internal worker common for RTUdpServerListen and the thread created by
+ * RTUdpServerCreate().
+ *
+ * The caller makes sure it has its own memory reference and releases it upon
+ * return.
+ */
+static int rtUdpServerListen(PRTUDPSERVER pServer)
+{
+ /*
+ * Wait for incoming datagrams loop.
+ */
+ for (;;)
+ {
+ /*
+ * Change state, getting an extra reference to the socket so we can
+ * allow others to close it while we're stuck in rtSocketAccept.
+ */
+ RTUDPSERVERSTATE enmState = pServer->enmState;
+ RTSOCKET hSocket;
+ ASMAtomicReadHandle(&pServer->hSocket, &hSocket);
+ if (hSocket != NIL_RTSOCKET)
+ RTSocketRetain(hSocket);
+ if ( enmState != RTUDPSERVERSTATE_WAITING
+ && enmState != RTUDPSERVERSTATE_RECEIVING)
+ {
+ RTSocketRelease(hSocket);
+ return rtUdpServerListenCleanup(pServer);
+ }
+ if (!rtUdpServerTrySetState(pServer, RTUDPSERVERSTATE_WAITING, enmState))
+ {
+ RTSocketRelease(hSocket);
+ continue;
+ }
+
+ /*
+ * Wait for incoming datagrams or errors.
+ */
+ uint32_t fEvents;
+ int rc = RTSocketSelectOneEx(hSocket, RTSOCKET_EVT_READ | RTSOCKET_EVT_ERROR, &fEvents, 1000);
+ RTSocketRelease(hSocket);
+ if (rc == VERR_TIMEOUT)
+ continue;
+ if (RT_FAILURE(rc))
+ {
+ /* These are typical for what can happen during destruction. */
+ if ( rc == VERR_INVALID_HANDLE
+ || rc == VERR_INVALID_PARAMETER
+ || rc == VERR_NET_NOT_SOCKET)
+ return rtUdpServerListenCleanup(pServer);
+ continue;
+ }
+ if (fEvents & RTSOCKET_EVT_ERROR)
+ return rtUdpServerListenCleanup(pServer);
+
+ /*
+ * Run a pfnServe callback.
+ */
+ if (!rtUdpServerTrySetState(pServer, RTUDPSERVERSTATE_RECEIVING, RTUDPSERVERSTATE_WAITING))
+ return rtUdpServerListenCleanup(pServer);
+ rc = pServer->pfnServe(hSocket, pServer->pvUser);
+
+ /*
+ * Stop the server?
+ */
+ if (rc == VERR_UDP_SERVER_STOP)
+ {
+ if (rtUdpServerTrySetState(pServer, RTUDPSERVERSTATE_STOPPING, RTUDPSERVERSTATE_RECEIVING))
+ {
+ /*
+ * Reset the server socket and change the state to stopped. After that state change
+ * we cannot safely access the handle so we'll have to return here.
+ */
+ hSocket = rtUdpAtomicXchgSock(&pServer->hSocket, NIL_RTSOCKET);
+ rtUdpServerSetState(pServer, RTUDPSERVERSTATE_STOPPED, RTUDPSERVERSTATE_STOPPING);
+ rtUdpClose(hSocket, "Listener: server stopped");
+ }
+ else
+ rtUdpServerListenCleanup(pServer); /* ignore rc */
+ return rc;
+ }
+ }
+}
+
+
+/**
+ * Clean up after listener.
+ */
+static int rtUdpServerListenCleanup(PRTUDPSERVER pServer)
+{
+ /*
+ * Close the server socket.
+ */
+ rtUdpServerDestroySocket(&pServer->hSocket, "ListenCleanup");
+
+ /*
+ * Figure the return code and make sure the state is OK.
+ */
+ RTUDPSERVERSTATE enmState = pServer->enmState;
+ switch (enmState)
+ {
+ case RTUDPSERVERSTATE_STOPPING:
+ case RTUDPSERVERSTATE_STOPPED:
+ return VERR_UDP_SERVER_SHUTDOWN;
+
+ case RTUDPSERVERSTATE_WAITING:
+ rtUdpServerTrySetState(pServer, RTUDPSERVERSTATE_STOPPED, enmState);
+ return VERR_UDP_SERVER_DESTROYED;
+
+ case RTUDPSERVERSTATE_DESTROYING:
+ return VERR_UDP_SERVER_DESTROYED;
+
+ case RTUDPSERVERSTATE_STARTING:
+ case RTUDPSERVERSTATE_RECEIVING:
+ default:
+ AssertMsgFailedReturn(("pServer=%p enmState=%d\n", pServer, enmState), VERR_INTERNAL_ERROR_4);
+ }
+}
+
+
+RTR3DECL(int) RTUdpServerShutdown(PRTUDPSERVER pServer)
+{
+ /*
+ * Validate input and retain the instance.
+ */
+ AssertPtrReturn(pServer, VERR_INVALID_HANDLE);
+ AssertReturn(pServer->u32Magic == RTUDPSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRetain(pServer) != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ /*
+ * Try change the state to stopping, then replace and destroy the server socket.
+ */
+ for (;;)
+ {
+ RTUDPSERVERSTATE enmState = pServer->enmState;
+ if ( enmState != RTUDPSERVERSTATE_WAITING
+ && enmState != RTUDPSERVERSTATE_RECEIVING)
+ {
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ switch (enmState)
+ {
+ case RTUDPSERVERSTATE_CREATED:
+ case RTUDPSERVERSTATE_STARTING:
+ default:
+ AssertMsgFailed(("%d\n", enmState));
+ return VERR_INVALID_STATE;
+
+ case RTUDPSERVERSTATE_STOPPING:
+ case RTUDPSERVERSTATE_STOPPED:
+ return VINF_SUCCESS;
+
+ case RTUDPSERVERSTATE_DESTROYING:
+ return VERR_UDP_SERVER_DESTROYED;
+ }
+ }
+ if (rtUdpServerTrySetState(pServer, RTUDPSERVERSTATE_STOPPING, enmState))
+ {
+ rtUdpServerDestroySocket(&pServer->hSocket, "RTUdpServerShutdown");
+ rtUdpServerSetState(pServer, RTUDPSERVERSTATE_STOPPED, RTUDPSERVERSTATE_STOPPING);
+
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return VINF_SUCCESS;
+ }
+ }
+}
+
+
+RTR3DECL(int) RTUdpServerDestroy(PRTUDPSERVER pServer)
+{
+ /*
+ * Validate input and retain the instance.
+ */
+ AssertPtrReturn(pServer, VERR_INVALID_HANDLE);
+ AssertReturn(pServer->u32Magic == RTUDPSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRetain(pServer) != UINT32_MAX, VERR_INVALID_HANDLE); /* paranoia */
+
+ /*
+ * Move the state along so the listener can figure out what's going on.
+ */
+ for (;;)
+ {
+ bool fDestroyable;
+ RTUDPSERVERSTATE enmState = pServer->enmState;
+ switch (enmState)
+ {
+ case RTUDPSERVERSTATE_STARTING:
+ case RTUDPSERVERSTATE_WAITING:
+ case RTUDPSERVERSTATE_RECEIVING:
+ case RTUDPSERVERSTATE_CREATED:
+ case RTUDPSERVERSTATE_STOPPED:
+ fDestroyable = rtUdpServerTrySetState(pServer, RTUDPSERVERSTATE_DESTROYING, enmState);
+ break;
+
+ /* destroyable states */
+ case RTUDPSERVERSTATE_STOPPING:
+ fDestroyable = true;
+ break;
+
+ /*
+ * Everything else means user or internal misbehavior.
+ */
+ default:
+ AssertMsgFailed(("pServer=%p enmState=%d\n", pServer, enmState));
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return VERR_INTERNAL_ERROR;
+ }
+ if (fDestroyable)
+ break;
+ }
+
+ /*
+ * Destroy it.
+ */
+ ASMAtomicWriteU32(&pServer->u32Magic, ~RTUDPSERVER_MAGIC);
+ rtUdpServerDestroySocket(&pServer->hSocket, "Destroyer: server");
+
+ /*
+ * Release it.
+ */
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Internal close function which does all the proper bitching.
+ */
+static int rtUdpClose(RTSOCKET Sock, const char *pszMsg)
+{
+ NOREF(pszMsg); /** @todo drop this parameter? */
+
+ /* ignore nil handles. */
+ if (Sock == NIL_RTSOCKET)
+ return VINF_SUCCESS;
+
+ /*
+ * Close the socket handle (drops our reference to it).
+ */
+ return RTSocketClose(Sock);
+}
+
+
+RTR3DECL(int) RTUdpRead(RTSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead, PRTNETADDR pSrcAddr)
+{
+ if (!RT_VALID_PTR(pcbRead))
+ return VERR_INVALID_POINTER;
+ return RTSocketReadFrom(Sock, pvBuffer, cbBuffer, pcbRead, pSrcAddr);
+}
+
+
+RTR3DECL(int) RTUdpWrite(PRTUDPSERVER pServer, const void *pvBuffer, size_t cbBuffer, PCRTNETADDR pDstAddr)
+{
+ /*
+ * Validate input and retain the instance.
+ */
+ AssertPtrReturn(pServer, VERR_INVALID_HANDLE);
+ AssertReturn(pServer->u32Magic == RTUDPSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTMemPoolRetain(pServer) != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ RTSOCKET hSocket;
+ ASMAtomicReadHandle(&pServer->hSocket, &hSocket);
+ if (hSocket == NIL_RTSOCKET)
+ {
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+ return VERR_INVALID_HANDLE;
+ }
+ RTSocketRetain(hSocket);
+
+ int rc = VINF_SUCCESS;
+ RTUDPSERVERSTATE enmState = pServer->enmState;
+ if ( enmState != RTUDPSERVERSTATE_CREATED
+ && enmState != RTUDPSERVERSTATE_STARTING
+ && enmState != RTUDPSERVERSTATE_WAITING
+ && enmState != RTUDPSERVERSTATE_RECEIVING
+ && enmState != RTUDPSERVERSTATE_STOPPING)
+ rc = VERR_INVALID_STATE;
+
+ if (RT_SUCCESS(rc))
+ rc = RTSocketWriteTo(hSocket, pvBuffer, cbBuffer, pDstAddr);
+
+ RTSocketRelease(hSocket);
+ RTMemPoolRelease(RTMEMPOOL_DEFAULT, pServer);
+
+ return rc;
+}
+
+
+RTR3DECL(int) RTUdpCreateClientSocket(const char *pszAddress, uint32_t uPort, PRTNETADDR pLocalAddr, PRTSOCKET pSock)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(uPort > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSock, VERR_INVALID_POINTER);
+
+ /*
+ * Resolve the address.
+ */
+ RTNETADDR Addr;
+ int rc = RTSocketParseInetAddress(pszAddress, uPort, &Addr);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Create the socket and connect.
+ */
+ RTSOCKET Sock;
+ rc = rtSocketCreate(&Sock, AF_INET, SOCK_DGRAM, 0, false /*fInheritable*/);
+ if (RT_SUCCESS(rc))
+ {
+ if (pLocalAddr)
+ rc = rtSocketBind(Sock, pLocalAddr);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtSocketConnect(Sock, &Addr, RT_SOCKETCONNECT_DEFAULT_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ *pSock = Sock;
+ return VINF_SUCCESS;
+ }
+ }
+ RTSocketClose(Sock);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTUdpCreateServerSocket(const char *pszAddress, uint32_t uPort, PRTSOCKET pSock)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(uPort > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSock, VERR_INVALID_POINTER);
+
+ /*
+ * Resolve the address.
+ */
+ RTNETADDR LocalAddr;
+ int rc = RTSocketParseInetAddress(pszAddress, uPort, &LocalAddr);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Setting up socket.
+ */
+ RTSOCKET Sock;
+ rc = rtSocketCreate(&Sock, AF_INET, SOCK_DGRAM, IPPROTO_UDP, false /*fInheritable*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set socket options.
+ */
+ int fFlag = 1;
+ if (!rtSocketSetOpt(Sock, SOL_SOCKET, SO_REUSEADDR, &fFlag, sizeof(fFlag)))
+ {
+ /*
+ * Bind a name to the socket.
+ */
+ rc = rtSocketBind(Sock, &LocalAddr);
+ if (RT_SUCCESS(rc))
+ {
+ *pSock = Sock;
+ return VINF_SUCCESS;
+ }
+ }
+ RTSocketClose(Sock);
+ }
+ return rc;
+}
diff --git a/src/VBox/Runtime/r3/win/Makefile.kup b/src/VBox/Runtime/r3/win/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/Makefile.kup
diff --git a/src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp b/src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp
new file mode 100644
index 00000000..1eead2b1
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp
@@ -0,0 +1,194 @@
+/* $Id: RTCrStoreCreateSnapshotById-win.cpp $ */
+/** @file
+ * IPRT - RTCrStoreCreateSnapshotById, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/crypto/store.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/once.h>
+#include <iprt/ldr.h>
+
+#include <iprt/win/windows.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef HCERTSTORE (WINAPI *PFNCERTOPENSTORE)(PCSTR pszStoreProvider, DWORD dwEncodingType, HCRYPTPROV_LEGACY hCryptProv,
+ DWORD dwFlags, const void *pvParam);
+typedef BOOL (WINAPI *PFNCERTCLOSESTORE)(HCERTSTORE hCertStore, DWORD dwFlags);
+typedef PCCERT_CONTEXT (WINAPI *PFNCERTENUMCERTIFICATESINSTORE)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
+
+
+
+static int rtCrStoreAddCertsFromNative(RTCRSTORE hStore, DWORD fStore, PCRTUTF16 pwszStoreName,
+ PFNCERTOPENSTORE pfnOpenStore, PFNCERTCLOSESTORE pfnCloseStore,
+ PFNCERTENUMCERTIFICATESINSTORE pfnEnumCerts, int rc, PRTERRINFO pErrInfo)
+{
+ DWORD fOpenStore = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
+ HCERTSTORE hNativeStore = pfnOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ NULL /* hCryptProv = default */, fStore | fOpenStore, pwszStoreName);
+ if (hNativeStore)
+ {
+ PCCERT_CONTEXT pCurCtx = NULL;
+ while ((pCurCtx = pfnEnumCerts(hNativeStore, pCurCtx)) != NULL)
+ {
+ if (pCurCtx->dwCertEncodingType & X509_ASN_ENCODING)
+ {
+ RTERRINFOSTATIC StaticErrInfo;
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded,
+ RTErrInfoInitStatic(&StaticErrInfo),
+ &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
+ RTCRX509CERTIFICATE MyCert;
+ int rc2 = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &MyCert, "Cert");
+ if (RT_SUCCESS(rc2))
+ {
+ rc2 = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_X509_DER | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
+ pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded,
+ RTErrInfoInitStatic(&StaticErrInfo));
+ RTCrX509Certificate_Delete(&MyCert);
+ }
+ if (RT_FAILURE(rc2))
+ {
+ if (RTErrInfoIsSet(&StaticErrInfo.Core))
+ RTErrInfoAddF(pErrInfo, -rc2, " %s", StaticErrInfo.Core.pszMsg);
+ else
+ RTErrInfoAddF(pErrInfo, -rc2, " %Rrc adding cert", rc2);
+ rc = -rc2;
+ }
+ }
+ }
+ pfnCloseStore(hNativeStore, CERT_CLOSE_STORE_CHECK_FLAG);
+ }
+ else
+ {
+ DWORD uLastErr = GetLastError();
+ if (uLastErr != ERROR_FILE_NOT_FOUND)
+ rc = RTErrInfoAddF(pErrInfo, -RTErrConvertFromWin32(uLastErr),
+ " CertOpenStore(%#x,'%ls') failed: %u", fStore, pwszStoreName);
+ }
+ return rc;
+}
+
+
+
+RTDECL(int) RTCrStoreCreateSnapshotById(PRTCRSTORE phStore, RTCRSTOREID enmStoreId, PRTERRINFO pErrInfo)
+{
+ AssertReturn(enmStoreId > RTCRSTOREID_INVALID && enmStoreId < RTCRSTOREID_END, VERR_INVALID_PARAMETER);
+
+ /*
+ * Create an empty in-memory store.
+ */
+ RTCRSTORE hStore;
+ int rc = RTCrStoreCreateInMem(&hStore, 128);
+ if (RT_SUCCESS(rc))
+ {
+ *phStore = hStore;
+
+ /*
+ * Resolve the APIs we need to do this job.
+ */
+ RTLDRMOD hLdrMod;
+ int rc2 = RTLdrLoadSystem("crypt32.dll", false /*NoUnload*/, &hLdrMod);
+ if (RT_SUCCESS(rc2))
+ {
+ PFNCERTOPENSTORE pfnOpenStore = NULL;
+ rc2 = RTLdrGetSymbol(hLdrMod, "CertOpenStore", (void **)&pfnOpenStore);
+
+ PFNCERTCLOSESTORE pfnCloseStore = NULL;
+ if (RT_SUCCESS(rc2))
+ rc2 = RTLdrGetSymbol(hLdrMod, "CertCloseStore", (void **)&pfnCloseStore);
+
+ PFNCERTENUMCERTIFICATESINSTORE pfnEnumCerts = NULL;
+ if (RT_SUCCESS(rc2))
+ rc2 = RTLdrGetSymbol(hLdrMod, "CertEnumCertificatesInStore", (void **)&pfnEnumCerts);
+ if (RT_SUCCESS(rc2))
+ {
+ /*
+ * Do the work.
+ */
+ DWORD fStore = CERT_SYSTEM_STORE_CURRENT_USER;
+ switch (enmStoreId)
+ {
+ case RTCRSTOREID_SYSTEM_TRUSTED_CAS_AND_CERTIFICATES:
+ case RTCRSTOREID_SYSTEM_INTERMEDIATE_CAS:
+ fStore = CERT_SYSTEM_STORE_LOCAL_MACHINE;
+ RT_FALL_THRU();
+ case RTCRSTOREID_USER_TRUSTED_CAS_AND_CERTIFICATES:
+ case RTCRSTOREID_USER_INTERMEDIATE_CAS:
+ {
+ /** @todo CA and MY in s_apwszRootStores are _very_ questionable!!! However,
+ * curl may need them to work correct and it doesn't seem to have any
+ * intermediate ca file. :/ */
+ static PCRTUTF16 const s_apwszRootStores[] = { L"AuthRoot", L"CA", L"MY", L"Root" };
+ static PCRTUTF16 const s_apwszIntermediateStores[] = { L"CA", L"MY" };
+ PCRTUTF16 const *papwszStores = s_apwszRootStores;
+ uint32_t cStores = RT_ELEMENTS(s_apwszRootStores);
+ if (enmStoreId == RTCRSTOREID_USER_INTERMEDIATE_CAS || enmStoreId == RTCRSTOREID_SYSTEM_INTERMEDIATE_CAS)
+ {
+ papwszStores = s_apwszIntermediateStores;
+ cStores = RT_ELEMENTS(s_apwszIntermediateStores);
+ }
+
+ for (uint32_t i = 0; i < cStores; i++)
+ rc = rtCrStoreAddCertsFromNative(hStore, fStore, papwszStores[i], pfnOpenStore, pfnCloseStore,
+ pfnEnumCerts, rc, pErrInfo);
+ break;
+ }
+
+ default:
+ AssertFailed(); /* implement me */
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, -rc2, "Error resolving crypt32.dll APIs");
+ RTLdrClose(hLdrMod);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, -rc2, "Error loading crypt32.dll");
+ }
+ else
+ RTErrInfoSet(pErrInfo, rc, "RTCrStoreCreateInMem failed");
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCreateSnapshotById);
+
diff --git a/src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp b/src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp
new file mode 100644
index 00000000..97a38271
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp
@@ -0,0 +1,71 @@
+/* $Id: RTFileQuerySectorSize-win.cpp $ */
+/** @file
+ * IPRT - RTFileQuerySectorSize, Windows.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+#include <iprt/win/windows.h>
+
+
+
+RTDECL(int) RTFileQuerySectorSize(RTFILE hFile, uint32_t *pcbSector)
+{
+ AssertPtrReturn(pcbSector, VERR_INVALID_PARAMETER);
+
+ DISK_GEOMETRY DriveGeo;
+ RT_ZERO(DriveGeo);
+ DWORD cbDriveGeo = 0;
+ if (DeviceIoControl((HANDLE)RTFileToNative(hFile),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
+ &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
+ {
+ AssertReturn(DriveGeo.BytesPerSector > 0, VERR_INVALID_PARAMETER);
+ *pcbSector = DriveGeo.BytesPerSector;
+ return VINF_SUCCESS;
+ }
+ int rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsg(rc == VERR_IO_NOT_READY, ("%d / %Rrc\n", GetLastError(), rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp b/src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp
new file mode 100644
index 00000000..c0b7f780
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp
@@ -0,0 +1,143 @@
+/* $Id: RTHandleGetStandard-win.cpp $ */
+/** @file
+ * IPRT - RTHandleGetStandard, Windows.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/handle.h>
+
+#include <iprt/file.h>
+#include <iprt/pipe.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+
+#include <iprt/win/windows.h>
+
+#include "internal/socket.h" /* (Needs Windows.h.) */
+#include "internal-r3-win.h" /* (Needs Windows.h.) */
+
+
+RTDECL(int) RTHandleGetStandard(RTHANDLESTD enmStdHandle, bool fLeaveOpen, PRTHANDLE ph)
+{
+ /*
+ * Validate and convert input.
+ */
+ AssertPtrReturn(ph, VERR_INVALID_POINTER);
+ DWORD dwStdHandle;
+ switch (enmStdHandle)
+ {
+ case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break;
+ case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break;
+ case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Is the requested descriptor valid and which IPRT handle type does it
+ * best map on to?
+ */
+ HANDLE hNative = GetStdHandle(dwStdHandle);
+ if (hNative == INVALID_HANDLE_VALUE)
+ return RTErrConvertFromWin32(GetLastError());
+
+ DWORD dwInfo = 0;
+ if (g_pfnGetHandleInformation && !g_pfnGetHandleInformation(hNative, &dwInfo))
+ return RTErrConvertFromWin32(GetLastError());
+ bool const fInherit = RT_BOOL(dwInfo & HANDLE_FLAG_INHERIT);
+
+ SetLastError(NO_ERROR);
+ RTHANDLE h;
+ DWORD dwType = GetFileType(hNative);
+ switch (dwType & ~FILE_TYPE_REMOTE)
+ {
+ case FILE_TYPE_UNKNOWN:
+ if (GetLastError() != NO_ERROR)
+ return RTErrConvertFromWin32(GetLastError());
+ RT_FALL_THROUGH();
+ default:
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_DISK:
+ h.enmType = RTHANDLETYPE_FILE;
+ break;
+
+ case FILE_TYPE_PIPE:
+ {
+ DWORD cMaxInstances;
+ DWORD fInfo;
+ if (!GetNamedPipeInfo(hNative, &fInfo, NULL, NULL, &cMaxInstances))
+ h.enmType = RTHANDLETYPE_SOCKET;
+ else
+ h.enmType = RTHANDLETYPE_PIPE;
+ break;
+ }
+ }
+
+ /*
+ * Create the IPRT handle.
+ */
+ int rc;
+ switch (h.enmType)
+ {
+ case RTHANDLETYPE_FILE:
+ /** @todo fLeaveOpen */
+ rc = RTFileFromNative(&h.u.hFile, (RTHCUINTPTR)hNative);
+ break;
+
+ case RTHANDLETYPE_PIPE:
+ rc = RTPipeFromNative(&h.u.hPipe, (RTHCUINTPTR)hNative,
+ (enmStdHandle == RTHANDLESTD_INPUT ? RTPIPE_N_READ : RTPIPE_N_WRITE)
+ | (fInherit ? RTPIPE_N_INHERIT : 0)
+ | (fLeaveOpen ? RTPIPE_N_LEAVE_OPEN : 0));
+ break;
+
+ case RTHANDLETYPE_SOCKET:
+ rc = rtSocketCreateForNative(&h.u.hSocket, (RTHCUINTPTR)hNative, fLeaveOpen);
+ break;
+
+ default: /* shut up gcc */
+ return VERR_INTERNAL_ERROR;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ph = h;
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp b/src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp
new file mode 100644
index 00000000..10efd342
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp
@@ -0,0 +1,116 @@
+/* $Id: RTLocaleQueryNormalizedBaseLocaleName-win.cpp $ */
+/** @file
+ * IPRT - RTLocaleQueryNormalizedBaseLocaleName, ring-3, Windows.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+
+#include <iprt/locale.h>
+#include "internal/iprt.h"
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTLocaleQueryNormalizedBaseLocaleName(char *pszName, size_t cbName)
+{
+ /*
+ * Note! This part is duplicate of r3/generic/RTLocaleQueryNormalizedBaseLocaleName-r3-generic.cpp!
+ */
+ char szLocale[_1K];
+ int rc = RTLocaleQueryLocaleName(szLocale, sizeof(szLocale));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * May return some complicated "LC_XXX=yyy;LC.." sequence if
+ * partially set (like IPRT does). Try get xx_YY sequence first
+ * because 'C' or 'POSIX' may be LC_xxx variants that haven't been
+ * set yet.
+ *
+ * ASSUMES complicated locale mangling is done in a certain way...
+ */
+ const char *pszLocale = strchr(szLocale, '=');
+ if (!pszLocale)
+ pszLocale = szLocale;
+ else
+ pszLocale++;
+ bool fSeenC = false;
+ bool fSeenPOSIX = false;
+ do
+ {
+ const char *pszEnd = strchr(pszLocale, ';');
+
+ if ( RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(pszLocale)
+ && ( pszLocale[5] == '\0'
+ || RT_C_IS_PUNCT(pszLocale[5])) )
+ return RTStrCopyEx(pszName, cbName, pszLocale, 5);
+
+ if ( pszLocale[0] == 'C'
+ && ( pszLocale[1] == '\0'
+ || RT_C_IS_PUNCT(pszLocale[1])) )
+ fSeenC = true;
+ else if ( strncmp(pszLocale, "POSIX", 5) == 0
+ && ( pszLocale[5] == '\0'
+ || RT_C_IS_PUNCT(pszLocale[5])) )
+ fSeenPOSIX = true;
+
+ /* advance */
+ pszLocale = pszEnd ? strchr(pszEnd + 1, '=') : NULL;
+ } while (pszLocale++);
+
+ if (fSeenC || fSeenPOSIX)
+ return RTStrCopy(pszName, cbName, "C"); /* C and POSIX should be identical IIRC, so keep it simple. */
+
+ rc = VERR_NOT_AVAILABLE;
+ }
+
+ /*
+ * Fallback.
+ */
+ if ( GetLocaleInfoA(GetUserDefaultLCID(), LOCALE_SISO639LANGNAME, szLocale, sizeof(szLocale)) == 3
+ && GetLocaleInfoA(GetUserDefaultLCID(), LOCALE_SISO3166CTRYNAME, &szLocale[3], sizeof(szLocale) - 4) == 3)
+ {
+ szLocale[2] = '_';
+ Assert(RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(szLocale));
+ return RTStrCopy(pszName, cbName, szLocale);
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp b/src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp
new file mode 100644
index 00000000..a95b1c22
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp
@@ -0,0 +1,129 @@
+/* $Id: RTLocaleQueryUserCountryCode-win.cpp $ */
+/** @file
+ * IPRT - RTLocaleQueryUserCountryCode, ring-3, Windows.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+
+#include <iprt/locale.h>
+#include "internal/iprt.h"
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef GEOID (WINAPI *PFNGETUSERGEOID)(GEOCLASS);
+typedef INT (WINAPI *PFNGETGEOINFOW)(GEOID,GEOTYPE,LPWSTR,INT,LANGID);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to GetUserGeoID. */
+static PFNGETUSERGEOID g_pfnGetUserGeoID = NULL;
+/** Pointer to GetGeoInfoW. */
+static PFNGETGEOINFOW g_pfnGetGeoInfoW = NULL;
+/** Set if we've tried to resolve the APIs. */
+static bool volatile g_fResolvedApis = false;
+
+
+RTDECL(int) RTLocaleQueryUserCountryCode(char pszCountryCode[3])
+{
+ /*
+ * Get API pointers.
+ */
+ PFNGETUSERGEOID pfnGetUserGeoID;
+ PFNGETGEOINFOW pfnGetGeoInfoW;
+ if (g_fResolvedApis)
+ {
+ pfnGetUserGeoID = g_pfnGetUserGeoID;
+ pfnGetGeoInfoW = g_pfnGetGeoInfoW;
+ }
+ else
+ {
+ pfnGetUserGeoID = (PFNGETUSERGEOID)GetProcAddress(g_hModKernel32, "GetUserGeoID");
+ pfnGetGeoInfoW = (PFNGETGEOINFOW)GetProcAddress(g_hModKernel32, "GetGeoInfoW");
+ g_pfnGetUserGeoID = pfnGetUserGeoID;
+ g_pfnGetGeoInfoW = pfnGetGeoInfoW;
+ g_fResolvedApis = true;
+ }
+
+ int rc;
+ if ( pfnGetGeoInfoW
+ && pfnGetUserGeoID)
+ {
+ /*
+ * Call the API and retrieve the two letter ISO country code.
+ */
+ GEOID idGeo = pfnGetUserGeoID(GEOCLASS_NATION);
+ if (idGeo != GEOID_NOT_AVAILABLE)
+ {
+ RTUTF16 wszName[16];
+ RT_ZERO(wszName);
+ DWORD cwcReturned = pfnGetGeoInfoW(idGeo, GEO_ISO2, wszName, RT_ELEMENTS(wszName), LOCALE_NEUTRAL);
+ if ( cwcReturned >= 2
+ && cwcReturned <= 3
+ && wszName[2] == '\0'
+ && wszName[1] != '\0'
+ && RT_C_IS_ALPHA(wszName[1])
+ && wszName[0] != '\0'
+ && RT_C_IS_ALPHA(wszName[0]) )
+ {
+ pszCountryCode[0] = RT_C_TO_UPPER(wszName[0]);
+ pszCountryCode[1] = RT_C_TO_UPPER(wszName[1]);
+ pszCountryCode[2] = '\0';
+ return VINF_SUCCESS;
+ }
+ AssertMsgFailed(("cwcReturned=%d err=%u wszName='%.16ls'\n", cwcReturned, GetLastError(), wszName));
+ }
+ rc = VERR_NOT_AVAILABLE;
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ pszCountryCode[0] = 'Z';
+ pszCountryCode[1] = 'Z';
+ pszCountryCode[2] = '\0';
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp b/src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp
new file mode 100644
index 00000000..27d3bc05
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp
@@ -0,0 +1,54 @@
+/* $Id: RTLogWriteDebugger-win.cpp $ */
+/** @file
+ * IPRT - Log To Debugger, Win32.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+
+#include <iprt/log.h>
+#include <iprt/assert.h>
+
+
+RTDECL(void) RTLogWriteDebugger(const char *pch, size_t cb)
+{
+ if (pch[cb] != '\0')
+ AssertBreakpoint();
+ OutputDebugStringA(pch);
+ return;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp b/src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp
new file mode 100644
index 00000000..d00dacb1
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp
@@ -0,0 +1,223 @@
+/* $Id: RTSystemFirmware-win.cpp $ */
+/** @file
+ * IPRT - System firmware information, Win32.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/system.h>
+
+#include <iprt/nt/nt-and-windows.h>
+#include <WinSDKVer.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/ldr.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#if _WIN32_MAXVER < 0x0602 /* Windows 7 or older, supply missing GetFirmwareType bits. */
+typedef enum _FIRMWARE_TYPE
+{
+ FirmwareTypeUnknown,
+ FirmwareTypeBios,
+ FirmwareTypeUefi,
+ FirmwareTypeMax
+} FIRMWARE_TYPE;
+typedef FIRMWARE_TYPE *PFIRMWARE_TYPE;
+WINBASEAPI BOOL WINAPI GetFirmwareType(PFIRMWARE_TYPE);
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Defines the UEFI Globals UUID. */
+#define VBOX_UEFI_UUID_GLOBALS L"{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}"
+/** Defines an UEFI dummy UUID, see MSDN docs of the API. */
+#define VBOX_UEFI_UUID_DUMMY L"{00000000-0000-0000-0000-000000000000}"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static volatile bool g_fResolvedApis = false;
+static decltype(GetFirmwareType) *g_pfnGetFirmwareType;
+static decltype(GetFirmwareEnvironmentVariableW) *g_pfnGetFirmwareEnvironmentVariableW;
+
+
+static void rtSystemFirmwareResolveApis(void)
+{
+ FARPROC pfnTmp1 = GetProcAddress(g_hModKernel32, "GetFirmwareType");
+ FARPROC pfnTmp2 = GetProcAddress(g_hModKernel32, "GetFirmwareEnvironmentVariableW");
+ ASMCompilerBarrier(); /* paranoia^2 */
+
+ g_pfnGetFirmwareType = (decltype(GetFirmwareType) *)pfnTmp1;
+ g_pfnGetFirmwareEnvironmentVariableW = (decltype(GetFirmwareEnvironmentVariableW) *)pfnTmp2;
+ ASMAtomicWriteBool(&g_fResolvedApis, true);
+}
+
+
+static int rtSystemFirmwareGetPrivileges(LPCTSTR pcszPrivilege)
+{
+ HANDLE hToken;
+ BOOL fRc = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
+ if (!fRc)
+ return RTErrConvertFromWin32(GetLastError());
+
+ int rc = VINF_SUCCESS;
+
+ TOKEN_PRIVILEGES tokenPriv;
+ fRc = LookupPrivilegeValue(NULL, pcszPrivilege, &tokenPriv.Privileges[0].Luid);
+ if (fRc)
+ {
+ tokenPriv.PrivilegeCount = 1;
+ tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ fRc = AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, 0, (PTOKEN_PRIVILEGES)NULL, 0);
+ if (!fRc)
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ CloseHandle(hToken);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSystemQueryFirmwareType(PRTSYSFWTYPE penmFirmwareType)
+{
+ AssertPtrReturn(penmFirmwareType, VERR_INVALID_POINTER);
+
+ if (!g_fResolvedApis)
+ rtSystemFirmwareResolveApis();
+
+ *penmFirmwareType = RTSYSFWTYPE_INVALID;
+ int rc = VERR_NOT_SUPPORTED;
+
+ /* GetFirmwareType is Windows 8 and later. */
+ if (g_pfnGetFirmwareType)
+ {
+ FIRMWARE_TYPE enmWinFwType;
+ if (g_pfnGetFirmwareType(&enmWinFwType))
+ {
+ switch (enmWinFwType)
+ {
+ case FirmwareTypeBios:
+ *penmFirmwareType = RTSYSFWTYPE_BIOS;
+ break;
+ case FirmwareTypeUefi:
+ *penmFirmwareType = RTSYSFWTYPE_UEFI;
+ break;
+ default:
+ *penmFirmwareType = RTSYSFWTYPE_UNKNOWN;
+ AssertMsgFailed(("%d\n", enmWinFwType));
+ break;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ /* GetFirmwareEnvironmentVariableW is XP and later. */
+ else if (g_pfnGetFirmwareEnvironmentVariableW)
+ {
+ rtSystemFirmwareGetPrivileges(SE_SYSTEM_ENVIRONMENT_NAME);
+
+ /* On a non-UEFI system (or such a system in legacy boot mode), we will get
+ back ERROR_INVALID_FUNCTION when querying any firmware variable. While on a
+ UEFI system we'll typically get ERROR_ACCESS_DENIED or similar as the dummy
+ is a non-exising dummy namespace. See the API docs. */
+ SetLastError(0);
+ uint8_t abWhatever[64];
+ DWORD cbRet = g_pfnGetFirmwareEnvironmentVariableW(L"", VBOX_UEFI_UUID_DUMMY, abWhatever, sizeof(abWhatever));
+ DWORD dwErr = GetLastError();
+ *penmFirmwareType = cbRet != 0 || dwErr != ERROR_INVALID_FUNCTION ? RTSYSFWTYPE_UEFI : RTSYSFWTYPE_BIOS;
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTSystemQueryFirmwareBoolean(RTSYSFWBOOL enmBoolean, bool *pfValue)
+{
+ *pfValue = false;
+
+ /*
+ * Translate the enmBoolean to a name:
+ */
+ const wchar_t *pwszName = NULL;
+ switch (enmBoolean)
+ {
+ case RTSYSFWBOOL_SECURE_BOOT:
+ pwszName = L"SecureBoot";
+ break;
+
+ default:
+ AssertReturn(enmBoolean > RTSYSFWBOOL_INVALID && enmBoolean < RTSYSFWBOOL_END, VERR_INVALID_PARAMETER);
+ return VERR_SYS_UNSUPPORTED_FIRMWARE_PROPERTY;
+ }
+
+ /*
+ * Do the query.
+ * Note! This will typically fail with access denied unless we're in an elevated process.
+ */
+ if (!g_pfnGetFirmwareEnvironmentVariableW)
+ return VERR_NOT_SUPPORTED;
+ rtSystemFirmwareGetPrivileges(SE_SYSTEM_ENVIRONMENT_NAME);
+
+ uint8_t bValue = 0;
+ DWORD cbRet = g_pfnGetFirmwareEnvironmentVariableW(pwszName, VBOX_UEFI_UUID_GLOBALS, &bValue, sizeof(bValue));
+ *pfValue = cbRet != 0 && bValue != 0;
+ if (cbRet != 0)
+ return VINF_SUCCESS;
+ DWORD dwErr = GetLastError();
+ if ( dwErr == ERROR_INVALID_FUNCTION
+ || dwErr == ERROR_ENVVAR_NOT_FOUND)
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(dwErr);
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp b/src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp
new file mode 100644
index 00000000..bd9c97e5
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp
@@ -0,0 +1,269 @@
+/* $Id: RTSystemQueryDmiString-win.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryDmiString, windows ring-3.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define _WIN32_DCOM
+#include <iprt/win/windows.h>
+#include <WbemCli.h>
+
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+
+/**
+ * Initialize COM.
+ *
+ * @returns COM status code.
+ */
+static HRESULT rtSystemDmiWinInitialize(void)
+{
+ HRESULT hrc = CoInitializeEx(0, COINIT_MULTITHREADED);
+ if (SUCCEEDED(hrc))
+ {
+ hrc = CoInitializeSecurity(NULL,
+ -1, /* COM authentication. */
+ NULL, /* Which authentication services. */
+ NULL, /* Reserved. */
+ RPC_C_AUTHN_LEVEL_DEFAULT, /* Default authentication. */
+ RPC_C_IMP_LEVEL_IMPERSONATE, /* Default impersonation. */
+ NULL, /* Authentication info. */
+ EOAC_NONE, /* Additional capabilities. */
+ NULL); /* Reserved. */
+ if (hrc == RPC_E_TOO_LATE)
+ hrc = S_OK;
+ else if (FAILED(hrc))
+ CoUninitialize();
+ }
+ return hrc;
+}
+
+
+/**
+ * Undo what rtSystemDmiWinInitialize did.
+ */
+static void rtSystemDmiWinTerminate(void)
+{
+ CoUninitialize();
+}
+
+
+/**
+ * Convert a UTF-8 string to a BSTR.
+ *
+ * @returns BSTR pointer.
+ * @param psz The UTF-8 string.
+ */
+static BSTR rtSystemWinBstrFromUtf8(const char *psz)
+{
+ PRTUTF16 pwsz = NULL;
+ int rc = RTStrToUtf16(psz, &pwsz);
+ if (RT_FAILURE(rc))
+ return NULL;
+ BSTR pBStr = SysAllocString((const OLECHAR *)pwsz);
+ RTUtf16Free(pwsz);
+ return pBStr;
+}
+
+
+/**
+ * Connect to the DMI server.
+ *
+ * @returns COM status code.
+ * @param pLocator The locator.
+ * @param pszServer The server name.
+ * @param ppServices Where to return the services interface.
+ */
+static HRESULT rtSystemDmiWinConnectToServer(IWbemLocator *pLocator, const char *pszServer, IWbemServices **ppServices)
+{
+ AssertPtr(pLocator);
+ AssertPtrNull(pszServer);
+ AssertPtr(ppServices);
+
+ BSTR pBStrServer = rtSystemWinBstrFromUtf8(pszServer);
+ if (!pBStrServer)
+ return E_OUTOFMEMORY;
+
+ HRESULT hrc = pLocator->ConnectServer(pBStrServer,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ 0,
+ ppServices);
+ if (SUCCEEDED(hrc))
+ {
+ hrc = CoSetProxyBlanket(*ppServices,
+ RPC_C_AUTHN_WINNT,
+ RPC_C_AUTHZ_NONE,
+ NULL,
+ RPC_C_AUTHN_LEVEL_CALL,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ NULL,
+ EOAC_NONE);
+ if (FAILED(hrc))
+ (*ppServices)->Release();
+ }
+ SysFreeString(pBStrServer);
+ return hrc;
+}
+
+
+RTDECL(int) RTSystemQueryDmiString(RTSYSDMISTR enmString, char *pszBuf, size_t cbBuf)
+{
+ AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbBuf > 0, VERR_INVALID_PARAMETER);
+ *pszBuf = '\0';
+ AssertReturn(enmString > RTSYSDMISTR_INVALID && enmString < RTSYSDMISTR_END, VERR_INVALID_PARAMETER);
+
+ /*
+ * Figure the property name before we start.
+ */
+ const char *pszPropName;
+ switch (enmString)
+ {
+ case RTSYSDMISTR_PRODUCT_NAME: pszPropName = "Name"; break;
+ case RTSYSDMISTR_PRODUCT_VERSION: pszPropName = "Version"; break;
+ case RTSYSDMISTR_PRODUCT_UUID: pszPropName = "UUID"; break;
+ case RTSYSDMISTR_PRODUCT_SERIAL: pszPropName = "IdentifyingNumber"; break;
+ case RTSYSDMISTR_MANUFACTURER: pszPropName = "Vendor"; break;
+
+ default:
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Before we do anything with COM, we have to initialize it.
+ */
+ bool fUninit = true;
+ HRESULT hrc = rtSystemDmiWinInitialize();
+ if (hrc == RPC_E_CHANGED_MODE)
+ fUninit = false; /* don't fail if already initialized */
+ else if (FAILED(hrc))
+ return VERR_NOT_SUPPORTED;
+
+ int rc = VERR_NOT_SUPPORTED;
+ BSTR pBstrPropName = rtSystemWinBstrFromUtf8(pszPropName);
+ if (pBstrPropName)
+ {
+ /*
+ * Instantiate the IWbemLocator, whatever that is and connect to the
+ * DMI serve.
+ */
+ IWbemLocator *pLoc;
+ hrc = CoCreateInstance(CLSID_WbemLocator,
+ 0,
+ CLSCTX_INPROC_SERVER,
+ IID_IWbemLocator,
+ (LPVOID *)&pLoc);
+ if (SUCCEEDED(hrc))
+ {
+ IWbemServices *pServices;
+ hrc = rtSystemDmiWinConnectToServer(pLoc, "ROOT\\CIMV2", &pServices);
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * Enumerate whatever it is we're looking at and try get
+ * the desired property.
+ */
+ BSTR pBstrFilter = rtSystemWinBstrFromUtf8("Win32_ComputerSystemProduct");
+ if (pBstrFilter)
+ {
+ IEnumWbemClassObject *pEnum;
+ hrc = pServices->CreateInstanceEnum(pBstrFilter, 0, NULL, &pEnum);
+ if (SUCCEEDED(hrc))
+ {
+ do
+ {
+ IWbemClassObject *pObj;
+ ULONG cObjRet;
+ hrc = pEnum->Next(WBEM_INFINITE, 1, &pObj, &cObjRet);
+ if ( SUCCEEDED(hrc)
+ && cObjRet >= 1)
+ {
+ VARIANT Var;
+ VariantInit(&Var);
+ hrc = pObj->Get(pBstrPropName, 0, &Var, 0, 0);
+ if ( SUCCEEDED(hrc)
+ && V_VT(&Var) == VT_BSTR)
+ {
+ /*
+ * Convert the BSTR to UTF-8 and copy it
+ * into the return buffer.
+ */
+ char *pszValue;
+ rc = RTUtf16ToUtf8(Var.bstrVal, &pszValue);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrCopy(pszBuf, cbBuf, pszValue);
+ RTStrFree(pszValue);
+ hrc = WBEM_S_FALSE;
+ }
+ }
+ VariantClear(&Var);
+ pObj->Release();
+ }
+ } while (hrc != WBEM_S_FALSE);
+
+ pEnum->Release();
+ }
+ SysFreeString(pBstrFilter);
+ }
+ else
+ hrc = E_OUTOFMEMORY;
+ pServices->Release();
+ }
+ pLoc->Release();
+ }
+ SysFreeString(pBstrPropName);
+ }
+ else
+ hrc = E_OUTOFMEMORY;
+ if (fUninit)
+ rtSystemDmiWinTerminate();
+ if (FAILED(hrc) && rc == VERR_NOT_SUPPORTED)
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp b/src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp
new file mode 100644
index 00000000..56688e42
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp
@@ -0,0 +1,358 @@
+/* $Id: RTSystemQueryOSInfo-win.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryOSInfo, generic stub.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/win/windows.h>
+#include <WinUser.h>
+
+#include "internal-r3-win.h"
+#include <iprt/system.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * These are the PRODUCT_* defines found in the Vista Platform SDK and returned
+ * by GetProductInfo().
+ *
+ * We define them ourselves because we don't necessarily have any Vista PSDK around.
+ */
+typedef enum RTWINPRODTYPE
+{
+ kRTWinProdType_UNDEFINED = 0x00000000, ///< An unknown product
+ kRTWinProdType_BUSINESS = 0x00000006, ///< Business Edition
+ kRTWinProdType_BUSINESS_N = 0x00000010, ///< Business Edition
+ kRTWinProdType_CLUSTER_SERVER = 0x00000012, ///< Cluster Server Edition
+ kRTWinProdType_DATACENTER_SERVER = 0x00000008, ///< Server Datacenter Edition (full installation)
+ kRTWinProdType_DATACENTER_SERVER_CORE = 0x0000000C, ///< Server Datacenter Edition (core installation)
+ kRTWinProdType_ENTERPRISE = 0x00000004, ///< Enterprise Edition
+ kRTWinProdType_ENTERPRISE_N = 0x0000001B, ///< Enterprise Edition
+ kRTWinProdType_ENTERPRISE_SERVER = 0x0000000A, ///< Server Enterprise Edition (full installation)
+ kRTWinProdType_ENTERPRISE_SERVER_CORE = 0x0000000E, ///< Server Enterprise Edition (core installation)
+ kRTWinProdType_ENTERPRISE_SERVER_IA64 = 0x0000000F, ///< Server Enterprise Edition for Itanium-based Systems
+ kRTWinProdType_HOME_BASIC = 0x00000002, ///< Home Basic Edition
+ kRTWinProdType_HOME_BASIC_N = 0x00000005, ///< Home Basic Edition
+ kRTWinProdType_HOME_PREMIUM = 0x00000003, ///< Home Premium Edition
+ kRTWinProdType_HOME_PREMIUM_N = 0x0000001A, ///< Home Premium Edition
+ kRTWinProdType_HOME_SERVER = 0x00000013, ///< Home Server Edition
+ kRTWinProdType_SERVER_FOR_SMALLBUSINESS = 0x00000018, ///< Server for Small Business Edition
+ kRTWinProdType_SMALLBUSINESS_SERVER = 0x00000009, ///< Small Business Server
+ kRTWinProdType_SMALLBUSINESS_SERVER_PREMIUM = 0x00000019, ///< Small Business Server Premium Edition
+ kRTWinProdType_STANDARD_SERVER = 0x00000007, ///< Server Standard Edition (full installation)
+ kRTWinProdType_STANDARD_SERVER_CORE = 0x0000000D, ///< Server Standard Edition (core installation)
+ kRTWinProdType_STARTER = 0x0000000B, ///< Starter Edition
+ kRTWinProdType_STORAGE_ENTERPRISE_SERVER = 0x00000017, ///< Storage Server Enterprise Edition
+ kRTWinProdType_STORAGE_EXPRESS_SERVER = 0x00000014, ///< Storage Server Express Edition
+ kRTWinProdType_STORAGE_STANDARD_SERVER = 0x00000015, ///< Storage Server Standard Edition
+ kRTWinProdType_STORAGE_WORKGROUP_SERVER = 0x00000016, ///< Storage Server Workgroup Edition
+ kRTWinProdType_ULTIMATE = 0x00000001, ///< Ultimate Edition
+ kRTWinProdType_ULTIMATE_N = 0x0000001C, ///< Ultimate Edition
+ kRTWinProdType_WEB_SERVER = 0x00000011, ///< Web Server Edition (full)
+ kRTWinProdType_WEB_SERVER_CORE = 0x0000001D ///< Web Server Edition (core)
+} RTWINPRODTYPE;
+
+
+/**
+ * Wrapper around the GetProductInfo API.
+ *
+ * @returns The vista type.
+ */
+static RTWINPRODTYPE rtSystemWinGetProductInfo(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion)
+{
+ BOOL (WINAPI *pfnGetProductInfo)(DWORD, DWORD, DWORD, DWORD, PDWORD);
+ pfnGetProductInfo = (BOOL (WINAPI *)(DWORD, DWORD, DWORD, DWORD, PDWORD))GetProcAddress(GetModuleHandle("kernel32.dll"),
+ "GetProductInfo");
+ if (pfnGetProductInfo)
+ {
+ DWORD dwProductType = kRTWinProdType_UNDEFINED;
+ if (pfnGetProductInfo(dwOSMajorVersion, dwOSMinorVersion, dwSpMajorVersion, dwSpMinorVersion, &dwProductType))
+ return (RTWINPRODTYPE)dwProductType;
+ }
+ return kRTWinProdType_UNDEFINED;
+}
+
+
+
+/**
+ * Appends the product type if available (Vista & 2003 only for some reason).
+ *
+ * @param pszTmp The buffer.
+ * @param cbTmp The buffer size.
+ */
+static void rtSystemWinAppendProductType(char *pszTmp, size_t cbTmp)
+{
+ RTWINPRODTYPE enmVistaType = rtSystemWinGetProductInfo(6, 0, 0, 0);
+ switch (enmVistaType)
+ {
+ case kRTWinProdType_BUSINESS: RTStrCat(pszTmp, cbTmp, " Business Edition"); break;
+ case kRTWinProdType_BUSINESS_N: RTStrCat(pszTmp, cbTmp, " Business Edition"); break;
+ case kRTWinProdType_CLUSTER_SERVER: RTStrCat(pszTmp, cbTmp, " Cluster Server Edition"); break;
+ case kRTWinProdType_DATACENTER_SERVER: RTStrCat(pszTmp, cbTmp, " Server Datacenter Edition (full installation)"); break;
+ case kRTWinProdType_DATACENTER_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Server Datacenter Edition (core installation)"); break;
+ case kRTWinProdType_ENTERPRISE: RTStrCat(pszTmp, cbTmp, " Enterprise Edition"); break;
+ case kRTWinProdType_ENTERPRISE_N: RTStrCat(pszTmp, cbTmp, " Enterprise Edition"); break;
+ case kRTWinProdType_ENTERPRISE_SERVER: RTStrCat(pszTmp, cbTmp, " Server Enterprise Edition (full installation)"); break;
+ case kRTWinProdType_ENTERPRISE_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Server Enterprise Edition (core installation)"); break;
+ case kRTWinProdType_ENTERPRISE_SERVER_IA64: RTStrCat(pszTmp, cbTmp, " Server Enterprise Edition for Itanium-based Systems"); break;
+ case kRTWinProdType_HOME_BASIC: RTStrCat(pszTmp, cbTmp, " Home Basic Edition"); break;
+ case kRTWinProdType_HOME_BASIC_N: RTStrCat(pszTmp, cbTmp, " Home Basic Edition"); break;
+ case kRTWinProdType_HOME_PREMIUM: RTStrCat(pszTmp, cbTmp, " Home Premium Edition"); break;
+ case kRTWinProdType_HOME_PREMIUM_N: RTStrCat(pszTmp, cbTmp, " Home Premium Edition"); break;
+ case kRTWinProdType_HOME_SERVER: RTStrCat(pszTmp, cbTmp, " Home Server Edition"); break;
+ case kRTWinProdType_SERVER_FOR_SMALLBUSINESS: RTStrCat(pszTmp, cbTmp, " Server for Small Business Edition"); break;
+ case kRTWinProdType_SMALLBUSINESS_SERVER: RTStrCat(pszTmp, cbTmp, " Small Business Server"); break;
+ case kRTWinProdType_SMALLBUSINESS_SERVER_PREMIUM: RTStrCat(pszTmp, cbTmp, " Small Business Server Premium Edition"); break;
+ case kRTWinProdType_STANDARD_SERVER: RTStrCat(pszTmp, cbTmp, " Server Standard Edition (full installation)"); break;
+ case kRTWinProdType_STANDARD_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Server Standard Edition (core installation)"); break;
+ case kRTWinProdType_STARTER: RTStrCat(pszTmp, cbTmp, " Starter Edition"); break;
+ case kRTWinProdType_STORAGE_ENTERPRISE_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Enterprise Edition"); break;
+ case kRTWinProdType_STORAGE_EXPRESS_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Express Edition"); break;
+ case kRTWinProdType_STORAGE_STANDARD_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Standard Edition"); break;
+ case kRTWinProdType_STORAGE_WORKGROUP_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Workgroup Edition"); break;
+ case kRTWinProdType_ULTIMATE: RTStrCat(pszTmp, cbTmp, " Ultimate Edition"); break;
+ case kRTWinProdType_ULTIMATE_N: RTStrCat(pszTmp, cbTmp, " Ultimate Edition"); break;
+ case kRTWinProdType_WEB_SERVER: RTStrCat(pszTmp, cbTmp, " Web Server Edition (full installation)"); break;
+ case kRTWinProdType_WEB_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Web Server Edition (core installation)"); break;
+ case kRTWinProdType_UNDEFINED: break;
+ }
+}
+
+
+/**
+ * Services the RTSYSOSINFO_PRODUCT, RTSYSOSINFO_RELEASE
+ * and RTSYSOSINFO_SERVICE_PACK requests.
+ *
+ * @returns See RTSystemQueryOSInfo.
+ * @param enmInfo See RTSystemQueryOSInfo.
+ * @param pszInfo See RTSystemQueryOSInfo.
+ * @param cchInfo See RTSystemQueryOSInfo.
+ */
+static int rtSystemWinQueryOSVersion(RTSYSOSINFO enmInfo, char *pszInfo, size_t cchInfo)
+{
+ /*
+ * Make sure it's terminated correctly in case of error.
+ */
+ *pszInfo = '\0';
+
+ /*
+ * Check that we got the windows version at init time.
+ */
+ AssertReturn(g_WinOsInfoEx.dwOSVersionInfoSize, VERR_WRONG_ORDER);
+
+ /*
+ * Service the request.
+ */
+ char szTmp[512];
+ szTmp[0] = '\0';
+ switch (enmInfo)
+ {
+ /*
+ * The product name.
+ */
+ case RTSYSOSINFO_PRODUCT:
+ {
+ switch (g_enmWinVer)
+ {
+ case kRTWinOSType_95: strcpy(szTmp, "Windows 95"); break;
+ case kRTWinOSType_95SP1: strcpy(szTmp, "Windows 95 (Service Pack 1)"); break;
+ case kRTWinOSType_95OSR2: strcpy(szTmp, "Windows 95 (OSR 2)"); break;
+ case kRTWinOSType_98: strcpy(szTmp, "Windows 98"); break;
+ case kRTWinOSType_98SP1: strcpy(szTmp, "Windows 98 (Service Pack 1)"); break;
+ case kRTWinOSType_98SE: strcpy(szTmp, "Windows 98 (Second Edition)"); break;
+ case kRTWinOSType_ME: strcpy(szTmp, "Windows Me"); break;
+ case kRTWinOSType_NT310: strcpy(szTmp, "Windows NT 3.10"); break;
+ case kRTWinOSType_NT350: strcpy(szTmp, "Windows NT 3.50"); break;
+ case kRTWinOSType_NT351: strcpy(szTmp, "Windows NT 3.51"); break;
+ case kRTWinOSType_NT4: strcpy(szTmp, "Windows NT 4.0"); break;
+ case kRTWinOSType_2K: strcpy(szTmp, "Windows 2000"); break;
+ case kRTWinOSType_XP:
+ strcpy(szTmp, "Windows XP");
+ if (g_WinOsInfoEx.wSuiteMask & VER_SUITE_PERSONAL)
+ RTStrCat(szTmp, sizeof(szTmp), " Home");
+ if ( g_WinOsInfoEx.wProductType == VER_NT_WORKSTATION
+ && !(g_WinOsInfoEx.wSuiteMask & VER_SUITE_PERSONAL))
+ RTStrCat(szTmp, sizeof(szTmp), " Professional");
+#if 0 /** @todo fixme */
+ if (GetSystemMetrics(SM_MEDIACENTER))
+ RTStrCat(szTmp, sizeof(szTmp), " Media Center");
+#endif
+ break;
+
+ case kRTWinOSType_2003: strcpy(szTmp, "Windows 2003"); break;
+ case kRTWinOSType_VISTA:
+ {
+ strcpy(szTmp, "Windows Vista");
+ rtSystemWinAppendProductType(szTmp, sizeof(szTmp));
+ break;
+ }
+ case kRTWinOSType_2008: strcpy(szTmp, "Windows 2008"); break;
+ case kRTWinOSType_7: strcpy(szTmp, "Windows 7"); break;
+ case kRTWinOSType_2008R2: strcpy(szTmp, "Windows 2008 R2"); break;
+ case kRTWinOSType_8: strcpy(szTmp, "Windows 8"); break;
+ case kRTWinOSType_2012: strcpy(szTmp, "Windows 2012"); break;
+ case kRTWinOSType_81: strcpy(szTmp, "Windows 8.1"); break;
+ case kRTWinOSType_2012R2: strcpy(szTmp, "Windows 2012 R2"); break;
+ case kRTWinOSType_10: strcpy(szTmp, "Windows 10"); break;
+ case kRTWinOSType_2016: strcpy(szTmp, "Windows 2016"); break;
+ case kRTWinOSType_2019: strcpy(szTmp, "Windows 2019"); break;
+ case kRTWinOSType_2022: strcpy(szTmp, "Windows 2022"); break;
+ case kRTWinOSType_11: strcpy(szTmp, "Windows 11"); break;
+
+ case kRTWinOSType_NT_UNKNOWN:
+ RTStrPrintf(szTmp, sizeof(szTmp), "Unknown NT v%u.%u",
+ g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion);
+ break;
+
+ default:
+ AssertFailed();
+ case kRTWinOSType_UNKNOWN:
+ RTStrPrintf(szTmp, sizeof(szTmp), "Unknown %d v%u.%u",
+ g_WinOsInfoEx.dwPlatformId, g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion);
+ break;
+ }
+ break;
+ }
+
+ /*
+ * The release.
+ */
+ case RTSYSOSINFO_RELEASE:
+ {
+ RTStrPrintf(szTmp, sizeof(szTmp), "%u.%u.%u",
+ g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion, g_WinOsInfoEx.dwBuildNumber);
+ break;
+ }
+
+
+ /*
+ * Get the service pack.
+ */
+ case RTSYSOSINFO_SERVICE_PACK:
+ {
+ if (g_WinOsInfoEx.wServicePackMajor)
+ {
+ if (g_WinOsInfoEx.wServicePackMinor)
+ RTStrPrintf(szTmp, sizeof(szTmp), "%u.%u",
+ (unsigned)g_WinOsInfoEx.wServicePackMajor, (unsigned)g_WinOsInfoEx.wServicePackMinor);
+ else
+ RTStrPrintf(szTmp, sizeof(szTmp), "%u",
+ (unsigned)g_WinOsInfoEx.wServicePackMajor);
+ }
+ else if (g_WinOsInfoEx.szCSDVersion[0])
+ {
+ /* just copy the entire string. */
+ char *pszTmp = szTmp;
+ int rc = RTUtf16ToUtf8Ex(g_WinOsInfoEx.szCSDVersion, RT_ELEMENTS(g_WinOsInfoEx.szCSDVersion),
+ &pszTmp, sizeof(szTmp), NULL);
+ if (RT_SUCCESS(rc))
+ RTStrStripR(szTmp);
+ else
+ szTmp[0] = '\0';
+ AssertCompile(sizeof(szTmp) > sizeof(g_WinOsInfoEx.szCSDVersion));
+ }
+ else
+ {
+ switch (g_enmWinVer)
+ {
+ case kRTWinOSType_95SP1: strcpy(szTmp, "1"); break;
+ case kRTWinOSType_98SP1: strcpy(szTmp, "1"); break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ AssertFatalFailed();
+ }
+
+ /*
+ * Copy the result to the return buffer.
+ */
+ size_t cchTmp = strlen(szTmp);
+ Assert(cchTmp < sizeof(szTmp));
+ if (cchTmp < cchInfo)
+ {
+ memcpy(pszInfo, szTmp, cchTmp + 1);
+ return VINF_SUCCESS;
+ }
+ memcpy(pszInfo, szTmp, cchInfo - 1);
+ pszInfo[cchInfo - 1] = '\0';
+ return VERR_BUFFER_OVERFLOW;
+}
+
+
+RTDECL(int) RTSystemQueryOSInfo(RTSYSOSINFO enmInfo, char *pszInfo, size_t cchInfo)
+{
+ /*
+ * Quick validation.
+ */
+ AssertReturn(enmInfo > RTSYSOSINFO_INVALID && enmInfo < RTSYSOSINFO_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszInfo, VERR_INVALID_POINTER);
+ if (!cchInfo)
+ return VERR_BUFFER_OVERFLOW;
+
+
+ /*
+ * Handle the request.
+ */
+ switch (enmInfo)
+ {
+ case RTSYSOSINFO_PRODUCT:
+ case RTSYSOSINFO_RELEASE:
+ case RTSYSOSINFO_SERVICE_PACK:
+ return rtSystemWinQueryOSVersion(enmInfo, pszInfo, cchInfo);
+
+ case RTSYSOSINFO_VERSION:
+ default:
+ *pszInfo = '\0';
+ }
+
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp b/src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp
new file mode 100644
index 00000000..49465166
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp
@@ -0,0 +1,131 @@
+/* $Id: RTSystemQueryTotalRam-win.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryTotalRam, windows ring-3.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static bool volatile g_fInitialized = false;
+typedef BOOL (WINAPI *PFNGLOBALMEMORYSTATUSEX)(LPMEMORYSTATUSEX);
+static PFNGLOBALMEMORYSTATUSEX g_pfnGlobalMemoryStatusEx = NULL;
+
+
+/**
+ * The GlobalMemoryStatusEx API is not available on older Windows version.
+ *
+ * @returns Pointer to GlobalMemoryStatusEx or NULL if not available.
+ */
+DECLINLINE(PFNGLOBALMEMORYSTATUSEX) rtSystemWinGetExApi(void)
+{
+ PFNGLOBALMEMORYSTATUSEX pfnEx;
+ if (g_fInitialized)
+ pfnEx = g_pfnGlobalMemoryStatusEx;
+ else
+ {
+ pfnEx = (PFNGLOBALMEMORYSTATUSEX)GetProcAddress(g_hModKernel32, "GlobalMemoryStatusEx");
+ g_pfnGlobalMemoryStatusEx = pfnEx;
+ g_fInitialized = true;
+ }
+ return pfnEx;
+}
+
+
+RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ PFNGLOBALMEMORYSTATUSEX pfnGlobalMemoryStatusEx = rtSystemWinGetExApi();
+ if (pfnGlobalMemoryStatusEx)
+ {
+ MEMORYSTATUSEX MemStatus;
+ MemStatus.dwLength = sizeof(MemStatus);
+ if (pfnGlobalMemoryStatusEx(&MemStatus))
+ *pcb = MemStatus.ullTotalPhys;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ MEMORYSTATUS MemStatus;
+ RT_ZERO(MemStatus);
+ MemStatus.dwLength = sizeof(MemStatus);
+ GlobalMemoryStatus(&MemStatus);
+ *pcb = MemStatus.dwTotalPhys;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ PFNGLOBALMEMORYSTATUSEX pfnGlobalMemoryStatusEx = rtSystemWinGetExApi();
+ if (pfnGlobalMemoryStatusEx)
+ {
+ MEMORYSTATUSEX MemStatus;
+ MemStatus.dwLength = sizeof(MemStatus);
+ if (pfnGlobalMemoryStatusEx(&MemStatus))
+ *pcb = MemStatus.ullAvailPhys;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ MEMORYSTATUS MemStatus;
+ RT_ZERO(MemStatus);
+ MemStatus.dwLength = sizeof(MemStatus);
+ GlobalMemoryStatus(&MemStatus);
+ *pcb = MemStatus.dwAvailPhys;
+ }
+ return rc;
+}
diff --git a/src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp b/src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp
new file mode 100644
index 00000000..9a19ef3c
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp
@@ -0,0 +1,183 @@
+/* $Id: RTSystemShutdown-win.cpp $ */
+/** @file
+ * IPRT - RTSystemShutdown, Windows.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include <iprt/win/windows.h>
+
+
+RTDECL(int) RTSystemShutdown(RTMSINTERVAL cMsDelay, uint32_t fFlags, const char *pszLogMsg)
+{
+ AssertPtrReturn(pszLogMsg, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTSYSTEM_SHUTDOWN_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Before we start, try grant the necessary privileges.
+ */
+ DWORD dwErr;
+ HANDLE hToken = NULL;
+ if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE /*OpenAsSelf*/, &hToken))
+ dwErr = NO_ERROR;
+ else
+ {
+ dwErr = GetLastError();
+ if (dwErr == ERROR_NO_TOKEN)
+ {
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
+ dwErr = NO_ERROR;
+ else
+ dwErr = GetLastError();
+ }
+ }
+ if (dwErr == NO_ERROR)
+ {
+ union
+ {
+ TOKEN_PRIVILEGES TokenPriv;
+ char ab[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
+ } u;
+ u.TokenPriv.PrivilegeCount = 1;
+ u.TokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ if (LookupPrivilegeValue(NULL /*localhost*/, SE_SHUTDOWN_NAME, &u.TokenPriv.Privileges[0].Luid))
+ {
+ if (!AdjustTokenPrivileges(hToken,
+ FALSE /*DisableAllPrivileges*/,
+ &u.TokenPriv,
+ RT_UOFFSETOF(TOKEN_PRIVILEGES, Privileges[1]),
+ NULL,
+ NULL) )
+ dwErr = GetLastError();
+ }
+ else
+ dwErr = GetLastError();
+ CloseHandle(hToken);
+ }
+
+ /*
+ * Do some parameter conversion.
+ */
+ PRTUTF16 pwszLogMsg;
+ int rc = RTStrToUtf16(pszLogMsg, &pwszLogMsg);
+ if (RT_FAILURE(rc))
+ return rc;
+ DWORD cSecsTimeout = (cMsDelay + 499) / 1000;
+
+ /*
+ * If we're told to power off the system, we should try use InitiateShutdownW (6.0+)
+ * or ExitWindowsEx (3.50) rather than InitiateSystemShutdownW, because these other
+ * APIs allows us to explicitly specify that we want to power off.
+ *
+ * Note! For NT version 4, 3.51, and 3.50 the system may instaed reboot since the
+ * x86 HALs typically didn't know how to perform a power off.
+ */
+ bool fDone = false;
+ if ( (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_POWER_OFF
+ || (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_POWER_OFF_HALT)
+ {
+ /* This API has the grace period thing. */
+ decltype(InitiateShutdownW) *pfnInitiateShutdownW;
+ pfnInitiateShutdownW = (decltype(InitiateShutdownW) *)GetProcAddress(GetModuleHandleW(L"ADVAPI32.DLL"), "InitiateShutdownW");
+ if (pfnInitiateShutdownW)
+ {
+ DWORD fShutdownFlags = SHUTDOWN_POWEROFF;
+ if (fFlags & RTSYSTEM_SHUTDOWN_FORCE)
+ fShutdownFlags |= SHUTDOWN_FORCE_OTHERS | SHUTDOWN_FORCE_SELF;
+ DWORD fReason = SHTDN_REASON_MAJOR_OTHER | (fFlags & RTSYSTEM_SHUTDOWN_PLANNED ? SHTDN_REASON_FLAG_PLANNED : 0);
+ dwErr = pfnInitiateShutdownW(NULL /*pwszMachineName*/, pwszLogMsg, cSecsTimeout, fShutdownFlags, fReason);
+ if (dwErr == ERROR_INVALID_PARAMETER)
+ {
+ fReason &= ~SHTDN_REASON_FLAG_PLANNED; /* just in case... */
+ dwErr = pfnInitiateShutdownW(NULL /*pwszMachineName*/, pwszLogMsg, cSecsTimeout, fShutdownFlags, fReason);
+ }
+ if (dwErr == ERROR_SUCCESS)
+ {
+ rc = VINF_SUCCESS;
+ fDone = true;
+ }
+ }
+
+ if (!fDone)
+ {
+ /* No grace period here, too bad. */
+ decltype(ExitWindowsEx) *pfnExitWindowsEx;
+ pfnExitWindowsEx = (decltype(ExitWindowsEx) *)GetProcAddress(GetModuleHandleW(L"USER32.DLL"), "ExitWindowsEx");
+ if (pfnExitWindowsEx)
+ {
+ DWORD fExitWindows = EWX_POWEROFF | EWX_SHUTDOWN;
+ if (fFlags & RTSYSTEM_SHUTDOWN_FORCE)
+ fExitWindows |= EWX_FORCE | EWX_FORCEIFHUNG;
+
+ if (pfnExitWindowsEx(fExitWindows, SHTDN_REASON_MAJOR_OTHER))
+ fDone = true;
+ else if (pfnExitWindowsEx(fExitWindows & ~EWX_FORCEIFHUNG, SHTDN_REASON_MAJOR_OTHER))
+ fDone = true;
+ }
+ }
+ }
+
+ /*
+ * Fall back on the oldest API.
+ */
+ if (!fDone)
+ {
+ BOOL fRebootAfterShutdown = (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_REBOOT
+ ? TRUE : FALSE;
+ BOOL fForceAppsClosed = fFlags & RTSYSTEM_SHUTDOWN_FORCE ? TRUE : FALSE;
+ if (InitiateSystemShutdownW(NULL /*pwszMachineName = NULL = localhost*/,
+ pwszLogMsg,
+ cSecsTimeout,
+ fForceAppsClosed,
+ fRebootAfterShutdown))
+ rc = (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_HALT ? VINF_SYS_MAY_POWER_OFF : VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+
+ RTUtf16Free(pwszLogMsg);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSystemShutdown);
+
diff --git a/src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp b/src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp
new file mode 100644
index 00000000..310d8507
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp
@@ -0,0 +1,117 @@
+/* $Id: RTTimeZoneGetCurrent-win.cpp $ */
+/** @file
+ * IPRT - RTTimeZoneGetCurrent, generic.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/env.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/win/windows.h>
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef DWORD (WINAPI *PFNGETDYNAMICTIMEZONEINFORMATION)(PDYNAMIC_TIME_ZONE_INFORMATION);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to the GetDynamicTimeZoneInformation API if present. */
+static PFNGETDYNAMICTIMEZONEINFORMATION g_pfnGetDynamicTimeZoneInformation = NULL;
+/** Flipped after we've tried to resolve g_pfnGetDynamicTimeZoneInformation. */
+static bool volatile g_fResolvedApi = false;
+
+
+RTDECL(int) RTTimeZoneGetCurrent(char *pszName, size_t cbName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(cbName > 0, VERR_BUFFER_OVERFLOW);
+
+ /*
+ * Resolve API.
+ */
+ PFNGETDYNAMICTIMEZONEINFORMATION pfnApi;
+ if (g_fResolvedApi)
+ pfnApi = g_pfnGetDynamicTimeZoneInformation;
+ else
+ {
+ pfnApi = (PFNGETDYNAMICTIMEZONEINFORMATION)GetProcAddress(g_hModKernel32, "GetDynamicTimeZoneInformation");
+ g_pfnGetDynamicTimeZoneInformation = pfnApi;
+ g_fResolvedApi = true;
+ }
+
+ /*
+ * Call the API and convert the name we get.
+ */
+ union
+ {
+ TIME_ZONE_INFORMATION Tzi;
+ DYNAMIC_TIME_ZONE_INFORMATION DynTzi;
+ } uBuf;
+ RT_ZERO(uBuf);
+ DWORD dwRc;
+ PCRTUTF16 pwszSrcName;
+ size_t cwcSrcName;
+ if (pfnApi)
+ {
+ dwRc = pfnApi(&uBuf.DynTzi);
+ pwszSrcName = uBuf.DynTzi.TimeZoneKeyName;
+ cwcSrcName = RT_ELEMENTS(uBuf.DynTzi.TimeZoneKeyName);
+ }
+ else
+ {
+ /* Not sure how helpful this fallback really is... */
+ dwRc = GetTimeZoneInformation(&uBuf.Tzi);
+ pwszSrcName = uBuf.Tzi.StandardName;
+ cwcSrcName = RT_ELEMENTS(uBuf.Tzi.StandardName);
+ }
+ if (dwRc != TIME_ZONE_ID_INVALID)
+ {
+ Assert(*pwszSrcName != '\0');
+ return RTUtf16ToUtf8Ex(pwszSrcName, cwcSrcName, &pszName, cbName, &cbName);
+ }
+ return RTErrConvertFromWin32(GetLastError());
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp b/src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp
new file mode 100644
index 00000000..99a5692f
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp
@@ -0,0 +1,82 @@
+/* $Id: RTUuidCreate-win.cpp $ */
+/** @file
+ * IPRT - UUID, Windows RTUuidCreate implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_UUID
+#include <iprt/win/windows.h>
+
+#include <iprt/uuid.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/rand.h>
+
+#include "internal-r3-win.h"
+
+
+RTDECL(int) RTUuidCreate(PRTUUID pUuid)
+{
+ /*
+ * Input validation.
+ */
+ AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
+
+ /*
+ * When using the UuidCreate API shortly after boot on NT 3.1 it typcially
+ * hangs for a long long time while polling for some service to start.
+ * What then usually happens next is a failure because it couldn't figure
+ * out the MAC address of the NIC. So, on NT 3.1 we always use the fallback.
+ */
+ if (g_enmWinVer != kRTWinOSType_NT310)
+ {
+ RPC_STATUS rc = UuidCreate((UUID *)pUuid);
+ if ( rc == RPC_S_OK
+ || rc == RPC_S_UUID_LOCAL_ONLY)
+ return VINF_SUCCESS;
+ AssertMsg(rc == RPC_S_UUID_NO_ADDRESS, ("UuidCreate -> %u (%#x)\n", rc, rc));
+ }
+
+ /*
+ * Use generic implementation as fallback (copy of RTUuidCreate-generic.cpp).
+ */
+ RTRandBytes(pUuid, sizeof(*pUuid));
+ pUuid->Gen.u8ClockSeqHiAndReserved = (pUuid->Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
+ pUuid->Gen.u16TimeHiAndVersion = (pUuid->Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def b/src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def
new file mode 100644
index 00000000..5395dc50
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def
@@ -0,0 +1,1678 @@
+
+ ??0?$_Yarn@D@std@@QAE@ABV01@@Z
+ ??0?$_Yarn@D@std@@QAE@PBD@Z
+ ??0?$_Yarn@D@std@@QAE@XZ
+ ??0?$basic_ios@DU?$char_traits@D@std@@@std@@IAE@XZ
+ ??0?$basic_ios@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z
+ ??0?$basic_ios@GU?$char_traits@G@std@@@std@@IAE@XZ
+ ??0?$basic_ios@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z
+ ??0?$basic_ios@_WU?$char_traits@_W@std@@@std@@IAE@XZ
+ ??0?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z
+ ??0?$basic_iostream@DU?$char_traits@D@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_iostream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z
+ ??0?$basic_iostream@GU?$char_traits@G@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_iostream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z
+ ??0?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z
+ ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@_N1@Z
+ ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@_N@Z
+ ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@W4_Uninitialized@1@@Z
+ ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@_N1@Z
+ ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@_N@Z
+ ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@W4_Uninitialized@1@@Z
+ ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@_N1@Z
+ ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@_N@Z
+ ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@W4_Uninitialized@1@@Z
+ ??0?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@_N@Z
+ ??0?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@W4_Uninitialized@1@_N@Z
+ ??0?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@_N@Z
+ ??0?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE@W4_Uninitialized@1@_N@Z
+ ??0?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@_N@Z
+ ??0?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE@W4_Uninitialized@1@_N@Z
+ ??0?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAE@ABV01@@Z
+ ??0?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAE@W4_Uninitialized@1@@Z
+ ??0?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAE@XZ
+ ??0?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAE@ABV01@@Z
+ ??0?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAE@W4_Uninitialized@1@@Z
+ ??0?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAE@XZ
+ ??0?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAE@ABV01@@Z
+ ??0?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAE@W4_Uninitialized@1@@Z
+ ??0?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAE@XZ
+ ??0?$codecvt@DDH@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$codecvt@DDH@std@@QAE@I@Z
+ ??0?$codecvt@GDH@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$codecvt@GDH@std@@QAE@I@Z
+ ??0?$codecvt@_WDH@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$codecvt@_WDH@std@@QAE@I@Z
+ ??0?$ctype@D@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$ctype@D@std@@QAE@PBF_NI@Z
+ ??0?$ctype@G@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$ctype@G@std@@QAE@I@Z
+ ??0?$ctype@_W@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$ctype@_W@std@@QAE@I@Z
+ ??0?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z
+ ??0?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z
+ ??0?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z
+ ??0?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z
+ ??0?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z
+ ??0?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAE@PBDI@Z
+ ??0?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAE@PBDI@Z
+ ??0?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAE@PBDI@Z
+ ??0?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAE@PBDI@Z
+ ??0?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAE@PBDI@Z
+ ??0?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z
+ ??0Init@ios_base@std@@QAE@XZ
+ ??0_Concurrent_queue_base_v4@details@Concurrency@@IAE@I@Z
+ ??0_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAE@ABV_Concurrent_queue_base_v4@12@@Z
+ ??0_Container_base12@std@@QAE@ABU01@@Z
+ ??0_Container_base12@std@@QAE@XZ
+ ??0_Init_locks@std@@QAE@XZ
+ ??0_Locimp@locale@std@@AAE@ABV012@@Z
+ ??0_Locimp@locale@std@@AAE@_N@Z
+ ??0_Locinfo@std@@QAE@HPBD@Z
+ ??0_Locinfo@std@@QAE@PBD@Z
+ ??0_Lockit@std@@QAE@H@Z
+ ??0_Lockit@std@@QAE@XZ
+ ??0_Mutex@std@@QAE@W4_Uninitialized@1@@Z
+ ??0_Mutex@std@@QAE@XZ
+ ??0_Runtime_object@details@Concurrency@@QAE@H@Z
+ ??0_Runtime_object@details@Concurrency@@QAE@XZ
+ ??0_Timevec@std@@QAE@ABV01@@Z
+ ??0_Timevec@std@@QAE@PAX@Z
+ ??0_UShinit@std@@QAE@XZ
+ ??0_Winit@std@@QAE@XZ
+ ??0agent@Concurrency@@QAE@AAVScheduleGroup@1@@Z
+ ??0agent@Concurrency@@QAE@AAVScheduler@1@@Z
+ ??0agent@Concurrency@@QAE@XZ
+ ??0codecvt_base@std@@QAE@I@Z
+ ??0ctype_base@std@@QAE@I@Z
+ ??0facet@locale@std@@IAE@I@Z
+ ??0id@locale@std@@QAE@I@Z
+ ??0ios_base@std@@IAE@XZ
+ ??0ios_base@std@@QAE@ABV01@@Z
+ ??0time_base@std@@QAE@I@Z
+ ??1?$_Yarn@D@std@@QAE@XZ
+ ??1?$basic_ios@DU?$char_traits@D@std@@@std@@UAE@XZ
+ ??1?$basic_ios@GU?$char_traits@G@std@@@std@@UAE@XZ
+ ??1?$basic_ios@_WU?$char_traits@_W@std@@@std@@UAE@XZ
+ ??1?$basic_iostream@DU?$char_traits@D@std@@@std@@UAE@XZ
+ ??1?$basic_iostream@GU?$char_traits@G@std@@@std@@UAE@XZ
+ ??1?$basic_iostream@_WU?$char_traits@_W@std@@@std@@UAE@XZ
+ ??1?$basic_istream@DU?$char_traits@D@std@@@std@@UAE@XZ
+ ??1?$basic_istream@GU?$char_traits@G@std@@@std@@UAE@XZ
+ ??1?$basic_istream@_WU?$char_traits@_W@std@@@std@@UAE@XZ
+ ??1?$basic_ostream@DU?$char_traits@D@std@@@std@@UAE@XZ
+ ??1?$basic_ostream@GU?$char_traits@G@std@@@std@@UAE@XZ
+ ??1?$basic_ostream@_WU?$char_traits@_W@std@@@std@@UAE@XZ
+ ??1?$basic_streambuf@DU?$char_traits@D@std@@@std@@UAE@XZ
+ ??1?$basic_streambuf@GU?$char_traits@G@std@@@std@@UAE@XZ
+ ??1?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@UAE@XZ
+ ??1?$codecvt@DDH@std@@MAE@XZ
+ ??1?$codecvt@GDH@std@@MAE@XZ
+ ??1?$codecvt@_WDH@std@@MAE@XZ
+ ??1?$ctype@D@std@@MAE@XZ
+ ??1?$ctype@G@std@@MAE@XZ
+ ??1?$ctype@_W@std@@MAE@XZ
+ ??1?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ
+ ??1?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ
+ ??1?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ
+ ??1?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ
+ ??1?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ
+ ??1?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ
+ ??1?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ
+ ??1?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ
+ ??1?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ
+ ??1?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ
+ ??1?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ
+ ??1?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ
+ ??1Init@ios_base@std@@QAE@XZ
+ ??1_Concurrent_queue_base_v4@details@Concurrency@@MAE@XZ
+ ??1_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAE@XZ
+ ??1_Concurrent_vector_base_v4@details@Concurrency@@IAE@XZ
+ ??1_Container_base12@std@@QAE@XZ
+ ??1_Init_locks@std@@QAE@XZ
+ ??1_Locimp@locale@std@@MAE@XZ
+ ??1_Locinfo@std@@QAE@XZ
+ ??1_Lockit@std@@QAE@XZ
+ ??1_Mutex@std@@QAE@XZ
+ ??1_Timevec@std@@QAE@XZ
+ ??1_UShinit@std@@QAE@XZ
+ ??1_Winit@std@@QAE@XZ
+ ??1agent@Concurrency@@UAE@XZ
+ ??1codecvt_base@std@@UAE@XZ
+ ??1ctype_base@std@@UAE@XZ
+ ??1facet@locale@std@@UAE@XZ
+ ??1ios_base@std@@UAE@XZ
+ ??1time_base@std@@UAE@XZ
+ ??4?$_Iosb@H@std@@QAEAAV01@ABV01@@Z
+ ??4?$_Yarn@D@std@@QAEAAV01@ABV01@@Z
+ ??4?$_Yarn@D@std@@QAEAAV01@PBD@Z
+ ??4?$basic_iostream@DU?$char_traits@D@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_iostream@GU?$char_traits@G@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEAAV01@ABV01@@Z
+ ??4?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEAAV01@ABV01@@Z
+ ??4?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@C@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@D@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@E@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@F@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@G@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@H@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@I@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@J@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@K@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@M@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@N@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@O@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@_J@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@_K@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@_N@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@_W@std@@QAEAAV01@ABV01@@Z
+ ??4Init@ios_base@std@@QAEAAV012@ABV012@@Z
+ ??4_Container_base0@std@@QAEAAU01@ABU01@@Z
+ ??4_Container_base12@std@@QAEAAU01@ABU01@@Z
+ ??4_Init_locks@std@@QAEAAV01@ABV01@@Z
+ ??4_Num_base@std@@QAEAAU01@ABU01@@Z
+ ??4_Num_float_base@std@@QAEAAU01@ABU01@@Z
+ ??4_Num_int_base@std@@QAEAAU01@ABU01@@Z
+ ??4_Timevec@std@@QAEAAV01@ABV01@@Z
+ ??4_UShinit@std@@QAEAAV01@ABV01@@Z
+ ??4_Winit@std@@QAEAAV01@ABV01@@Z
+ ??4ios_base@std@@QAEAAV01@ABV01@@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAF@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAG@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAH@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAI@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAJ@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAK@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAM@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAN@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAO@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAPAX@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AA_J@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AA_K@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AA_N@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@DU?$char_traits@D@std@@@1@AAV21@@Z@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAF@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAG@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAH@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAI@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAJ@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAK@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAM@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAN@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAO@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAPAX@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AA_J@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AA_K@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AA_N@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@GU?$char_traits@G@std@@@1@AAV21@@Z@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAF@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAG@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAH@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAI@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAJ@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAK@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAM@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAN@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAO@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAPAX@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AA_J@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AA_K@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AA_N@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@_WU?$char_traits@_W@std@@@1@AAV21@@Z@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@F@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@G@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@I@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@J@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@K@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@M@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@N@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@O@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@DU?$char_traits@D@std@@@1@AAV21@@Z@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@PBX@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@_J@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@_K@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@_N@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@F@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@G@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@H@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@I@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@J@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@K@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@M@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@N@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@O@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@GU?$char_traits@G@std@@@1@AAV21@@Z@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@PBX@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@_J@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@_K@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@_N@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@F@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@G@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@H@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@I@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@J@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@K@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@M@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@N@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@O@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@_WU?$char_traits@_W@std@@@1@AAV21@@Z@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@PBX@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@_J@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@_K@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@_N@Z
+ ??7ios_base@std@@QBE_NXZ
+ ??Bid@locale@std@@QAEIXZ
+ ??Bios_base@std@@QBEPAXXZ
+ ??_7?$basic_ios@DU?$char_traits@D@std@@@std@@6B@
+ ??_7?$basic_ios@GU?$char_traits@G@std@@@std@@6B@
+ ??_7?$basic_ios@_WU?$char_traits@_W@std@@@std@@6B@
+ ??_7?$basic_iostream@DU?$char_traits@D@std@@@std@@6B@
+ ??_7?$basic_iostream@GU?$char_traits@G@std@@@std@@6B@
+ ??_7?$basic_iostream@_WU?$char_traits@_W@std@@@std@@6B@
+ ??_7?$basic_istream@DU?$char_traits@D@std@@@std@@6B@
+ ??_7?$basic_istream@GU?$char_traits@G@std@@@std@@6B@
+ ??_7?$basic_istream@_WU?$char_traits@_W@std@@@std@@6B@
+ ??_7?$basic_ostream@DU?$char_traits@D@std@@@std@@6B@
+ ??_7?$basic_ostream@GU?$char_traits@G@std@@@std@@6B@
+ ??_7?$basic_ostream@_WU?$char_traits@_W@std@@@std@@6B@
+ ??_7?$basic_streambuf@DU?$char_traits@D@std@@@std@@6B@
+ ??_7?$basic_streambuf@GU?$char_traits@G@std@@@std@@6B@
+ ??_7?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@6B@
+ ??_7?$codecvt@DDH@std@@6B@
+ ??_7?$codecvt@GDH@std@@6B@
+ ??_7?$codecvt@_WDH@std@@6B@
+ ??_7?$ctype@D@std@@6B@
+ ??_7?$ctype@G@std@@6B@
+ ??_7?$ctype@_W@std@@6B@
+ ??_7?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@
+ ??_7?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@
+ ??_7?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@
+ ??_7?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@
+ ??_7?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@
+ ??_7?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@
+ ??_7?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@
+ ??_7?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@
+ ??_7?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@
+ ??_7?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@
+ ??_7?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@
+ ??_7?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@
+ ??_7_Locimp@locale@std@@6B@
+ ??_7codecvt_base@std@@6B@
+ ??_7ctype_base@std@@6B@
+ ??_7ios_base@std@@6B@
+ ??_7time_base@std@@6B@
+ ??_8?$basic_iostream@DU?$char_traits@D@std@@@std@@7B?$basic_istream@DU?$char_traits@D@std@@@1@@
+ ??_8?$basic_iostream@DU?$char_traits@D@std@@@std@@7B?$basic_ostream@DU?$char_traits@D@std@@@1@@
+ ??_8?$basic_iostream@GU?$char_traits@G@std@@@std@@7B?$basic_istream@GU?$char_traits@G@std@@@1@@
+ ??_8?$basic_iostream@GU?$char_traits@G@std@@@std@@7B?$basic_ostream@GU?$char_traits@G@std@@@1@@
+ ??_8?$basic_iostream@_WU?$char_traits@_W@std@@@std@@7B?$basic_istream@_WU?$char_traits@_W@std@@@1@@
+ ??_8?$basic_iostream@_WU?$char_traits@_W@std@@@std@@7B?$basic_ostream@_WU?$char_traits@_W@std@@@1@@
+ ??_8?$basic_istream@DU?$char_traits@D@std@@@std@@7B@
+ ??_8?$basic_istream@GU?$char_traits@G@std@@@std@@7B@
+ ??_8?$basic_istream@_WU?$char_traits@_W@std@@@std@@7B@
+ ??_8?$basic_ostream@DU?$char_traits@D@std@@@std@@7B@
+ ??_8?$basic_ostream@GU?$char_traits@G@std@@@std@@7B@
+ ??_8?$basic_ostream@_WU?$char_traits@_W@std@@@std@@7B@
+ ??_D?$basic_iostream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ??_D?$basic_iostream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ??_D?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ??_D?$basic_istream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ??_D?$basic_istream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ??_D?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ??_D?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ??_D?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ??_D?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ??_F?$codecvt@DDH@std@@QAEXXZ
+ ??_F?$codecvt@GDH@std@@QAEXXZ
+ ??_F?$codecvt@_WDH@std@@QAEXXZ
+ ??_F?$ctype@D@std@@QAEXXZ
+ ??_F?$ctype@G@std@@QAEXXZ
+ ??_F?$ctype@_W@std@@QAEXXZ
+ ??_F?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ
+ ??_F?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ
+ ??_F?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ
+ ??_F?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ
+ ??_F?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ
+ ??_F?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ
+ ??_F_Locimp@locale@std@@QAEXXZ
+ ??_F_Locinfo@std@@QAEXXZ
+ ??_F_Timevec@std@@QAEXXZ
+ ??_Fcodecvt_base@std@@QAEXXZ
+ ??_Fctype_base@std@@QAEXXZ
+ ??_Ffacet@locale@std@@QAEXXZ
+ ??_Fid@locale@std@@QAEXXZ
+ ??_Ftime_base@std@@QAEXXZ
+ ?NFS_Allocate@details@Concurrency@@YAPAXIIPAX@Z
+ ?NFS_Free@details@Concurrency@@YAXPAX@Z
+ ?NFS_GetLineSize@details@Concurrency@@YAIXZ
+ ?_Addcats@_Locinfo@std@@QAEAAV12@HPBD@Z
+ ?_Addfac@_Locimp@locale@std@@AAEXPAVfacet@23@I@Z
+ ?_Addstd@ios_base@std@@SAXPAV12@@Z
+ ?_Advance@_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAEXXZ
+ ?_Assign@_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAEXABV123@@Z
+ ?_Atexit@@YAXP6AXXZ@Z
+ ?_BADOFF@std@@3_JB
+ ?_C_str@?$_Yarn@D@std@@QBEPBDXZ
+ ?_Callfns@ios_base@std@@AAEXW4event@12@@Z
+ ?_Clocptr@_Locimp@locale@std@@0PAV123@A
+ ?_Decref@facet@locale@std@@QAEPAV123@XZ
+ ?_Donarrow@?$ctype@G@std@@IBEDGD@Z
+ ?_Donarrow@?$ctype@_W@std@@IBED_WD@Z
+ ?_Dowiden@?$ctype@G@std@@IBEGD@Z
+ ?_Dowiden@?$ctype@_W@std@@IBE_WD@Z
+ ?_Empty@?$_Yarn@D@std@@QBE_NXZ
+ ?_Ffmt@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAPADPADDH@Z
+ ?_Ffmt@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAPADPADDH@Z
+ ?_Ffmt@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAPADPADDH@Z
+ ?_Findarr@ios_base@std@@AAEAAU_Iosarray@12@H@Z
+ ?_Fiopen@std@@YAPAU_iobuf@@PBDHH@Z
+ ?_Fiopen@std@@YAPAU_iobuf@@PBGHH@Z
+ ?_Fiopen@std@@YAPAU_iobuf@@PB_WHH@Z
+ ?_Fput@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBDIIII@Z
+ ?_Fput@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBDIIII@Z
+ ?_Fput@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBDIIII@Z
+ ?_GetCombinableSize@details@Concurrency@@YAIXZ
+ ?_GetCurrentThreadId@details@Concurrency@@YAKXZ
+ ?_Getcat@?$codecvt@DDH@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$codecvt@GDH@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$codecvt@_WDH@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$ctype@D@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$ctype@G@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$ctype@_W@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@facet@locale@std@@SAIPAPBV123@PBV23@@Z
+ ?_Getcoll@_Locinfo@std@@QBE?AU_Collvec@@XZ
+ ?_Getctype@_Locinfo@std@@QBE?AU_Ctypevec@@XZ
+ ?_Getcvt@_Locinfo@std@@QBE?AU_Cvtvec@@XZ
+ ?_Getdateorder@_Locinfo@std@@QBEHXZ
+ ?_Getdays@_Locinfo@std@@QBEPBDXZ
+ ?_Getfalse@_Locinfo@std@@QBEPBDXZ
+ ?_Getffld@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getffld@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getffld@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getffldx@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getffldx@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getffldx@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getfmt@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD@Z
+ ?_Getfmt@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD@Z
+ ?_Getfmt@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD@Z
+ ?_Getgloballocale@locale@std@@CAPAV_Locimp@12@XZ
+ ?_Getifld@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@1HABVlocale@2@@Z
+ ?_Getifld@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@1HABVlocale@2@@Z
+ ?_Getifld@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@1HABVlocale@2@@Z
+ ?_Getint@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@0HHAAH@Z
+ ?_Getint@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@0HHAAH@Z
+ ?_Getint@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@0HHAAH@Z
+ ?_Getlconv@_Locinfo@std@@QBEPBUlconv@@XZ
+ ?_Getmonths@_Locinfo@std@@QBEPBDXZ
+ ?_Getname@_Locinfo@std@@QBEPBDXZ
+ ?_Getpfirst@_Container_base12@std@@QBEPAPAU_Iterator_base12@2@XZ
+ ?_Getptr@_Timevec@std@@QBEPAXXZ
+ ?_Gettnames@_Locinfo@std@@QBE?AV_Timevec@2@XZ
+ ?_Gettrue@_Locinfo@std@@QBEPBDXZ
+ ?_Gnavail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBE_JXZ
+ ?_Gnavail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBE_JXZ
+ ?_Gnavail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBE_JXZ
+ ?_Gndec@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ
+ ?_Gndec@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ
+ ?_Gndec@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ
+ ?_Gninc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ
+ ?_Gninc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ
+ ?_Gninc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ
+ ?_Gnpreinc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ
+ ?_Gnpreinc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ
+ ?_Gnpreinc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ
+ ?_Hexdig@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHDDDD@Z
+ ?_Hexdig@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHGGGG@Z
+ ?_Hexdig@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAH_W000@Z
+ ?_Id_cnt@id@locale@std@@0HA
+ ?_Ifmt@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAPADPADPBDH@Z
+ ?_Ifmt@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAPADPADPBDH@Z
+ ?_Ifmt@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAPADPADPBDH@Z
+ ?_Incref@facet@locale@std@@QAEXXZ
+ ?_Index@ios_base@std@@0HA
+ ?_Init@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAPAD0PAH001@Z
+ ?_Init@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXXZ
+ ?_Init@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAPAG0PAH001@Z
+ ?_Init@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXXZ
+ ?_Init@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPAPA_W0PAH001@Z
+ ?_Init@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXXZ
+ ?_Init@?$codecvt@DDH@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$codecvt@GDH@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$codecvt@_WDH@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$ctype@D@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$ctype@G@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$ctype@_W@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@ios_base@std@@IAEXXZ
+ ?_Init@locale@std@@CAPAV_Locimp@12@XZ
+ ?_Init_cnt@Init@ios_base@std@@0HA
+ ?_Init_cnt@_UShinit@std@@0HA
+ ?_Init_cnt@_Winit@std@@0HA
+ ?_Init_cnt_func@Init@ios_base@std@@CAAAHXZ
+ ?_Init_ctor@Init@ios_base@std@@CAXPAV123@@Z
+ ?_Init_dtor@Init@ios_base@std@@CAXPAV123@@Z
+ ?_Init_locks_ctor@_Init_locks@std@@CAXPAV12@@Z
+ ?_Init_locks_dtor@_Init_locks@std@@CAXPAV12@@Z
+ ?_Internal_assign@_Concurrent_vector_base_v4@details@Concurrency@@IAEXABV123@IP6AXPAXI@ZP6AX1PBXI@Z4@Z
+ ?_Internal_capacity@_Concurrent_vector_base_v4@details@Concurrency@@IBEIXZ
+ ?_Internal_clear@_Concurrent_vector_base_v4@details@Concurrency@@IAEIP6AXPAXI@Z@Z
+ ?_Internal_compact@_Concurrent_vector_base_v4@details@Concurrency@@IAEPAXIPAXP6AX0I@ZP6AX0PBXI@Z@Z
+ ?_Internal_copy@_Concurrent_vector_base_v4@details@Concurrency@@IAEXABV123@IP6AXPAXPBXI@Z@Z
+ ?_Internal_empty@_Concurrent_queue_base_v4@details@Concurrency@@IBE_NXZ
+ ?_Internal_finish_clear@_Concurrent_queue_base_v4@details@Concurrency@@IAEXXZ
+ ?_Internal_grow_by@_Concurrent_vector_base_v4@details@Concurrency@@IAEIIIP6AXPAXPBXI@Z1@Z
+ ?_Internal_grow_to_at_least_with_result@_Concurrent_vector_base_v4@details@Concurrency@@IAEIIIP6AXPAXPBXI@Z1@Z
+ ?_Internal_pop_if_present@_Concurrent_queue_base_v4@details@Concurrency@@IAE_NPAX@Z
+ ?_Internal_push@_Concurrent_queue_base_v4@details@Concurrency@@IAEXPBX@Z
+ ?_Internal_push_back@_Concurrent_vector_base_v4@details@Concurrency@@IAEPAXIAAI@Z
+ ?_Internal_reserve@_Concurrent_vector_base_v4@details@Concurrency@@IAEXIII@Z
+ ?_Internal_resize@_Concurrent_vector_base_v4@details@Concurrency@@IAEXIIIP6AXPAXI@ZP6AX0PBXI@Z2@Z
+ ?_Internal_size@_Concurrent_queue_base_v4@details@Concurrency@@IBEIXZ
+ ?_Internal_swap@_Concurrent_vector_base_v4@details@Concurrency@@IAEXAAV123@@Z
+ ?_Internal_throw_exception@_Concurrent_queue_base_v4@details@Concurrency@@IBEXXZ
+ ?_Internal_throw_exception@_Concurrent_vector_base_v4@details@Concurrency@@IBEXI@Z
+ ?_Ios_base_dtor@ios_base@std@@CAXPAV12@@Z
+ ?_Ipfx@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE_N_N@Z
+ ?_Ipfx@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE_N_N@Z
+ ?_Ipfx@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE_N_N@Z
+ ?_Iput@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPADI@Z
+ ?_Iput@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPADI@Z
+ ?_Iput@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPADI@Z
+ ?_Locimp_Addfac@_Locimp@locale@std@@CAXPAV123@PAVfacet@23@I@Z
+ ?_Locimp_ctor@_Locimp@locale@std@@CAXPAV123@ABV123@@Z
+ ?_Locimp_dtor@_Locimp@locale@std@@CAXPAV123@@Z
+ ?_Locinfo_Addcats@_Locinfo@std@@SAAAV12@PAV12@HPBD@Z
+ ?_Locinfo_ctor@_Locinfo@std@@SAXPAV12@HPBD@Z
+ ?_Locinfo_ctor@_Locinfo@std@@SAXPAV12@PBD@Z
+ ?_Locinfo_dtor@_Locinfo@std@@SAXPAV12@@Z
+ ?_Lock@?$basic_streambuf@DU?$char_traits@D@std@@@std@@UAEXXZ
+ ?_Lock@?$basic_streambuf@GU?$char_traits@G@std@@@std@@UAEXXZ
+ ?_Lock@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@UAEXXZ
+ ?_Lock@_Mutex@std@@QAEXXZ
+ ?_Lockit_ctor@_Lockit@std@@CAXPAV12@@Z
+ ?_Lockit_ctor@_Lockit@std@@CAXPAV12@H@Z
+ ?_Lockit_ctor@_Lockit@std@@SAXH@Z
+ ?_Lockit_dtor@_Lockit@std@@CAXPAV12@@Z
+ ?_Lockit_dtor@_Lockit@std@@SAXH@Z
+ ?_MP_Add@tr1@std@@YAXQA_K_K@Z
+ ?_MP_Get@tr1@std@@YA_KQA_K@Z
+ ?_MP_Mul@tr1@std@@YAXQA_K_K1@Z
+ ?_MP_Rem@tr1@std@@YAXQA_K_K@Z
+ ?_Makeloc@_Locimp@locale@std@@CAPAV123@ABV_Locinfo@3@HPAV123@PBV23@@Z
+ ?_Makeushloc@_Locimp@locale@std@@CAXABV_Locinfo@3@HPAV123@PBV23@@Z
+ ?_Makewloc@_Locimp@locale@std@@CAXABV_Locinfo@3@HPAV123@PBV23@@Z
+ ?_Makexloc@_Locimp@locale@std@@CAXABV_Locinfo@3@HPAV123@PBV23@@Z
+ ?_Mtx_delete@threads@stdext@@YAXPAX@Z
+ ?_Mtx_lock@threads@stdext@@YAXPAX@Z
+ ?_Mtx_new@threads@stdext@@YAXAAPAX@Z
+ ?_Mtx_unlock@threads@stdext@@YAXPAX@Z
+ ?_Mutex_Lock@_Mutex@std@@CAXPAV12@@Z
+ ?_Mutex_Unlock@_Mutex@std@@CAXPAV12@@Z
+ ?_Mutex_ctor@_Mutex@std@@CAXPAV12@@Z
+ ?_Mutex_dtor@_Mutex@std@@CAXPAV12@@Z
+ ?_Nomemory@std@@YAXXZ
+ ?_Orphan_all@_Container_base0@std@@QAEXXZ
+ ?_Orphan_all@_Container_base12@std@@QAEXXZ
+ ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ?_Osfx@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ?_Osfx@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ?_Pnavail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBE_JXZ
+ ?_Pnavail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBE_JXZ
+ ?_Pnavail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBE_JXZ
+ ?_Pninc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ
+ ?_Pninc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ
+ ?_Pninc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ
+ ?_Ptr_cerr@std@@3PAV?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?_Ptr_cin@std@@3PAV?$basic_istream@DU?$char_traits@D@std@@@1@A
+ ?_Ptr_clog@std@@3PAV?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?_Ptr_cout@std@@3PAV?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?_Ptr_wcerr@std@@3PAV?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?_Ptr_wcerr@std@@3PAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?_Ptr_wcin@std@@3PAV?$basic_istream@GU?$char_traits@G@std@@@1@A
+ ?_Ptr_wcin@std@@3PAV?$basic_istream@_WU?$char_traits@_W@std@@@1@A
+ ?_Ptr_wclog@std@@3PAV?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?_Ptr_wclog@std@@3PAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?_Ptr_wcout@std@@3PAV?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?_Ptr_wcout@std@@3PAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?_Put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@PBDI@Z
+ ?_Put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@PBGI@Z
+ ?_Put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@PB_WI@Z
+ ?_Putc@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@PBDI@Z
+ ?_Putc@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@PBDI@Z
+ ?_Putc@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@PBDI@Z
+ ?_Putgrouped@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@PBDID@Z
+ ?_Putgrouped@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@PBDIG@Z
+ ?_Putgrouped@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@PBDI_W@Z
+ ?_Raise_handler@std@@3P6AXABVexception@stdext@@@ZA
+ ?_Random_device@tr1@std@@YAIXZ
+ ?_Rep@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@DI@Z
+ ?_Rep@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@GI@Z
+ ?_Rep@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@_WI@Z
+ ?_Rng_abort@tr1@std@@YAXPBD@Z
+ ?_Segment_index_of@_Concurrent_vector_base_v4@details@Concurrency@@KAII@Z
+ ?_Setgloballocale@locale@std@@CAXPAX@Z
+ ?_Swap_all@_Container_base0@std@@QAEXAAU12@@Z
+ ?_Swap_all@_Container_base12@std@@QAEXAAU12@@Z
+ ?_Sync@ios_base@std@@0_NA
+ ?_Tidy@?$_Yarn@D@std@@AAEXXZ
+ ?_Tidy@?$ctype@D@std@@IAEXXZ
+ ?_Tidy@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@AAEXXZ
+ ?_Tidy@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@AAEXXZ
+ ?_Tidy@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@AAEXXZ
+ ?_Tidy@ios_base@std@@AAEXXZ
+ ?_Unlock@?$basic_streambuf@DU?$char_traits@D@std@@@std@@UAEXXZ
+ ?_Unlock@?$basic_streambuf@GU?$char_traits@G@std@@@std@@UAEXXZ
+ ?_Unlock@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@UAEXXZ
+ ?_Unlock@_Mutex@std@@QAEXXZ
+ ?_XLgamma@tr1@std@@YAMM@Z
+ ?_XLgamma@tr1@std@@YANN@Z
+ ?_XLgamma@tr1@std@@YAOO@Z
+ ?_Xbad@tr1@std@@YAXW4error_type@regex_constants@12@@Z
+ ?_Xfunc@tr1@std@@YAXXZ
+ ?_Xinvalid_argument@std@@YAXPBD@Z
+ ?_Xlength_error@std@@YAXPBD@Z
+ ?_Xmem@tr1@std@@YAXXZ
+ ?_Xout_of_range@std@@YAXPBD@Z
+ ?_Xoverflow_error@std@@YAXPBD@Z
+ ?_Xruntime_error@std@@YAXPBD@Z
+ ?always_noconv@codecvt_base@std@@QBE_NXZ
+ ?bad@ios_base@std@@QBE_NXZ
+ ?c_str@?$_Yarn@D@std@@QBEPBDXZ
+ ?cancel@agent@Concurrency@@QAE_NXZ
+ ?cerr@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?cin@std@@3V?$basic_istream@DU?$char_traits@D@std@@@1@A
+ ?classic@locale@std@@SAABV12@XZ
+ ?classic_table@?$ctype@D@std@@SAPBFXZ
+ ?clear@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXH_N@Z
+ ?clear@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXI@Z
+ ?clear@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXH_N@Z
+ ?clear@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXI@Z
+ ?clear@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXH_N@Z
+ ?clear@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXI@Z
+ ?clear@ios_base@std@@QAEXH@Z
+ ?clear@ios_base@std@@QAEXH_N@Z
+ ?clear@ios_base@std@@QAEXI@Z
+ ?clog@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?copyfmt@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEAAV12@ABV12@@Z
+ ?copyfmt@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEAAV12@ABV12@@Z
+ ?copyfmt@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEAAV12@ABV12@@Z
+ ?copyfmt@ios_base@std@@QAEAAV12@ABV12@@Z
+ ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?date_order@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AW4dateorder@time_base@2@XZ
+ ?date_order@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AW4dateorder@time_base@2@XZ
+ ?date_order@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AW4dateorder@time_base@2@XZ
+ ?denorm_min@?$numeric_limits@C@std@@SACXZ
+ ?denorm_min@?$numeric_limits@D@std@@SADXZ
+ ?denorm_min@?$numeric_limits@E@std@@SAEXZ
+ ?denorm_min@?$numeric_limits@F@std@@SAFXZ
+ ?denorm_min@?$numeric_limits@G@std@@SAGXZ
+ ?denorm_min@?$numeric_limits@H@std@@SAHXZ
+ ?denorm_min@?$numeric_limits@I@std@@SAIXZ
+ ?denorm_min@?$numeric_limits@J@std@@SAJXZ
+ ?denorm_min@?$numeric_limits@K@std@@SAKXZ
+ ?denorm_min@?$numeric_limits@M@std@@SAMXZ
+ ?denorm_min@?$numeric_limits@N@std@@SANXZ
+ ?denorm_min@?$numeric_limits@O@std@@SAOXZ
+ ?denorm_min@?$numeric_limits@_J@std@@SA_JXZ
+ ?denorm_min@?$numeric_limits@_K@std@@SA_KXZ
+ ?denorm_min@?$numeric_limits@_N@std@@SA_NXZ
+ ?denorm_min@?$numeric_limits@_W@std@@SA_WXZ
+ ?digits10@?$numeric_limits@C@std@@2HB
+ ?digits10@?$numeric_limits@D@std@@2HB
+ ?digits10@?$numeric_limits@E@std@@2HB
+ ?digits10@?$numeric_limits@F@std@@2HB
+ ?digits10@?$numeric_limits@G@std@@2HB
+ ?digits10@?$numeric_limits@H@std@@2HB
+ ?digits10@?$numeric_limits@I@std@@2HB
+ ?digits10@?$numeric_limits@J@std@@2HB
+ ?digits10@?$numeric_limits@K@std@@2HB
+ ?digits10@?$numeric_limits@M@std@@2HB
+ ?digits10@?$numeric_limits@N@std@@2HB
+ ?digits10@?$numeric_limits@O@std@@2HB
+ ?digits10@?$numeric_limits@_J@std@@2HB
+ ?digits10@?$numeric_limits@_K@std@@2HB
+ ?digits10@?$numeric_limits@_N@std@@2HB
+ ?digits10@?$numeric_limits@_W@std@@2HB
+ ?digits10@_Num_base@std@@2HB
+ ?digits@?$numeric_limits@C@std@@2HB
+ ?digits@?$numeric_limits@D@std@@2HB
+ ?digits@?$numeric_limits@E@std@@2HB
+ ?digits@?$numeric_limits@F@std@@2HB
+ ?digits@?$numeric_limits@G@std@@2HB
+ ?digits@?$numeric_limits@H@std@@2HB
+ ?digits@?$numeric_limits@I@std@@2HB
+ ?digits@?$numeric_limits@J@std@@2HB
+ ?digits@?$numeric_limits@K@std@@2HB
+ ?digits@?$numeric_limits@M@std@@2HB
+ ?digits@?$numeric_limits@N@std@@2HB
+ ?digits@?$numeric_limits@O@std@@2HB
+ ?digits@?$numeric_limits@_J@std@@2HB
+ ?digits@?$numeric_limits@_K@std@@2HB
+ ?digits@?$numeric_limits@_N@std@@2HB
+ ?digits@?$numeric_limits@_W@std@@2HB
+ ?digits@_Num_base@std@@2HB
+ ?do_always_noconv@?$codecvt@GDH@std@@MBE_NXZ
+ ?do_always_noconv@?$codecvt@_WDH@std@@MBE_NXZ
+ ?do_always_noconv@codecvt_base@std@@MBE_NXZ
+ ?do_date_order@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AW4dateorder@time_base@2@XZ
+ ?do_date_order@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AW4dateorder@time_base@2@XZ
+ ?do_date_order@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AW4dateorder@time_base@2@XZ
+ ?do_encoding@codecvt_base@std@@MBEHXZ
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?do_get@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?do_get@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?do_get@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?do_get_date@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_date@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_date@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_monthname@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_monthname@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_monthname@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_time@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_time@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_time@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_weekday@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_weekday@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_weekday@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_year@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_year@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_year@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_in@?$codecvt@DDH@std@@MBEHAAHPBD1AAPBDPAD3AAPAD@Z
+ ?do_in@?$codecvt@GDH@std@@MBEHAAHPBD1AAPBDPAG3AAPAG@Z
+ ?do_in@?$codecvt@_WDH@std@@MBEHAAHPBD1AAPBDPA_W3AAPA_W@Z
+ ?do_is@?$ctype@G@std@@MBEPBGPBG0PAF@Z
+ ?do_is@?$ctype@G@std@@MBE_NFG@Z
+ ?do_is@?$ctype@_W@std@@MBEPB_WPB_W0PAF@Z
+ ?do_is@?$ctype@_W@std@@MBE_NF_W@Z
+ ?do_length@?$codecvt@DDH@std@@MBEHABHPBD1I@Z
+ ?do_length@?$codecvt@GDH@std@@MBEHABHPBD1I@Z
+ ?do_length@?$codecvt@_WDH@std@@MBEHABHPBD1I@Z
+ ?do_max_length@?$codecvt@GDH@std@@MBEHXZ
+ ?do_max_length@?$codecvt@_WDH@std@@MBEHXZ
+ ?do_max_length@codecvt_base@std@@MBEHXZ
+ ?do_narrow@?$ctype@D@std@@MBEDDD@Z
+ ?do_narrow@?$ctype@D@std@@MBEPBDPBD0DPAD@Z
+ ?do_narrow@?$ctype@G@std@@MBEDGD@Z
+ ?do_narrow@?$ctype@G@std@@MBEPBGPBG0DPAD@Z
+ ?do_narrow@?$ctype@_W@std@@MBED_WD@Z
+ ?do_narrow@?$ctype@_W@std@@MBEPB_WPB_W0DPAD@Z
+ ?do_out@?$codecvt@DDH@std@@MBEHAAHPBD1AAPBDPAD3AAPAD@Z
+ ?do_out@?$codecvt@GDH@std@@MBEHAAHPBG1AAPBGPAD3AAPAD@Z
+ ?do_out@?$codecvt@_WDH@std@@MBEHAAHPB_W1AAPB_WPAD3AAPAD@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DJ@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DK@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DN@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DO@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBX@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_J@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_K@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_N@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GJ@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GK@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GN@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GO@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBX@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_J@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_K@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_N@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WJ@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WK@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WN@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WO@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBX@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_J@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_K@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_N@Z
+ ?do_put@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBUtm@@DD@Z
+ ?do_put@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBUtm@@DD@Z
+ ?do_put@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBUtm@@DD@Z
+ ?do_scan_is@?$ctype@G@std@@MBEPBGFPBG0@Z
+ ?do_scan_is@?$ctype@_W@std@@MBEPB_WFPB_W0@Z
+ ?do_scan_not@?$ctype@G@std@@MBEPBGFPBG0@Z
+ ?do_scan_not@?$ctype@_W@std@@MBEPB_WFPB_W0@Z
+ ?do_tolower@?$ctype@D@std@@MBEDD@Z
+ ?do_tolower@?$ctype@D@std@@MBEPBDPADPBD@Z
+ ?do_tolower@?$ctype@G@std@@MBEGG@Z
+ ?do_tolower@?$ctype@G@std@@MBEPBGPAGPBG@Z
+ ?do_tolower@?$ctype@_W@std@@MBEPB_WPA_WPB_W@Z
+ ?do_tolower@?$ctype@_W@std@@MBE_W_W@Z
+ ?do_toupper@?$ctype@D@std@@MBEDD@Z
+ ?do_toupper@?$ctype@D@std@@MBEPBDPADPBD@Z
+ ?do_toupper@?$ctype@G@std@@MBEGG@Z
+ ?do_toupper@?$ctype@G@std@@MBEPBGPAGPBG@Z
+ ?do_toupper@?$ctype@_W@std@@MBEPB_WPA_WPB_W@Z
+ ?do_toupper@?$ctype@_W@std@@MBE_W_W@Z
+ ?do_unshift@?$codecvt@DDH@std@@MBEHAAHPAD1AAPAD@Z
+ ?do_unshift@?$codecvt@GDH@std@@MBEHAAHPAD1AAPAD@Z
+ ?do_unshift@?$codecvt@_WDH@std@@MBEHAAHPAD1AAPAD@Z
+ ?do_widen@?$ctype@D@std@@MBEDD@Z
+ ?do_widen@?$ctype@D@std@@MBEPBDPBD0PAD@Z
+ ?do_widen@?$ctype@G@std@@MBEGD@Z
+ ?do_widen@?$ctype@G@std@@MBEPBDPBD0PAG@Z
+ ?do_widen@?$ctype@_W@std@@MBEPBDPBD0PA_W@Z
+ ?do_widen@?$ctype@_W@std@@MBE_WD@Z
+ ?done@agent@Concurrency@@IAE_NXZ
+ ?eback@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?eback@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?eback@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?egptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?egptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?egptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?empty@?$_Yarn@D@std@@QBE_NXZ
+ ?empty@locale@std@@SA?AV12@XZ
+ ?encoding@codecvt_base@std@@QBEHXZ
+ ?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
+ ?endl@std@@YAAAV?$basic_ostream@GU?$char_traits@G@std@@@1@AAV21@@Z
+ ?endl@std@@YAAAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@AAV21@@Z
+ ?ends@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
+ ?ends@std@@YAAAV?$basic_ostream@GU?$char_traits@G@std@@@1@AAV21@@Z
+ ?ends@std@@YAAAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@AAV21@@Z
+ ?eof@ios_base@std@@QBE_NXZ
+ ?epptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?epptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?epptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?epsilon@?$numeric_limits@C@std@@SACXZ
+ ?epsilon@?$numeric_limits@D@std@@SADXZ
+ ?epsilon@?$numeric_limits@E@std@@SAEXZ
+ ?epsilon@?$numeric_limits@F@std@@SAFXZ
+ ?epsilon@?$numeric_limits@G@std@@SAGXZ
+ ?epsilon@?$numeric_limits@H@std@@SAHXZ
+ ?epsilon@?$numeric_limits@I@std@@SAIXZ
+ ?epsilon@?$numeric_limits@J@std@@SAJXZ
+ ?epsilon@?$numeric_limits@K@std@@SAKXZ
+ ?epsilon@?$numeric_limits@M@std@@SAMXZ
+ ?epsilon@?$numeric_limits@N@std@@SANXZ
+ ?epsilon@?$numeric_limits@O@std@@SAOXZ
+ ?epsilon@?$numeric_limits@_J@std@@SA_JXZ
+ ?epsilon@?$numeric_limits@_K@std@@SA_KXZ
+ ?epsilon@?$numeric_limits@_N@std@@SA_NXZ
+ ?epsilon@?$numeric_limits@_W@std@@SA_WXZ
+ ?exceptions@ios_base@std@@QAEXH@Z
+ ?exceptions@ios_base@std@@QAEXI@Z
+ ?exceptions@ios_base@std@@QBEHXZ
+ ?fail@ios_base@std@@QBE_NXZ
+ ?fill@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEDD@Z
+ ?fill@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDXZ
+ ?fill@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEGG@Z
+ ?fill@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEGXZ
+ ?fill@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAE_W_W@Z
+ ?fill@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBE_WXZ
+ ?flags@ios_base@std@@QAEHH@Z
+ ?flags@ios_base@std@@QBEHXZ
+ ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ
+ ?flush@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@XZ
+ ?flush@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@XZ
+ ?flush@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
+ ?flush@std@@YAAAV?$basic_ostream@GU?$char_traits@G@std@@@1@AAV21@@Z
+ ?flush@std@@YAAAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@AAV21@@Z
+ ?gbump@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXH@Z
+ ?gbump@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXH@Z
+ ?gbump@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXH@Z
+ ?gcount@?$basic_istream@DU?$char_traits@D@std@@@std@@QBE_JXZ
+ ?gcount@?$basic_istream@GU?$char_traits@G@std@@@std@@QBE_JXZ
+ ?gcount@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QBE_JXZ
+ ?generic_category@std@@YAABVerror_category@1@XZ
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@AAD@Z
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@AAV?$basic_streambuf@DU?$char_traits@D@std@@@2@@Z
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@AAV?$basic_streambuf@DU?$char_traits@D@std@@@2@D@Z
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_J@Z
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_JD@Z
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@AAG@Z
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@AAV?$basic_streambuf@GU?$char_traits@G@std@@@2@@Z
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@AAV?$basic_streambuf@GU?$char_traits@G@std@@@2@G@Z
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_J@Z
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_JG@Z
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@AAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@@Z
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@AAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@_W@Z
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@AA_W@Z
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J@Z
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J_W@Z
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?get@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?get@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD4@Z
+ ?get@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?get@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBG4@Z
+ ?get@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?get@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PB_W4@Z
+ ?get_date@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_date@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_date@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_monthname@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_monthname@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_monthname@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_time@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_time@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_time@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_weekday@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_weekday@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_weekday@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_year@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_year@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_year@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?getline@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_J@Z
+ ?getline@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_JD@Z
+ ?getline@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_J@Z
+ ?getline@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_JG@Z
+ ?getline@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J@Z
+ ?getline@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J_W@Z
+ ?getloc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QBE?AVlocale@2@XZ
+ ?getloc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QBE?AVlocale@2@XZ
+ ?getloc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QBE?AVlocale@2@XZ
+ ?getloc@ios_base@std@@QBE?AVlocale@2@XZ
+ ?global@locale@std@@SA?AV12@ABV12@@Z
+ ?good@ios_base@std@@QBE_NXZ
+ ?gptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?gptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?gptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?has_denorm@_Num_base@std@@2W4float_denorm_style@2@B
+ ?has_denorm@_Num_float_base@std@@2W4float_denorm_style@2@B
+ ?has_denorm_loss@_Num_base@std@@2_NB
+ ?has_denorm_loss@_Num_float_base@std@@2_NB
+ ?has_infinity@_Num_base@std@@2_NB
+ ?has_infinity@_Num_float_base@std@@2_NB
+ ?has_quiet_NaN@_Num_base@std@@2_NB
+ ?has_quiet_NaN@_Num_float_base@std@@2_NB
+ ?has_signaling_NaN@_Num_base@std@@2_NB
+ ?has_signaling_NaN@_Num_float_base@std@@2_NB
+ ?id@?$codecvt@DDH@std@@2V0locale@2@A
+ ?id@?$codecvt@GDH@std@@2V0locale@2@A
+ ?id@?$codecvt@_WDH@std@@2V0locale@2@A
+ ?id@?$collate@D@std@@2V0locale@2@A
+ ?id@?$collate@G@std@@2V0locale@2@A
+ ?id@?$collate@_W@std@@2V0locale@2@A
+ ?id@?$ctype@D@std@@2V0locale@2@A
+ ?id@?$ctype@G@std@@2V0locale@2@A
+ ?id@?$ctype@_W@std@@2V0locale@2@A
+ ?id@?$messages@D@std@@2V0locale@2@A
+ ?id@?$messages@G@std@@2V0locale@2@A
+ ?id@?$messages@_W@std@@2V0locale@2@A
+ ?id@?$money_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$money_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$money_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$money_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$money_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$money_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$moneypunct@D$00@std@@2V0locale@2@A
+ ?id@?$moneypunct@D$0A@@std@@2V0locale@2@A
+ ?id@?$moneypunct@G$00@std@@2V0locale@2@A
+ ?id@?$moneypunct@G$0A@@std@@2V0locale@2@A
+ ?id@?$moneypunct@_W$00@std@@2V0locale@2@A
+ ?id@?$moneypunct@_W$0A@@std@@2V0locale@2@A
+ ?id@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$numpunct@D@std@@2V0locale@2@A
+ ?id@?$numpunct@G@std@@2V0locale@2@A
+ ?id@?$numpunct@_W@std@@2V0locale@2@A
+ ?id@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?ignore@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@_JH@Z
+ ?ignore@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@_JG@Z
+ ?ignore@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_JG@Z
+ ?imbue@?$basic_ios@DU?$char_traits@D@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?imbue@?$basic_ios@GU?$char_traits@G@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?imbue@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?imbue@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEXABVlocale@2@@Z
+ ?imbue@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEXABVlocale@2@@Z
+ ?imbue@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEXABVlocale@2@@Z
+ ?imbue@ios_base@std@@QAE?AVlocale@2@ABV32@@Z
+ ?in@?$codecvt@DDH@std@@QBEHAAHPBD1AAPBDPAD3AAPAD@Z
+ ?in@?$codecvt@GDH@std@@QBEHAAHPBD1AAPBDPAG3AAPAG@Z
+ ?in@?$codecvt@_WDH@std@@QBEHAAHPBD1AAPBDPA_W3AAPA_W@Z
+ ?in_avail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE_JXZ
+ ?in_avail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE_JXZ
+ ?in_avail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE_JXZ
+ ?infinity@?$numeric_limits@C@std@@SACXZ
+ ?infinity@?$numeric_limits@D@std@@SADXZ
+ ?infinity@?$numeric_limits@E@std@@SAEXZ
+ ?infinity@?$numeric_limits@F@std@@SAFXZ
+ ?infinity@?$numeric_limits@G@std@@SAGXZ
+ ?infinity@?$numeric_limits@H@std@@SAHXZ
+ ?infinity@?$numeric_limits@I@std@@SAIXZ
+ ?infinity@?$numeric_limits@J@std@@SAJXZ
+ ?infinity@?$numeric_limits@K@std@@SAKXZ
+ ?infinity@?$numeric_limits@M@std@@SAMXZ
+ ?infinity@?$numeric_limits@N@std@@SANXZ
+ ?infinity@?$numeric_limits@O@std@@SAOXZ
+ ?infinity@?$numeric_limits@_J@std@@SA_JXZ
+ ?infinity@?$numeric_limits@_K@std@@SA_KXZ
+ ?infinity@?$numeric_limits@_N@std@@SA_NXZ
+ ?infinity@?$numeric_limits@_W@std@@SA_WXZ
+ ?init@?$basic_ios@DU?$char_traits@D@std@@@std@@IAEXPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@_N@Z
+ ?init@?$basic_ios@GU?$char_traits@G@std@@@std@@IAEXPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@_N@Z
+ ?init@?$basic_ios@_WU?$char_traits@_W@std@@@std@@IAEXPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@_N@Z
+ ?intl@?$moneypunct@D$00@std@@2_NB
+ ?intl@?$moneypunct@D$0A@@std@@2_NB
+ ?intl@?$moneypunct@G$00@std@@2_NB
+ ?intl@?$moneypunct@G$0A@@std@@2_NB
+ ?intl@?$moneypunct@_W$00@std@@2_NB
+ ?intl@?$moneypunct@_W$0A@@std@@2_NB
+ ?iostream_category@std@@YAABVerror_category@1@XZ
+ ?ipfx@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE_N_N@Z
+ ?ipfx@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE_N_N@Z
+ ?ipfx@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE_N_N@Z
+ ?is@?$ctype@D@std@@QBEPBDPBD0PAF@Z
+ ?is@?$ctype@D@std@@QBE_NFD@Z
+ ?is@?$ctype@G@std@@QBEPBGPBG0PAF@Z
+ ?is@?$ctype@G@std@@QBE_NFG@Z
+ ?is@?$ctype@_W@std@@QBEPB_WPB_W0PAF@Z
+ ?is@?$ctype@_W@std@@QBE_NF_W@Z
+ ?is_bounded@_Num_base@std@@2_NB
+ ?is_bounded@_Num_float_base@std@@2_NB
+ ?is_bounded@_Num_int_base@std@@2_NB
+ ?is_current_task_group_canceling@Concurrency@@YA_NXZ
+ ?is_exact@_Num_base@std@@2_NB
+ ?is_exact@_Num_float_base@std@@2_NB
+ ?is_exact@_Num_int_base@std@@2_NB
+ ?is_iec559@_Num_base@std@@2_NB
+ ?is_iec559@_Num_float_base@std@@2_NB
+ ?is_integer@_Num_base@std@@2_NB
+ ?is_integer@_Num_float_base@std@@2_NB
+ ?is_integer@_Num_int_base@std@@2_NB
+ ?is_modulo@?$numeric_limits@_N@std@@2_NB
+ ?is_modulo@_Num_base@std@@2_NB
+ ?is_modulo@_Num_float_base@std@@2_NB
+ ?is_modulo@_Num_int_base@std@@2_NB
+ ?is_signed@?$numeric_limits@C@std@@2_NB
+ ?is_signed@?$numeric_limits@D@std@@2_NB
+ ?is_signed@?$numeric_limits@E@std@@2_NB
+ ?is_signed@?$numeric_limits@F@std@@2_NB
+ ?is_signed@?$numeric_limits@G@std@@2_NB
+ ?is_signed@?$numeric_limits@H@std@@2_NB
+ ?is_signed@?$numeric_limits@I@std@@2_NB
+ ?is_signed@?$numeric_limits@J@std@@2_NB
+ ?is_signed@?$numeric_limits@K@std@@2_NB
+ ?is_signed@?$numeric_limits@_J@std@@2_NB
+ ?is_signed@?$numeric_limits@_K@std@@2_NB
+ ?is_signed@?$numeric_limits@_N@std@@2_NB
+ ?is_signed@?$numeric_limits@_W@std@@2_NB
+ ?is_signed@_Num_base@std@@2_NB
+ ?is_signed@_Num_float_base@std@@2_NB
+ ?is_specialized@_Num_base@std@@2_NB
+ ?is_specialized@_Num_float_base@std@@2_NB
+ ?is_specialized@_Num_int_base@std@@2_NB
+ ?isfx@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ?isfx@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ?isfx@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ?iword@ios_base@std@@QAEAAJH@Z
+ ?length@?$codecvt@DDH@std@@QBEHABHPBD1I@Z
+ ?length@?$codecvt@GDH@std@@QBEHABHPBD1I@Z
+ ?length@?$codecvt@_WDH@std@@QBEHABHPBD1I@Z
+ ?lowest@?$numeric_limits@C@std@@SACXZ
+ ?lowest@?$numeric_limits@D@std@@SADXZ
+ ?lowest@?$numeric_limits@E@std@@SAEXZ
+ ?lowest@?$numeric_limits@F@std@@SAFXZ
+ ?lowest@?$numeric_limits@G@std@@SAGXZ
+ ?lowest@?$numeric_limits@H@std@@SAHXZ
+ ?lowest@?$numeric_limits@I@std@@SAIXZ
+ ?lowest@?$numeric_limits@J@std@@SAJXZ
+ ?lowest@?$numeric_limits@K@std@@SAKXZ
+ ?lowest@?$numeric_limits@M@std@@SAMXZ
+ ?lowest@?$numeric_limits@N@std@@SANXZ
+ ?lowest@?$numeric_limits@O@std@@SAOXZ
+ ?lowest@?$numeric_limits@_J@std@@SA_JXZ
+ ?lowest@?$numeric_limits@_K@std@@SA_KXZ
+ ?lowest@?$numeric_limits@_N@std@@SA_NXZ
+ ?lowest@?$numeric_limits@_W@std@@SA_WXZ
+ ?max@?$numeric_limits@C@std@@SACXZ
+ ?max@?$numeric_limits@D@std@@SADXZ
+ ?max@?$numeric_limits@E@std@@SAEXZ
+ ?max@?$numeric_limits@F@std@@SAFXZ
+ ?max@?$numeric_limits@G@std@@SAGXZ
+ ?max@?$numeric_limits@H@std@@SAHXZ
+ ?max@?$numeric_limits@I@std@@SAIXZ
+ ?max@?$numeric_limits@J@std@@SAJXZ
+ ?max@?$numeric_limits@K@std@@SAKXZ
+ ?max@?$numeric_limits@M@std@@SAMXZ
+ ?max@?$numeric_limits@N@std@@SANXZ
+ ?max@?$numeric_limits@O@std@@SAOXZ
+ ?max@?$numeric_limits@_J@std@@SA_JXZ
+ ?max@?$numeric_limits@_K@std@@SA_KXZ
+ ?max@?$numeric_limits@_N@std@@SA_NXZ
+ ?max@?$numeric_limits@_W@std@@SA_WXZ
+ ?max_digits10@?$numeric_limits@C@std@@2HB
+ ?max_digits10@?$numeric_limits@D@std@@2HB
+ ?max_digits10@?$numeric_limits@E@std@@2HB
+ ?max_digits10@?$numeric_limits@F@std@@2HB
+ ?max_digits10@?$numeric_limits@G@std@@2HB
+ ?max_digits10@?$numeric_limits@H@std@@2HB
+ ?max_digits10@?$numeric_limits@I@std@@2HB
+ ?max_digits10@?$numeric_limits@J@std@@2HB
+ ?max_digits10@?$numeric_limits@K@std@@2HB
+ ?max_digits10@?$numeric_limits@M@std@@2HB
+ ?max_digits10@?$numeric_limits@N@std@@2HB
+ ?max_digits10@?$numeric_limits@O@std@@2HB
+ ?max_digits10@?$numeric_limits@_J@std@@2HB
+ ?max_digits10@?$numeric_limits@_K@std@@2HB
+ ?max_digits10@?$numeric_limits@_N@std@@2HB
+ ?max_digits10@?$numeric_limits@_W@std@@2HB
+ ?max_digits10@_Num_base@std@@2HB
+ ?max_exponent10@?$numeric_limits@M@std@@2HB
+ ?max_exponent10@?$numeric_limits@N@std@@2HB
+ ?max_exponent10@?$numeric_limits@O@std@@2HB
+ ?max_exponent10@_Num_base@std@@2HB
+ ?max_exponent@?$numeric_limits@M@std@@2HB
+ ?max_exponent@?$numeric_limits@N@std@@2HB
+ ?max_exponent@?$numeric_limits@O@std@@2HB
+ ?max_exponent@_Num_base@std@@2HB
+ ?max_length@codecvt_base@std@@QBEHXZ
+ ?min@?$numeric_limits@C@std@@SACXZ
+ ?min@?$numeric_limits@D@std@@SADXZ
+ ?min@?$numeric_limits@E@std@@SAEXZ
+ ?min@?$numeric_limits@F@std@@SAFXZ
+ ?min@?$numeric_limits@G@std@@SAGXZ
+ ?min@?$numeric_limits@H@std@@SAHXZ
+ ?min@?$numeric_limits@I@std@@SAIXZ
+ ?min@?$numeric_limits@J@std@@SAJXZ
+ ?min@?$numeric_limits@K@std@@SAKXZ
+ ?min@?$numeric_limits@M@std@@SAMXZ
+ ?min@?$numeric_limits@N@std@@SANXZ
+ ?min@?$numeric_limits@O@std@@SAOXZ
+ ?min@?$numeric_limits@_J@std@@SA_JXZ
+ ?min@?$numeric_limits@_K@std@@SA_KXZ
+ ?min@?$numeric_limits@_N@std@@SA_NXZ
+ ?min@?$numeric_limits@_W@std@@SA_WXZ
+ ?min_exponent10@?$numeric_limits@M@std@@2HB
+ ?min_exponent10@?$numeric_limits@N@std@@2HB
+ ?min_exponent10@?$numeric_limits@O@std@@2HB
+ ?min_exponent10@_Num_base@std@@2HB
+ ?min_exponent@?$numeric_limits@M@std@@2HB
+ ?min_exponent@?$numeric_limits@N@std@@2HB
+ ?min_exponent@?$numeric_limits@O@std@@2HB
+ ?min_exponent@_Num_base@std@@2HB
+ ?move@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEX$$QAV12@@Z
+ ?move@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEX$$QAV12@@Z
+ ?move@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEX$$QAV12@@Z
+ ?narrow@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDDD@Z
+ ?narrow@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEDGD@Z
+ ?narrow@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBED_WD@Z
+ ?narrow@?$ctype@D@std@@QBEDDD@Z
+ ?narrow@?$ctype@D@std@@QBEPBDPBD0DPAD@Z
+ ?narrow@?$ctype@G@std@@QBEDGD@Z
+ ?narrow@?$ctype@G@std@@QBEPBGPBG0DPAD@Z
+ ?narrow@?$ctype@_W@std@@QBED_WD@Z
+ ?narrow@?$ctype@_W@std@@QBEPB_WPB_W0DPAD@Z
+ ?opfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE_NXZ
+ ?opfx@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE_NXZ
+ ?opfx@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE_NXZ
+ ?osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ?osfx@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ?osfx@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ?out@?$codecvt@DDH@std@@QBEHAAHPBD1AAPBDPAD3AAPAD@Z
+ ?out@?$codecvt@GDH@std@@QBEHAAHPBG1AAPBGPAD3AAPAD@Z
+ ?out@?$codecvt@_WDH@std@@QBEHAAHPB_W1AAPB_WPAD3AAPAD@Z
+ ?overflow@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHH@Z
+ ?overflow@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGG@Z
+ ?overflow@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGG@Z
+ ?pbackfail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHH@Z
+ ?pbackfail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGG@Z
+ ?pbackfail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGG@Z
+ ?pbase@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?pbase@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?pbase@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?pbump@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXH@Z
+ ?pbump@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXH@Z
+ ?pbump@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXH@Z
+ ?peek@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?peek@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?peek@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?pptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?pptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?pptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?precision@ios_base@std@@QAE_J_J@Z
+ ?precision@ios_base@std@@QBE_JXZ
+ ?pubimbue@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?pubimbue@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?pubimbue@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?pubseekoff@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@_JHH@Z
+ ?pubseekoff@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@_JII@Z
+ ?pubseekoff@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@_JHH@Z
+ ?pubseekoff@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@_JII@Z
+ ?pubseekoff@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@_JHH@Z
+ ?pubseekoff@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@_JII@Z
+ ?pubseekpos@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@V32@H@Z
+ ?pubseekpos@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@V32@I@Z
+ ?pubseekpos@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@V32@H@Z
+ ?pubseekpos@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@V32@I@Z
+ ?pubseekpos@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@V32@H@Z
+ ?pubseekpos@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@V32@I@Z
+ ?pubsetbuf@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEPAV12@PAD_J@Z
+ ?pubsetbuf@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEPAV12@PAG_J@Z
+ ?pubsetbuf@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEPAV12@PA_W_J@Z
+ ?pubsync@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?pubsync@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEHXZ
+ ?pubsync@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEHXZ
+ ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@D@Z
+ ?put@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@G@Z
+ ?put@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_W@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DJ@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DK@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DN@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DO@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBX@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_J@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_K@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_N@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GJ@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GK@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GN@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GO@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBX@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_J@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_K@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_N@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WJ@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WK@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WN@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WO@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBX@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_J@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_K@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_N@Z
+ ?put@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBUtm@@DD@Z
+ ?put@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBUtm@@PBD3@Z
+ ?put@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBUtm@@DD@Z
+ ?put@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBUtm@@PBG3@Z
+ ?put@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBUtm@@DD@Z
+ ?put@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBUtm@@PB_W4@Z
+ ?putback@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@D@Z
+ ?putback@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@G@Z
+ ?putback@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_W@Z
+ ?pword@ios_base@std@@QAEAAPAXH@Z
+ ?quiet_NaN@?$numeric_limits@C@std@@SACXZ
+ ?quiet_NaN@?$numeric_limits@D@std@@SADXZ
+ ?quiet_NaN@?$numeric_limits@E@std@@SAEXZ
+ ?quiet_NaN@?$numeric_limits@F@std@@SAFXZ
+ ?quiet_NaN@?$numeric_limits@G@std@@SAGXZ
+ ?quiet_NaN@?$numeric_limits@H@std@@SAHXZ
+ ?quiet_NaN@?$numeric_limits@I@std@@SAIXZ
+ ?quiet_NaN@?$numeric_limits@J@std@@SAJXZ
+ ?quiet_NaN@?$numeric_limits@K@std@@SAKXZ
+ ?quiet_NaN@?$numeric_limits@M@std@@SAMXZ
+ ?quiet_NaN@?$numeric_limits@N@std@@SANXZ
+ ?quiet_NaN@?$numeric_limits@O@std@@SAOXZ
+ ?quiet_NaN@?$numeric_limits@_J@std@@SA_JXZ
+ ?quiet_NaN@?$numeric_limits@_K@std@@SA_KXZ
+ ?quiet_NaN@?$numeric_limits@_N@std@@SA_NXZ
+ ?quiet_NaN@?$numeric_limits@_W@std@@SA_WXZ
+ ?radix@_Num_base@std@@2HB
+ ?radix@_Num_float_base@std@@2HB
+ ?radix@_Num_int_base@std@@2HB
+ ?rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@PAV32@@Z
+ ?rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@XZ
+ ?rdbuf@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@PAV32@@Z
+ ?rdbuf@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@XZ
+ ?rdbuf@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@PAV32@@Z
+ ?rdbuf@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBEPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@XZ
+ ?rdstate@ios_base@std@@QBEHXZ
+ ?read@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_J@Z
+ ?read@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_J@Z
+ ?read@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J@Z
+ ?readsome@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE_JPAD_J@Z
+ ?readsome@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE_JPAG_J@Z
+ ?readsome@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE_JPA_W_J@Z
+ ?register_callback@ios_base@std@@QAEXP6AXW4event@12@AAV12@H@ZH@Z
+ ?resetiosflags@std@@YA?AU?$_Smanip@H@1@H@Z
+ ?round_error@?$numeric_limits@C@std@@SACXZ
+ ?round_error@?$numeric_limits@D@std@@SADXZ
+ ?round_error@?$numeric_limits@E@std@@SAEXZ
+ ?round_error@?$numeric_limits@F@std@@SAFXZ
+ ?round_error@?$numeric_limits@G@std@@SAGXZ
+ ?round_error@?$numeric_limits@H@std@@SAHXZ
+ ?round_error@?$numeric_limits@I@std@@SAIXZ
+ ?round_error@?$numeric_limits@J@std@@SAJXZ
+ ?round_error@?$numeric_limits@K@std@@SAKXZ
+ ?round_error@?$numeric_limits@M@std@@SAMXZ
+ ?round_error@?$numeric_limits@N@std@@SANXZ
+ ?round_error@?$numeric_limits@O@std@@SAOXZ
+ ?round_error@?$numeric_limits@_J@std@@SA_JXZ
+ ?round_error@?$numeric_limits@_K@std@@SA_KXZ
+ ?round_error@?$numeric_limits@_N@std@@SA_NXZ
+ ?round_error@?$numeric_limits@_W@std@@SA_WXZ
+ ?round_style@_Num_base@std@@2W4float_round_style@2@B
+ ?round_style@_Num_float_base@std@@2W4float_round_style@2@B
+ ?sbumpc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?sbumpc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?sbumpc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?scan_is@?$ctype@D@std@@QBEPBDFPBD0@Z
+ ?scan_is@?$ctype@G@std@@QBEPBGFPBG0@Z
+ ?scan_is@?$ctype@_W@std@@QBEPB_WFPB_W0@Z
+ ?scan_not@?$ctype@D@std@@QBEPBDFPBD0@Z
+ ?scan_not@?$ctype@G@std@@QBEPBGFPBG0@Z
+ ?scan_not@?$ctype@_W@std@@QBEPB_WFPB_W0@Z
+ ?seekg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@_JH@Z
+ ?seekg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@_JH@Z
+ ?seekg@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekg@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_JH@Z
+ ?seekoff@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE?AV?$fpos@H@2@_JHH@Z
+ ?seekoff@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE?AV?$fpos@H@2@_JHH@Z
+ ?seekoff@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE?AV?$fpos@H@2@_JHH@Z
+ ?seekp@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekp@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@_JH@Z
+ ?seekp@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekp@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@_JH@Z
+ ?seekp@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekp@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_JH@Z
+ ?seekpos@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE?AV?$fpos@H@2@V32@H@Z
+ ?seekpos@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE?AV?$fpos@H@2@V32@H@Z
+ ?seekpos@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE?AV?$fpos@H@2@V32@H@Z
+ ?set_new_handler@std@@YAP6AXXZH@Z
+ ?set_new_handler@std@@YAP6AXXZP6AXXZ@Z
+ ?set_rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@@Z
+ ?set_rdbuf@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@@Z
+ ?set_rdbuf@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@@Z
+ ?setbase@std@@YA?AU?$_Smanip@H@1@H@Z
+ ?setbuf@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEPAV12@PAD_J@Z
+ ?setbuf@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEPAV12@PAG_J@Z
+ ?setbuf@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEPAV12@PA_W_J@Z
+ ?setf@ios_base@std@@QAEHH@Z
+ ?setf@ios_base@std@@QAEHHH@Z
+ ?setg@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAD00@Z
+ ?setg@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAG00@Z
+ ?setg@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPA_W00@Z
+ ?setiosflags@std@@YA?AU?$_Smanip@H@1@H@Z
+ ?setp@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAD00@Z
+ ?setp@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAD0@Z
+ ?setp@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAG00@Z
+ ?setp@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAG0@Z
+ ?setp@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPA_W00@Z
+ ?setp@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPA_W0@Z
+ ?setprecision@std@@YA?AU?$_Smanip@_J@1@_J@Z
+ ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXH_N@Z
+ ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXI@Z
+ ?setstate@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXH_N@Z
+ ?setstate@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXI@Z
+ ?setstate@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXH_N@Z
+ ?setstate@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXI@Z
+ ?setstate@ios_base@std@@QAEXH@Z
+ ?setstate@ios_base@std@@QAEXH_N@Z
+ ?setstate@ios_base@std@@QAEXI@Z
+ ?setw@std@@YA?AU?$_Smanip@_J@1@_J@Z
+ ?sgetc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?sgetc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?sgetc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?sgetn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE_JPAD_J@Z
+ ?sgetn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE_JPAG_J@Z
+ ?sgetn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE_JPA_W_J@Z
+ ?showmanyc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE_JXZ
+ ?showmanyc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE_JXZ
+ ?showmanyc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE_JXZ
+ ?signaling_NaN@?$numeric_limits@C@std@@SACXZ
+ ?signaling_NaN@?$numeric_limits@D@std@@SADXZ
+ ?signaling_NaN@?$numeric_limits@E@std@@SAEXZ
+ ?signaling_NaN@?$numeric_limits@F@std@@SAFXZ
+ ?signaling_NaN@?$numeric_limits@G@std@@SAGXZ
+ ?signaling_NaN@?$numeric_limits@H@std@@SAHXZ
+ ?signaling_NaN@?$numeric_limits@I@std@@SAIXZ
+ ?signaling_NaN@?$numeric_limits@J@std@@SAJXZ
+ ?signaling_NaN@?$numeric_limits@K@std@@SAKXZ
+ ?signaling_NaN@?$numeric_limits@M@std@@SAMXZ
+ ?signaling_NaN@?$numeric_limits@N@std@@SANXZ
+ ?signaling_NaN@?$numeric_limits@O@std@@SAOXZ
+ ?signaling_NaN@?$numeric_limits@_J@std@@SA_JXZ
+ ?signaling_NaN@?$numeric_limits@_K@std@@SA_KXZ
+ ?signaling_NaN@?$numeric_limits@_N@std@@SA_NXZ
+ ?signaling_NaN@?$numeric_limits@_W@std@@SA_WXZ
+ ?snextc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?snextc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?snextc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?sputbackc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHD@Z
+ ?sputbackc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGG@Z
+ ?sputbackc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEG_W@Z
+ ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHD@Z
+ ?sputc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGG@Z
+ ?sputc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEG_W@Z
+ ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE_JPBD_J@Z
+ ?sputn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE_JPBG_J@Z
+ ?sputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE_JPB_W_J@Z
+ ?start@agent@Concurrency@@QAE_NXZ
+ ?status@agent@Concurrency@@QAE?AW4agent_status@2@XZ
+ ?status_port@agent@Concurrency@@QAEPAV?$ISource@W4agent_status@Concurrency@@@2@XZ
+ ?stossc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ?stossc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ?stossc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ?sungetc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?sungetc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?sungetc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?swap@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_iostream@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_iostream@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXAAV12@@Z
+ ?swap@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXAAV12@@Z
+ ?swap@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXAAV12@@Z
+ ?swap@ios_base@std@@QAEXAAV12@@Z
+ ?sync@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?sync@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEHXZ
+ ?sync@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEHXZ
+ ?sync@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHXZ
+ ?sync@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEHXZ
+ ?sync@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEHXZ
+ ?sync_with_stdio@ios_base@std@@SA_N_N@Z
+ ?system_category@std@@YAABVerror_category@1@XZ
+ ?table@?$ctype@D@std@@QBEPBFXZ
+ ?table_size@?$ctype@D@std@@2IB
+ ?tellg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tellg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tellg@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tellp@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tellp@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tellp@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tie@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEPAV?$basic_ostream@DU?$char_traits@D@std@@@2@PAV32@@Z
+ ?tie@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_ostream@DU?$char_traits@D@std@@@2@XZ
+ ?tie@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEPAV?$basic_ostream@GU?$char_traits@G@std@@@2@PAV32@@Z
+ ?tie@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEPAV?$basic_ostream@GU?$char_traits@G@std@@@2@XZ
+ ?tie@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEPAV?$basic_ostream@_WU?$char_traits@_W@std@@@2@PAV32@@Z
+ ?tie@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBEPAV?$basic_ostream@_WU?$char_traits@_W@std@@@2@XZ
+ ?tinyness_before@_Num_base@std@@2_NB
+ ?tinyness_before@_Num_float_base@std@@2_NB
+ ?tolower@?$ctype@D@std@@QBEDD@Z
+ ?tolower@?$ctype@D@std@@QBEPBDPADPBD@Z
+ ?tolower@?$ctype@G@std@@QBEGG@Z
+ ?tolower@?$ctype@G@std@@QBEPBGPAGPBG@Z
+ ?tolower@?$ctype@_W@std@@QBEPB_WPA_WPB_W@Z
+ ?tolower@?$ctype@_W@std@@QBE_W_W@Z
+ ?toupper@?$ctype@D@std@@QBEDD@Z
+ ?toupper@?$ctype@D@std@@QBEPBDPADPBD@Z
+ ?toupper@?$ctype@G@std@@QBEGG@Z
+ ?toupper@?$ctype@G@std@@QBEPBGPAGPBG@Z
+ ?toupper@?$ctype@_W@std@@QBEPB_WPA_WPB_W@Z
+ ?toupper@?$ctype@_W@std@@QBE_W_W@Z
+ ?traps@_Num_base@std@@2_NB
+ ?traps@_Num_float_base@std@@2_NB
+ ?uflow@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHXZ
+ ?uflow@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGXZ
+ ?uflow@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGXZ
+ ?uncaught_exception@std@@YA_NXZ
+ ?underflow@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHXZ
+ ?underflow@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGXZ
+ ?underflow@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGXZ
+ ?unget@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ
+ ?unget@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@XZ
+ ?unget@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@XZ
+ ?unsetf@ios_base@std@@QAEXH@Z
+ ?unshift@?$codecvt@DDH@std@@QBEHAAHPAD1AAPAD@Z
+ ?unshift@?$codecvt@GDH@std@@QBEHAAHPAD1AAPAD@Z
+ ?unshift@?$codecvt@_WDH@std@@QBEHAAHPAD1AAPAD@Z
+ ?wait@agent@Concurrency@@SA?AW4agent_status@2@PAV12@I@Z
+ ?wait_for_all@agent@Concurrency@@SAXIPAPAV12@PAW4agent_status@2@I@Z
+ ?wait_for_one@agent@Concurrency@@SAXIPAPAV12@AAW4agent_status@2@AAII@Z
+ ?wcerr@std@@3V?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?wcerr@std@@3V?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?wcin@std@@3V?$basic_istream@GU?$char_traits@G@std@@@1@A
+ ?wcin@std@@3V?$basic_istream@_WU?$char_traits@_W@std@@@1@A
+ ?wclog@std@@3V?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?wclog@std@@3V?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?wcout@std@@3V?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?wcout@std@@3V?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?widen@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDD@Z
+ ?widen@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEGD@Z
+ ?widen@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBE_WD@Z
+ ?widen@?$ctype@D@std@@QBEDD@Z
+ ?widen@?$ctype@D@std@@QBEPBDPBD0PAD@Z
+ ?widen@?$ctype@G@std@@QBEGD@Z
+ ?widen@?$ctype@G@std@@QBEPBDPBD0PAG@Z
+ ?widen@?$ctype@_W@std@@QBEPBDPBD0PA_W@Z
+ ?widen@?$ctype@_W@std@@QBE_WD@Z
+ ?width@ios_base@std@@QAE_J_J@Z
+ ?width@ios_base@std@@QBE_JXZ
+ ?write@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@PBD_J@Z
+ ?write@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@PBG_J@Z
+ ?write@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PB_W_J@Z
+ ?ws@std@@YAAAV?$basic_istream@DU?$char_traits@D@std@@@1@AAV21@@Z
+ ?ws@std@@YAAAV?$basic_istream@GU?$char_traits@G@std@@@1@AAV21@@Z
+ ?ws@std@@YAAAV?$basic_istream@_WU?$char_traits@_W@std@@@1@AAV21@@Z
+ ?xalloc@ios_base@std@@SAHXZ
+ ?xsgetn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE_JPAD_J@Z
+ ?xsgetn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE_JPAG_J@Z
+ ?xsgetn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE_JPA_W_J@Z
+ ?xsputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE_JPBD_J@Z
+ ?xsputn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE_JPBG_J@Z
+ ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE_JPB_W_J@Z
+ _Cosh
+ _Denorm
+ _Dnorm
+ _Dscale
+ _Dtest
+ _Eps
+ _Exp
+ _FCosh
+ _FDenorm
+ _FDnorm
+ _FDscale
+ _FDtest
+ _FEps
+ _FExp
+ _FInf
+ _FNan
+ _FRteps
+ _FSinh
+ _FSnan
+ _FXbig
+ _GetLocaleForCP
+ _Getcoll
+ _Getctype
+ _Getcvt
+ _Getdateorder
+ _Getwctype
+ _Getwctypes
+ _Hugeval
+ _Inf
+ _LCosh
+ _LDenorm
+ _LDscale
+ _LDtest
+ _LEps
+ _LExp
+ _LInf
+ _LNan
+ _LPoly
+ _LRteps
+ _LSinh
+ _LSnan
+ _LXbig
+ _LZero
+ _Mbrtowc
+ _Mtxdst
+ _Mtxinit
+ _Mtxlock
+ _Mtxunlock
+ _Nan
+ _Once
+ _Poly
+ _Rteps
+ _Sinh
+ _Snan
+ _Stod
+ _Stodx
+ _Stof
+ _Stofx
+ _Stold
+ _Stoldx
+ _Stoll
+ _Stollx
+ _Stolx
+ _Stoul
+ _Stoull
+ _Stoullx
+ _Stoulx
+ _Strcoll
+ _Strxfrm
+ _Tolower
+ _Toupper
+ _Towlower
+ _Towupper
+ _Wcrtomb
+ _Wcscoll
+ _Wcsxfrm
+ _Xbig
+ __Wcrtomb_lk
+ towctrans
+ wctrans
+ wctype
+
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def b/src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def
new file mode 100644
index 00000000..6317e658
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def
@@ -0,0 +1,1640 @@
+
+ ;
+ _except_handler4
+ ;_heap_init
+ ;__crtExitProcess
+ ;_NMSG_WRITE
+ ;_FF_MSGBANNER
+ ;_cinit
+ ;_setenvp
+ ;__crtGetEnvironmentStringsA
+ ;_ioinit
+ ;_mtinit
+
+ _initterm=my_initterm
+ __dllonexit=my_dllonexit
+ __getmainargs=my_getmainargs
+ __setusermatherr=my_setusermatherr
+
+ _onexit DATA ; the one is crazy. atonexit.obj in msvcrt.lib which calls the __imp___onexit directly.
+
+ ; MSVCR100.DLL exports
+ ??0?$_SpinWait@$0A@@details@Concurrency@@QAE@P6AXXZ@Z
+ ??0SchedulerPolicy@Concurrency@@QAA@IZZ
+ ??0SchedulerPolicy@Concurrency@@QAE@ABV01@@Z
+ ??0SchedulerPolicy@Concurrency@@QAE@XZ
+ ??0_NonReentrantBlockingLock@details@Concurrency@@QAE@XZ
+ ??0_NonReentrantPPLLock@details@Concurrency@@QAE@XZ
+ ??0_ReaderWriterLock@details@Concurrency@@QAE@XZ
+ ??0_ReentrantBlockingLock@details@Concurrency@@QAE@XZ
+ ??0_ReentrantLock@details@Concurrency@@QAE@XZ
+ ??0_ReentrantPPLLock@details@Concurrency@@QAE@XZ
+ ??0_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z
+ ??0_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z
+ ??0_SpinLock@details@Concurrency@@QAE@ACJ@Z
+ ??0_TaskCollection@details@Concurrency@@QAE@XZ
+ ??0_Timer@details@Concurrency@@IAE@I_N@Z
+ ??0__non_rtti_object@std@@QAE@ABV01@@Z
+ ??0__non_rtti_object@std@@QAE@PBD@Z
+ ;fixme-not-in-libcmt.lib; ??0bad_cast@std@@AAE@PBQBD@Z
+ ??0bad_cast@std@@QAE@ABV01@@Z
+ ??0bad_cast@std@@QAE@PBD@Z
+ ??0bad_target@Concurrency@@QAE@PBD@Z
+ ??0bad_target@Concurrency@@QAE@XZ
+ ??0bad_typeid@std@@QAE@ABV01@@Z
+ ??0bad_typeid@std@@QAE@PBD@Z
+ ??0context_self_unblock@Concurrency@@QAE@PBD@Z
+ ??0context_self_unblock@Concurrency@@QAE@XZ
+ ??0context_unblock_unbalanced@Concurrency@@QAE@PBD@Z
+ ??0context_unblock_unbalanced@Concurrency@@QAE@XZ
+ ??0critical_section@Concurrency@@QAE@XZ
+ ??0default_scheduler_exists@Concurrency@@QAE@PBD@Z
+ ??0default_scheduler_exists@Concurrency@@QAE@XZ
+ ??0event@Concurrency@@QAE@XZ
+ ??0exception@std@@QAE@ABQBD@Z
+ ??0exception@std@@QAE@ABQBDH@Z
+ ??0exception@std@@QAE@ABV01@@Z
+ ??0exception@std@@QAE@XZ
+ ??0improper_lock@Concurrency@@QAE@PBD@Z
+ ??0improper_lock@Concurrency@@QAE@XZ
+ ??0improper_scheduler_attach@Concurrency@@QAE@PBD@Z
+ ??0improper_scheduler_attach@Concurrency@@QAE@XZ
+ ??0improper_scheduler_detach@Concurrency@@QAE@PBD@Z
+ ??0improper_scheduler_detach@Concurrency@@QAE@XZ
+ ??0improper_scheduler_reference@Concurrency@@QAE@PBD@Z
+ ??0improper_scheduler_reference@Concurrency@@QAE@XZ
+ ??0invalid_link_target@Concurrency@@QAE@PBD@Z
+ ??0invalid_link_target@Concurrency@@QAE@XZ
+ ??0invalid_multiple_scheduling@Concurrency@@QAE@PBD@Z
+ ??0invalid_multiple_scheduling@Concurrency@@QAE@XZ
+ ??0invalid_operation@Concurrency@@QAE@PBD@Z
+ ??0invalid_operation@Concurrency@@QAE@XZ
+ ??0invalid_oversubscribe_operation@Concurrency@@QAE@PBD@Z
+ ??0invalid_oversubscribe_operation@Concurrency@@QAE@XZ
+ ??0invalid_scheduler_policy_key@Concurrency@@QAE@PBD@Z
+ ??0invalid_scheduler_policy_key@Concurrency@@QAE@XZ
+ ??0invalid_scheduler_policy_thread_specification@Concurrency@@QAE@PBD@Z
+ ??0invalid_scheduler_policy_thread_specification@Concurrency@@QAE@XZ
+ ??0invalid_scheduler_policy_value@Concurrency@@QAE@PBD@Z
+ ??0invalid_scheduler_policy_value@Concurrency@@QAE@XZ
+ ??0message_not_found@Concurrency@@QAE@PBD@Z
+ ??0message_not_found@Concurrency@@QAE@XZ
+ ??0missing_wait@Concurrency@@QAE@PBD@Z
+ ??0missing_wait@Concurrency@@QAE@XZ
+ ??0nested_scheduler_missing_detach@Concurrency@@QAE@PBD@Z
+ ??0nested_scheduler_missing_detach@Concurrency@@QAE@XZ
+ ??0operation_timed_out@Concurrency@@QAE@PBD@Z
+ ??0operation_timed_out@Concurrency@@QAE@XZ
+ ??0reader_writer_lock@Concurrency@@QAE@XZ
+ ??0scheduler_not_attached@Concurrency@@QAE@PBD@Z
+ ??0scheduler_not_attached@Concurrency@@QAE@XZ
+ ??0scheduler_resource_allocation_error@Concurrency@@QAE@J@Z
+ ??0scheduler_resource_allocation_error@Concurrency@@QAE@PBDJ@Z
+ ??0scoped_lock@critical_section@Concurrency@@QAE@AAV12@@Z
+ ??0scoped_lock@reader_writer_lock@Concurrency@@QAE@AAV12@@Z
+ ??0scoped_lock_read@reader_writer_lock@Concurrency@@QAE@AAV12@@Z
+ ??0task_canceled@details@Concurrency@@QAE@PBD@Z
+ ??0task_canceled@details@Concurrency@@QAE@XZ
+ ??0unsupported_os@Concurrency@@QAE@PBD@Z
+ ??0unsupported_os@Concurrency@@QAE@XZ
+ ??1SchedulerPolicy@Concurrency@@QAE@XZ
+ ??1_NonReentrantBlockingLock@details@Concurrency@@QAE@XZ
+ ??1_ReentrantBlockingLock@details@Concurrency@@QAE@XZ
+ ??1_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@XZ
+ ??1_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@XZ
+ ??1_SpinLock@details@Concurrency@@QAE@XZ
+ ??1_TaskCollection@details@Concurrency@@QAE@XZ
+ ??1_Timer@details@Concurrency@@IAE@XZ
+ ??1__non_rtti_object@std@@UAE@XZ
+ ??1bad_cast@std@@UAE@XZ
+ ??1bad_typeid@std@@UAE@XZ
+ ??1critical_section@Concurrency@@QAE@XZ
+ ??1event@Concurrency@@QAE@XZ
+ ??1exception@std@@UAE@XZ
+ ??1reader_writer_lock@Concurrency@@QAE@XZ
+ ??1scoped_lock@critical_section@Concurrency@@QAE@XZ
+ ??1scoped_lock@reader_writer_lock@Concurrency@@QAE@XZ
+ ??1scoped_lock_read@reader_writer_lock@Concurrency@@QAE@XZ
+ ??1type_info@@UAE@XZ
+ ??2@YAPAXI@Z
+ ??2@YAPAXIHPBDH@Z
+ ??3@YAXPAX@Z
+ ;fixme-not-in-libcmt.lib; ??4?$_SpinWait@$00@details@Concurrency@@QAEAAV012@ABV012@@Z
+ ;fixme-not-in-libcmt.lib; ??4?$_SpinWait@$0A@@details@Concurrency@@QAEAAV012@ABV012@@Z
+ ??4SchedulerPolicy@Concurrency@@QAEAAV01@ABV01@@Z
+ ??4__non_rtti_object@std@@QAEAAV01@ABV01@@Z
+ ??4bad_cast@std@@QAEAAV01@ABV01@@Z
+ ??4bad_typeid@std@@QAEAAV01@ABV01@@Z
+ ??4exception@std@@QAEAAV01@ABV01@@Z
+ ??8type_info@@QBE_NABV0@@Z
+ ??9type_info@@QBE_NABV0@@Z
+ ??_7__non_rtti_object@std@@6B@
+ ??_7bad_cast@std@@6B@
+ ??_7bad_typeid@std@@6B@
+ ;fixme-not-in-libcmt.lib; ??_7exception@@6B@
+ ??_7exception@std@@6B@
+ ;fixme-not-in-libcmt.lib; ??_F?$_SpinWait@$00@details@Concurrency@@QAEXXZ
+ ;fixme-not-in-libcmt.lib; ??_F?$_SpinWait@$0A@@details@Concurrency@@QAEXXZ
+ ??_Fbad_cast@std@@QAEXXZ
+ ??_Fbad_typeid@std@@QAEXXZ
+ ??_U@YAPAXI@Z
+ ??_U@YAPAXIHPBDH@Z
+ ??_V@YAXPAX@Z
+ ?Alloc@Concurrency@@YAPAXI@Z
+ ?Block@Context@Concurrency@@SAXXZ
+ ?Create@CurrentScheduler@Concurrency@@SAXABVSchedulerPolicy@2@@Z
+ ?Create@Scheduler@Concurrency@@SAPAV12@ABVSchedulerPolicy@2@@Z
+ ?CreateResourceManager@Concurrency@@YAPAUIResourceManager@1@XZ
+ ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPAVScheduleGroup@2@XZ
+ ?CurrentContext@Context@Concurrency@@SAPAV12@XZ
+ ?Detach@CurrentScheduler@Concurrency@@SAXXZ
+ ?DisableTracing@Concurrency@@YAJXZ
+ ?EnableTracing@Concurrency@@YAJXZ
+ ?Free@Concurrency@@YAXPAX@Z
+ ?Get@CurrentScheduler@Concurrency@@SAPAVScheduler@2@XZ
+ ?GetExecutionContextId@Concurrency@@YAIXZ
+ ?GetNumberOfVirtualProcessors@CurrentScheduler@Concurrency@@SAIXZ
+ ?GetOSVersion@Concurrency@@YA?AW4OSVersion@IResourceManager@1@XZ
+ ?GetPolicy@CurrentScheduler@Concurrency@@SA?AVSchedulerPolicy@2@XZ
+ ?GetPolicyValue@SchedulerPolicy@Concurrency@@QBEIW4PolicyElementKey@2@@Z
+ ?GetProcessorCount@Concurrency@@YAIXZ
+ ?GetProcessorNodeCount@Concurrency@@YAIXZ
+ ?GetSchedulerId@Concurrency@@YAIXZ
+ ?GetSharedTimerQueue@details@Concurrency@@YAPAXXZ
+ ?Id@Context@Concurrency@@SAIXZ
+ ?Id@CurrentScheduler@Concurrency@@SAIXZ
+ ?IsCurrentTaskCollectionCanceling@Context@Concurrency@@SA_NXZ
+ ?Log2@details@Concurrency@@YAKI@Z
+ ?Oversubscribe@Context@Concurrency@@SAX_N@Z
+ ?RegisterShutdownEvent@CurrentScheduler@Concurrency@@SAXPAX@Z
+ ?ResetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXXZ
+ ?ScheduleGroupId@Context@Concurrency@@SAIXZ
+ ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPAX@Z0@Z
+ ?SetConcurrencyLimits@SchedulerPolicy@Concurrency@@QAEXII@Z
+ ?SetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXABVSchedulerPolicy@2@@Z
+ ?SetPolicyValue@SchedulerPolicy@Concurrency@@QAEIW4PolicyElementKey@2@I@Z
+ ?VirtualProcessorId@Context@Concurrency@@SAIXZ
+ ?Yield@Context@Concurrency@@SAXXZ
+ ?_Abort@_StructuredTaskCollection@details@Concurrency@@AAEXXZ
+ ?_Acquire@_NonReentrantBlockingLock@details@Concurrency@@QAEXXZ
+ ?_Acquire@_NonReentrantPPLLock@details@Concurrency@@QAEXPAX@Z
+ ?_Acquire@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ
+ ?_Acquire@_ReentrantLock@details@Concurrency@@QAEXXZ
+ ?_Acquire@_ReentrantPPLLock@details@Concurrency@@QAEXPAX@Z
+ ?_AcquireRead@_ReaderWriterLock@details@Concurrency@@QAEXXZ
+ ?_AcquireWrite@_ReaderWriterLock@details@Concurrency@@QAEXXZ
+ ?_Cancel@_StructuredTaskCollection@details@Concurrency@@QAEXXZ
+ ?_Cancel@_TaskCollection@details@Concurrency@@QAEXXZ
+ ?_CheckTaskCollection@_UnrealizedChore@details@Concurrency@@IAEXXZ
+ ?_ConcRT_Assert@details@Concurrency@@YAXPBD0H@Z
+ ?_ConcRT_CoreAssert@details@Concurrency@@YAXPBD0H@Z
+ ?_ConcRT_DumpMessage@details@Concurrency@@YAXPB_WZZ
+ ?_ConcRT_Trace@details@Concurrency@@YAXHPB_WZZ
+ ?_Copy_str@exception@std@@AAEXPBD@Z
+ ?_DoYield@?$_SpinWait@$00@details@Concurrency@@IAEXXZ
+ ?_DoYield@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ
+ ?_IsCanceling@_StructuredTaskCollection@details@Concurrency@@QAE_NXZ
+ ?_IsCanceling@_TaskCollection@details@Concurrency@@QAE_NXZ
+ ?_Name_base@type_info@@CAPBDPBV1@PAU__type_info_node@@@Z
+ ?_Name_base_internal@type_info@@CAPBDPBV1@PAU__type_info_node@@@Z
+ ?_NumberOfSpins@?$_SpinWait@$00@details@Concurrency@@IAEKXZ
+ ?_NumberOfSpins@?$_SpinWait@$0A@@details@Concurrency@@IAEKXZ
+ ?_Release@_NonReentrantBlockingLock@details@Concurrency@@QAEXXZ
+ ?_Release@_NonReentrantPPLLock@details@Concurrency@@QAEXXZ
+ ?_Release@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ
+ ?_Release@_ReentrantLock@details@Concurrency@@QAEXXZ
+ ?_Release@_ReentrantPPLLock@details@Concurrency@@QAEXXZ
+ ?_ReleaseRead@_ReaderWriterLock@details@Concurrency@@QAEXXZ
+ ?_ReleaseWrite@_ReaderWriterLock@details@Concurrency@@QAEXXZ
+ ?_Reset@?$_SpinWait@$00@details@Concurrency@@IAEXXZ
+ ?_Reset@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ
+ ?_RunAndWait@_StructuredTaskCollection@details@Concurrency@@QAG?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z
+ ?_RunAndWait@_TaskCollection@details@Concurrency@@QAG?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z
+ ?_Schedule@_StructuredTaskCollection@details@Concurrency@@QAEXPAV_UnrealizedChore@23@@Z
+ ?_Schedule@_TaskCollection@details@Concurrency@@QAEXPAV_UnrealizedChore@23@@Z
+ ?_SetSpinCount@?$_SpinWait@$00@details@Concurrency@@QAEXI@Z
+ ?_SetSpinCount@?$_SpinWait@$0A@@details@Concurrency@@QAEXI@Z
+ ?_ShouldSpinAgain@?$_SpinWait@$00@details@Concurrency@@IAE_NXZ
+ ?_ShouldSpinAgain@?$_SpinWait@$0A@@details@Concurrency@@IAE_NXZ
+ ?_SpinOnce@?$_SpinWait@$00@details@Concurrency@@QAE_NXZ
+ ?_SpinOnce@?$_SpinWait@$0A@@details@Concurrency@@QAE_NXZ
+ ?_SpinYield@Context@Concurrency@@SAXXZ
+ ?_Start@_Timer@details@Concurrency@@IAEXXZ
+ ?_Stop@_Timer@details@Concurrency@@IAEXXZ
+ ?_Tidy@exception@std@@AAEXXZ
+ ?_Trace_ppl_function@Concurrency@@YAXABU_GUID@@EW4ConcRT_EventType@1@@Z
+ ?_TryAcquire@_NonReentrantBlockingLock@details@Concurrency@@QAE_NXZ
+ ?_TryAcquire@_ReentrantBlockingLock@details@Concurrency@@QAE_NXZ
+ ?_TryAcquire@_ReentrantLock@details@Concurrency@@QAE_NXZ
+ ?_TryAcquireWrite@_ReaderWriterLock@details@Concurrency@@QAE_NXZ
+ ?_Type_info_dtor@type_info@@CAXPAV1@@Z
+ ?_Type_info_dtor_internal@type_info@@CAXPAV1@@Z
+ ?_UnderlyingYield@details@Concurrency@@YAXXZ
+ ?_ValidateExecute@@YAHP6GHXZ@Z
+ ?_ValidateRead@@YAHPBXI@Z
+ ?_ValidateWrite@@YAHPAXI@Z
+ ?_Value@_SpinCount@details@Concurrency@@SAIXZ
+ ?__ExceptionPtrAssign@@YAXPAXPBX@Z
+ ?__ExceptionPtrCompare@@YA_NPBX0@Z
+ ?__ExceptionPtrCopy@@YAXPAXPBX@Z
+ ?__ExceptionPtrCopyException@@YAXPAXPBX1@Z
+ ?__ExceptionPtrCreate@@YAXPAX@Z
+ ?__ExceptionPtrCurrentException@@YAXPAX@Z
+ ?__ExceptionPtrDestroy@@YAXPAX@Z
+ ?__ExceptionPtrRethrow@@YAXPBX@Z
+ __uncaught_exception
+ ?_inconsistency@@YAXXZ
+ ?_invalid_parameter@@YAXPBG00II@Z
+ ?_is_exception_typeof@@YAHABVtype_info@@PAU_EXCEPTION_POINTERS@@@Z
+ ?_name_internal_method@type_info@@QBEPBDPAU__type_info_node@@@Z
+ ?_open@@YAHPBDHH@Z
+ ?_query_new_handler@@YAP6AHI@ZXZ
+ ?_query_new_mode@@YAHXZ
+ ?_set_new_handler@@YAP6AHI@ZH@Z
+ ?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z
+ ?_set_new_mode@@YAHH@Z
+ ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZH@Z
+ ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z
+ ?_sopen@@YAHPBDHHH@Z
+ ?_type_info_dtor_internal_method@type_info@@QAEXXZ
+ ?_wopen@@YAHPB_WHH@Z
+ ?_wsopen@@YAHPB_WHHH@Z
+ ?before@type_info@@QBEHABV1@@Z
+ ?get_error_code@scheduler_resource_allocation_error@Concurrency@@QBEJXZ
+ ?lock@critical_section@Concurrency@@QAEXXZ
+ ?lock@reader_writer_lock@Concurrency@@QAEXXZ
+ ?lock_read@reader_writer_lock@Concurrency@@QAEXXZ
+ ?name@type_info@@QBEPBDPAU__type_info_node@@@Z
+ ?native_handle@critical_section@Concurrency@@QAEAAV12@XZ
+ ?raw_name@type_info@@QBEPBDXZ
+ ?reset@event@Concurrency@@QAEXXZ
+ ?set@event@Concurrency@@QAEXXZ
+ ?set_new_handler@@YAP6AXXZP6AXXZ@Z
+ ?set_terminate@@YAP6AXXZH@Z
+ ?set_terminate@@YAP6AXXZP6AXXZ@Z
+ ?set_unexpected@@YAP6AXXZH@Z
+ ?set_unexpected@@YAP6AXXZP6AXXZ@Z
+ ?swprintf@@YAHPAGIPBGZZ
+ ?swprintf@@YAHPA_WIPB_WZZ
+ ?terminate@@YAXXZ ;fixme-causes-trouble-with-version-in-libcmt.lib;
+ ?try_lock@critical_section@Concurrency@@QAE_NXZ
+ ?try_lock@reader_writer_lock@Concurrency@@QAE_NXZ
+ ?try_lock_read@reader_writer_lock@Concurrency@@QAE_NXZ
+ ?unexpected@@YAXXZ
+ ?unlock@critical_section@Concurrency@@QAEXXZ
+ ?unlock@reader_writer_lock@Concurrency@@QAEXXZ
+ ?vswprintf@@YAHPA_WIPB_WPAD@Z
+ ?wait@Concurrency@@YAXI@Z
+ ?wait@event@Concurrency@@QAEII@Z
+ ?wait_for_multiple@event@Concurrency@@SAIPAPAV12@I_NI@Z
+ ?what@exception@std@@UBEPBDXZ
+ $I10_OUTPUT
+ _CIacos
+ _CIasin
+ _CIatan
+ _CIatan2
+ _CIcos
+ _CIcosh
+ _CIexp
+ _CIfmod
+ _CIlog
+ _CIlog10
+ _CIpow
+ _CIsin
+ _CIsinh
+ _CIsqrt
+ _CItan
+ _CItanh
+ _CRT_RTC_INIT
+ _CRT_RTC_INITW
+ _CreateFrameInfo
+ _CxxThrowException
+ _EH_prolog
+ _FindAndUnlinkFrame
+ _Getdays
+ _Getmonths
+ _Gettnames
+ _HUGE
+ _IsExceptionObjectToBeDestroyed
+ _NLG_Dispatch2
+ _NLG_Return
+ _NLG_Return2
+ _Strftime
+ _XcptFilter
+ __AdjustPointer
+ __BuildCatchObject
+ __BuildCatchObjectHelper
+ __CppXcptFilter
+ __CxxCallUnwindDelDtor
+ __CxxCallUnwindDtor
+ __CxxCallUnwindStdDelDtor
+ __CxxCallUnwindVecDtor
+ __CxxDetectRethrow
+ __CxxExceptionFilter
+ __CxxFrameHandler
+ __CxxFrameHandler2
+ __CxxFrameHandler3
+ __CxxLongjmpUnwind
+ __CxxQueryExceptionSize
+ __CxxRegisterExceptionObject
+ __CxxUnregisterExceptionObject
+ __DestructExceptionObject
+ __FrameUnwindFilter
+ __RTCastToVoid
+ __RTDynamicCast
+ __RTtypeid
+ __STRINGTOLD
+ __STRINGTOLD_L
+ __TypeMatch
+ ___lc_codepage_func
+ ___lc_collate_cp_func
+ ___lc_handle_func
+ ___mb_cur_max_func
+ ___mb_cur_max_l_func
+ ___setlc_active_func
+ ___unguarded_readlc_active_add_func
+ __argc
+ __argv
+ __badioinfo
+ __clean_type_info_names_internal
+ __control87_2
+ __create_locale
+ __crtCompareStringA
+ __crtCompareStringW
+ __crtLCMapStringA
+ __crtLCMapStringW
+ __daylight
+ __doserrno
+ __dstbias
+ ___fls_getvalue@4
+ ___fls_setvalue@8
+ __fpecode
+ __free_locale
+ __get_current_locale
+ __get_flsindex
+ ;fixme?; __get_tlsindex
+ __initenv
+ __iob_func
+ __isascii
+ __iscsym
+ __iscsymf
+ __iswcsym
+ __iswcsymf
+ __lconv
+ __lconv_init
+ __libm_sse2_acos
+ __libm_sse2_acosf
+ __libm_sse2_asin
+ __libm_sse2_asinf
+ __libm_sse2_atan
+ __libm_sse2_atan2
+ __libm_sse2_atanf
+ __libm_sse2_cos
+ __libm_sse2_cosf
+ __libm_sse2_exp
+ __libm_sse2_expf
+ __libm_sse2_log
+ __libm_sse2_log10
+ __libm_sse2_log10f
+ __libm_sse2_logf
+ __libm_sse2_pow
+ __libm_sse2_powf
+ __libm_sse2_sin
+ __libm_sse2_sinf
+ __libm_sse2_tan
+ __libm_sse2_tanf
+ __mb_cur_max
+ ;fixme?; __p___argc
+ ;fixme?; __p___argv
+ ;fixme?; __p___initenv
+ ;fixme?; __p___mb_cur_max
+ ;fixme?; __p___wargv
+ ;fixme?; __p___winitenv
+ ;fixme?; __p__acmdln
+ ;fixme?; __p__commode
+ ;fixme?; __p__daylight
+ ;fixme?; __p__dstbias
+ ;fixme?; __p__environ
+ ;fixme?; __p__fmode
+ ;fixme?; __p__iob
+ ;fixme?; __p__mbcasemap
+ ;fixme?; __p__mbctype
+ ;fixme?; __p__pctype
+ ;fixme?; __p__pgmptr
+ ;fixme?; __p__pwctype
+ ;fixme?; __p__timezone
+ ;fixme?; __p__tzname
+ ;fixme?; __p__wcmdln
+ ;fixme?; __p__wenviron
+ ;fixme?; __p__wpgmptr
+
+ __pctype_func
+ __pioinfo
+ __pwctype_func
+ __pxcptinfoptrs
+ __report_gsfailure
+ __set_app_type
+ __set_flsgetvalue
+ __setlc_active
+ ;fixme?; __setusermatherr
+ __strncnt
+ __swprintf_l
+ __sys_errlist
+ __sys_nerr
+ __threadhandle
+ __threadid
+ __timezone
+ __toascii
+ __tzname
+ __unDName
+ __unDNameEx
+ __unDNameHelper
+ __unguarded_readlc_active
+ __vswprintf_l
+ __wargv
+ __wcserror
+ __wcserror_s
+ __wcsncnt
+ ;fixme?; __wgetmainargs
+ __winitenv
+ _abnormal_termination
+ _abs64
+ _access
+ _access_s
+ _acmdln
+ _aligned_free
+ _aligned_malloc
+ _aligned_msize
+ _aligned_offset_malloc
+ _aligned_offset_realloc
+ _aligned_offset_recalloc
+ _aligned_realloc
+ _aligned_recalloc
+ _amsg_exit
+ _assert
+ _atodbl
+ _atodbl_l
+ _atof_l
+ _atoflt
+ _atoflt_l
+ _atoi64
+ _atoi64_l
+ _atoi_l
+ _atol_l
+ _atoldbl
+ _atoldbl_l
+ _beep
+ _beginthread
+ _beginthreadex
+ _byteswap_uint64
+ _byteswap_ulong
+ _byteswap_ushort
+ _c_exit
+ _cabs
+ _callnewh
+ _calloc_crt
+ _cexit
+ _cgets
+ _cgets_s
+ _cgetws
+ _cgetws_s
+ _chdir
+ _chdrive
+ _chgsign
+ _chkesp
+ _chmod
+ _chsize
+ _chsize_s
+ _clearfp
+ _close
+ _commit
+ ;fixme?; _commode
+ _configthreadlocale
+ _control87
+ _controlfp
+ _controlfp_s
+ _copysign
+ _cprintf
+ _cprintf_l
+ _cprintf_p
+ _cprintf_p_l
+ _cprintf_s
+ _cprintf_s_l
+ _cputs
+ _cputws
+ _creat
+ _create_locale
+ _crt_debugger_hook
+ _cscanf
+ _cscanf_l
+ _cscanf_s
+ _cscanf_s_l
+ _ctime32
+ _ctime32_s
+ _ctime64
+ _ctime64_s
+ _cwait
+ _cwprintf
+ _cwprintf_l
+ _cwprintf_p
+ _cwprintf_p_l
+ _cwprintf_s
+ _cwprintf_s_l
+ _cwscanf
+ _cwscanf_l
+ _cwscanf_s
+ _cwscanf_s_l
+ _daylight
+ _difftime32
+ _difftime64
+ _dosmaperr
+ _dstbias
+ _dup
+ _dup2
+ _dupenv_s
+ _ecvt
+ _ecvt_s
+ _encoded_null
+ _endthread
+ _endthreadex
+ _environ
+ _eof
+ _errno
+ _except_handler2
+ _except_handler3
+ ;fixme?; _except_handler4_common
+ _execl
+ _execle
+ _execlp
+ _execlpe
+ _execv
+ _execve
+ _execvp
+ _execvpe
+ _exit
+ _expand
+ _fclose_nolock
+ _fcloseall
+ _fcvt
+ _fcvt_s
+ _fdopen
+ _fflush_nolock
+ _fgetchar
+ _fgetwc_nolock
+ _fgetwchar
+ _filbuf
+ _filelength
+ _filelengthi64
+ _fileno
+ _findclose
+ _findfirst32
+ _findfirst32i64
+ _findfirst64
+ _findfirst64i32
+ _findnext32
+ _findnext32i64
+ _findnext64
+ _findnext64i32
+ _finite
+ _flsbuf
+ _flushall
+ ;fixme?; _fmode
+ _fpclass
+ _fpieee_flt
+ _fpreset
+ _fprintf_l
+ _fprintf_p
+ _fprintf_p_l
+ _fprintf_s_l
+ _fputchar
+ _fputwc_nolock
+ _fputwchar
+ _fread_nolock
+ _fread_nolock_s
+ _free_locale
+ _freea
+ _freea_s
+ _freefls
+ _fscanf_l
+ _fscanf_s_l
+ _fseek_nolock
+ _fseeki64
+ _fseeki64_nolock
+ _fsopen
+ _fstat32
+ _fstat32i64
+ _fstat64
+ _fstat64i32
+ _ftell_nolock
+ _ftelli64
+ _ftelli64_nolock
+ _ftime32
+ _ftime32_s
+ _ftime64
+ _ftime64_s
+ _ftol
+ _fullpath
+ _futime32
+ _futime64
+ _fwprintf_l
+ _fwprintf_p
+ _fwprintf_p_l
+ _fwprintf_s_l
+ _fwrite_nolock
+ _fwscanf_l
+ _fwscanf_s_l
+ _gcvt
+ _gcvt_s
+ _get_current_locale
+ _get_daylight
+ _get_doserrno
+ _get_dstbias
+ _get_errno
+ _get_fmode
+ _get_heap_handle
+ _get_invalid_parameter_handler
+ _get_osfhandle
+ _get_output_format
+ _get_pgmptr
+ _get_printf_count_output
+ _get_purecall_handler
+ _get_terminate
+ _get_timezone
+ _get_tzname
+ _get_unexpected
+ _get_wpgmptr
+ _getc_nolock
+ _getch
+ _getch_nolock
+ _getche
+ _getche_nolock
+ _getcwd
+ _getdcwd
+ _getdcwd_nolock
+ _getdiskfree
+ _getdllprocaddr
+ _getdrive
+ _getdrives
+ _getmaxstdio
+ _getmbcp
+ _getpid
+ _getptd
+ _getsystime
+ _getw
+ _getwch
+ _getwch_nolock
+ _getwche
+ _getwche_nolock
+ _getws
+ _getws_s
+ _global_unwind2
+ _gmtime32
+ _gmtime32_s
+ _gmtime64
+ _gmtime64_s
+ _heapadd
+ _heapchk
+ _heapmin
+ _heapset
+ _heapused
+ _heapwalk
+ _hypot
+ _hypotf
+ _i64toa
+ _i64toa_s
+ _i64tow
+ _i64tow_s
+ _initptd
+ ;fixme?; _initterm
+ _initterm_e
+ _inp
+ _inpd
+ _inpw
+ _invalid_parameter
+ _invalid_parameter_noinfo
+ _invalid_parameter_noinfo_noreturn
+ _invoke_watson
+ _iob
+ _isalnum_l
+ _isalpha_l
+ _isatty
+ _iscntrl_l
+ _isctype
+ _isctype_l
+ _isdigit_l
+ _isgraph_l
+ _isleadbyte_l
+ _islower_l
+ _ismbbalnum
+ _ismbbalnum_l
+ _ismbbalpha
+ _ismbbalpha_l
+ _ismbbgraph
+ _ismbbgraph_l
+ _ismbbkalnum
+ _ismbbkalnum_l
+ _ismbbkana
+ _ismbbkana_l
+ _ismbbkprint
+ _ismbbkprint_l
+ _ismbbkpunct
+ _ismbbkpunct_l
+ _ismbblead
+ _ismbblead_l
+ _ismbbprint
+ _ismbbprint_l
+ _ismbbpunct
+ _ismbbpunct_l
+ _ismbbtrail
+ _ismbbtrail_l
+ _ismbcalnum
+ _ismbcalnum_l
+ _ismbcalpha
+ _ismbcalpha_l
+ _ismbcdigit
+ _ismbcdigit_l
+ _ismbcgraph
+ _ismbcgraph_l
+ _ismbchira
+ _ismbchira_l
+ _ismbckata
+ _ismbckata_l
+ _ismbcl0
+ _ismbcl0_l
+ _ismbcl1
+ _ismbcl1_l
+ _ismbcl2
+ _ismbcl2_l
+ _ismbclegal
+ _ismbclegal_l
+ _ismbclower
+ _ismbclower_l
+ _ismbcprint
+ _ismbcprint_l
+ _ismbcpunct
+ _ismbcpunct_l
+ _ismbcspace
+ _ismbcspace_l
+ _ismbcsymbol
+ _ismbcsymbol_l
+ _ismbcupper
+ _ismbcupper_l
+ _ismbslead
+ _ismbslead_l
+ _ismbstrail
+ _ismbstrail_l
+ _isnan
+ _isprint_l
+ _ispunct_l
+ _isspace_l
+ _isupper_l
+ _iswalnum_l
+ _iswalpha_l
+ _iswcntrl_l
+ _iswcsym_l
+ _iswcsymf_l
+ _iswctype_l
+ _iswdigit_l
+ _iswgraph_l
+ _iswlower_l
+ _iswprint_l
+ _iswpunct_l
+ _iswspace_l
+ _iswupper_l
+ _iswxdigit_l
+ _isxdigit_l
+ _itoa
+ _itoa_s
+ _itow
+ _itow_s
+ _j0
+ _j1
+ _jn
+ _kbhit
+ _lfind
+ _lfind_s
+ _loaddll
+ _local_unwind2
+ _local_unwind4
+ _localtime32
+ _localtime32_s
+ _localtime64
+ _localtime64_s
+ _lock
+ _lock_file
+ _locking
+ _logb
+ _longjmpex
+ _lrotl
+ _lrotr
+ _lsearch
+ _lsearch_s
+ _lseek
+ _lseeki64
+ _ltoa
+ _ltoa_s
+ _ltow
+ _ltow_s
+ _makepath
+ _makepath_s
+ _malloc_crt
+ _mbbtombc
+ _mbbtombc_l
+ _mbbtype
+ _mbbtype_l
+ _mbcasemap
+ _mbccpy
+ _mbccpy_l
+ _mbccpy_s
+ _mbccpy_s_l
+ _mbcjistojms
+ _mbcjistojms_l
+ _mbcjmstojis
+ _mbcjmstojis_l
+ _mbclen
+ _mbclen_l
+ _mbctohira
+ _mbctohira_l
+ _mbctokata
+ _mbctokata_l
+ _mbctolower
+ _mbctolower_l
+ _mbctombb
+ _mbctombb_l
+ _mbctoupper
+ _mbctoupper_l
+ _mbctype
+ _mblen_l
+ _mbsbtype
+ _mbsbtype_l
+ _mbscat_s
+ _mbscat_s_l
+ _mbschr
+ _mbschr_l
+ _mbscmp
+ _mbscmp_l
+ _mbscoll
+ _mbscoll_l
+ _mbscpy_s
+ _mbscpy_s_l
+ _mbscspn
+ _mbscspn_l
+ _mbsdec
+ _mbsdec_l
+ _mbsicmp
+ _mbsicmp_l
+ _mbsicoll
+ _mbsicoll_l
+ _mbsinc
+ _mbsinc_l
+ _mbslen
+ _mbslen_l
+ _mbslwr
+ _mbslwr_l
+ _mbslwr_s
+ _mbslwr_s_l
+ _mbsnbcat
+ _mbsnbcat_l
+ _mbsnbcat_s
+ _mbsnbcat_s_l
+ _mbsnbcmp
+ _mbsnbcmp_l
+ _mbsnbcnt
+ _mbsnbcnt_l
+ _mbsnbcoll
+ _mbsnbcoll_l
+ _mbsnbcpy
+ _mbsnbcpy_l
+ _mbsnbcpy_s
+ _mbsnbcpy_s_l
+ _mbsnbicmp
+ _mbsnbicmp_l
+ _mbsnbicoll
+ _mbsnbicoll_l
+ _mbsnbset
+ _mbsnbset_l
+ _mbsnbset_s
+ _mbsnbset_s_l
+ _mbsncat
+ _mbsncat_l
+ _mbsncat_s
+ _mbsncat_s_l
+ _mbsnccnt
+ _mbsnccnt_l
+ _mbsncmp
+ _mbsncmp_l
+ _mbsncoll
+ _mbsncoll_l
+ _mbsncpy
+ _mbsncpy_l
+ _mbsncpy_s
+ _mbsncpy_s_l
+ _mbsnextc
+ _mbsnextc_l
+ _mbsnicmp
+ _mbsnicmp_l
+ _mbsnicoll
+ _mbsnicoll_l
+ _mbsninc
+ _mbsninc_l
+ _mbsnlen
+ _mbsnlen_l
+ _mbsnset
+ _mbsnset_l
+ _mbsnset_s
+ _mbsnset_s_l
+ _mbspbrk
+ _mbspbrk_l
+ _mbsrchr
+ _mbsrchr_l
+ _mbsrev
+ _mbsrev_l
+ _mbsset
+ _mbsset_l
+ _mbsset_s
+ _mbsset_s_l
+ _mbsspn
+ _mbsspn_l
+ _mbsspnp
+ _mbsspnp_l
+ _mbsstr
+ _mbsstr_l
+ _mbstok
+ _mbstok_l
+ _mbstok_s
+ _mbstok_s_l
+ _mbstowcs_l
+ _mbstowcs_s_l
+ _mbstrlen
+ _mbstrlen_l
+ _mbstrnlen
+ _mbstrnlen_l
+ _mbsupr
+ _mbsupr_l
+ _mbsupr_s
+ _mbsupr_s_l
+ _mbtowc_l
+ _memccpy
+ _memicmp
+ _memicmp_l
+ _mkdir
+ _mkgmtime32
+ _mkgmtime64
+ _mktemp
+ _mktemp_s
+ _mktime32
+ _mktime64
+ _msize
+ _nextafter
+ _open
+ _open_osfhandle
+ _outp
+ _outpd
+ _outpw
+ _pclose
+ _pctype
+ _pgmptr
+ _pipe
+ _popen
+ _printf_l
+ _printf_p
+ _printf_p_l
+ _printf_s_l
+ _purecall
+ _putch
+ _putch_nolock
+ _putenv
+ _putenv_s
+ _putw
+ _putwch
+ _putwch_nolock
+ _putws
+ _pwctype
+ _read
+ _realloc_crt
+ _recalloc
+ _recalloc_crt
+ _resetstkoflw
+ _rmdir
+ _rmtmp
+ _rotl
+ _rotl64
+ _rotr
+ _rotr64
+ _scalb
+ _scanf_l
+ _scanf_s_l
+ _scprintf
+ _scprintf_l
+ _scprintf_p
+ _scprintf_p_l
+ _scwprintf
+ _scwprintf_l
+ _scwprintf_p
+ _scwprintf_p_l
+ _searchenv
+ _searchenv_s
+ _seh_longjmp_unwind4
+ _seh_longjmp_unwind
+ _set_SSE2_enable
+ _set_abort_behavior
+ _set_controlfp
+ _set_doserrno
+ _set_errno
+ _set_error_mode
+ _set_fmode
+ _set_invalid_parameter_handler
+ _set_malloc_crt_max_wait
+ _set_output_format
+ _set_printf_count_output
+ _set_purecall_handler
+ _seterrormode
+ _setjmp
+ _setjmp3
+ _setmaxstdio
+ _setmbcp
+ _setmode
+ _setsystime
+ _sleep
+ _snprintf
+ _snprintf_c
+ _snprintf_c_l
+ _snprintf_l
+ _snprintf_s
+ _snprintf_s_l
+ _snscanf
+ _snscanf_l
+ _snscanf_s
+ _snscanf_s_l
+ _snwprintf
+ _snwprintf_l
+ _snwprintf_s
+ _snwprintf_s_l
+ _snwscanf
+ _snwscanf_l
+ _snwscanf_s
+ _snwscanf_s_l
+ _sopen
+ _sopen_s
+ _spawnl
+ _spawnle
+ _spawnlp
+ _spawnlpe
+ _spawnv
+ _spawnve
+ _spawnvp
+ _spawnvpe
+ _splitpath
+ _splitpath_s
+ _sprintf_l
+ _sprintf_p
+ _sprintf_p_l
+ _sprintf_s_l
+ _sscanf_l
+ _sscanf_s_l
+ _stat32
+ _stat32i64
+ _stat64
+ _stat64i32
+ _statusfp
+ _statusfp2
+ _strcoll_l
+ _strdate
+ _strdate_s
+ _strdup
+ _strerror
+ _strerror_s
+ _strftime_l
+ _stricmp
+ _stricmp_l
+ _stricoll
+ _stricoll_l
+ _strlwr
+ _strlwr_l
+ _strlwr_s
+ _strlwr_s_l
+ _strncoll
+ _strncoll_l
+ _strnicmp
+ _strnicmp_l
+ _strnicoll
+ _strnicoll_l
+ _strnset
+ _strnset_s
+ _strrev
+ _strset
+ _strset_s
+ _strtime
+ _strtime_s
+ _strtod_l
+ _strtoi64
+ _strtoi64_l
+ _strtol_l
+ _strtoui64
+ _strtoui64_l
+ _strtoul_l
+ _strupr
+ _strupr_l
+ _strupr_s
+ _strupr_s_l
+ _strxfrm_l
+ _swab
+ _swprintf
+ _swprintf_c
+ _swprintf_c_l
+ _swprintf_p
+ _swprintf_p_l
+ _swprintf_s_l
+ _swscanf_l
+ _swscanf_s_l
+ _sys_errlist
+ _sys_nerr
+ _tell
+ _telli64
+ _tempnam
+ _time32
+ _time64
+ _timezone
+ _tolower
+ _tolower_l
+ _toupper
+ _toupper_l
+ _towlower_l
+ _towupper_l
+ _tzname
+ _tzset
+ _ui64toa
+ _ui64toa_s
+ _ui64tow
+ _ui64tow_s
+ _ultoa
+ _ultoa_s
+ _ultow
+ _ultow_s
+ _umask
+ _umask_s
+ _ungetc_nolock
+ _ungetch
+ _ungetch_nolock
+ _ungetwc_nolock
+ _ungetwch
+ _ungetwch_nolock
+ _unlink
+ _unloaddll
+ _unlock
+ _unlock_file
+ _utime32
+ _utime64
+ _vcprintf
+ _vcprintf_l
+ _vcprintf_p
+ _vcprintf_p_l
+ _vcprintf_s
+ _vcprintf_s_l
+ _vcwprintf
+ _vcwprintf_l
+ _vcwprintf_p
+ _vcwprintf_p_l
+ _vcwprintf_s
+ _vcwprintf_s_l
+ _vfprintf_l
+ _vfprintf_p
+ _vfprintf_p_l
+ _vfprintf_s_l
+ _vfwprintf_l
+ _vfwprintf_p
+ _vfwprintf_p_l
+ _vfwprintf_s_l
+ _vprintf_l
+ _vprintf_p
+ _vprintf_p_l
+ _vprintf_s_l
+ _vscprintf
+ _vscprintf_l
+ _vscprintf_p
+ _vscprintf_p_l
+ _vscwprintf
+ _vscwprintf_l
+ _vscwprintf_p
+ _vscwprintf_p_l
+ _vsnprintf
+ _vsnprintf_c
+ _vsnprintf_c_l
+ _vsnprintf_l
+ _vsnprintf_s
+ _vsnprintf_s_l
+ _vsnwprintf
+ _vsnwprintf_l
+ _vsnwprintf_s
+ _vsnwprintf_s_l
+ _vsprintf_l
+ _vsprintf_p
+ _vsprintf_p_l
+ _vsprintf_s_l
+ _vswprintf
+ _vswprintf_c
+ _vswprintf_c_l
+ _vswprintf_l
+ _vswprintf_p
+ _vswprintf_p_l
+ _vswprintf_s_l
+ _vwprintf_l
+ _vwprintf_p
+ _vwprintf_p_l
+ _vwprintf_s_l
+ _waccess
+ _waccess_s
+ _wasctime
+ _wasctime_s
+ _wassert
+ _wchdir
+ _wchmod
+ ;fixme?; _wcmdln
+ _wcreat
+ _wcscoll_l
+ _wcsdup
+ _wcserror
+ _wcserror_s
+ _wcsftime_l
+ _wcsicmp
+ _wcsicmp_l
+ _wcsicoll
+ _wcsicoll_l
+ _wcslwr
+ _wcslwr_l
+ _wcslwr_s
+ _wcslwr_s_l
+ _wcsncoll
+ _wcsncoll_l
+ _wcsnicmp
+ _wcsnicmp_l
+ _wcsnicoll
+ _wcsnicoll_l
+ _wcsnset
+ _wcsnset_s
+ _wcsrev
+ _wcsset
+ _wcsset_s
+ _wcstod_l
+ _wcstoi64
+ _wcstoi64_l
+ _wcstol_l
+ _wcstombs_l
+ _wcstombs_s_l
+ _wcstoui64
+ _wcstoui64_l
+ _wcstoul_l
+ _wcsupr
+ _wcsupr_l
+ _wcsupr_s
+ _wcsupr_s_l
+ _wcsxfrm_l
+ _wctime32
+ _wctime32_s
+ _wctime64
+ _wctime64_s
+ _wctomb_l
+ _wctomb_s_l
+ _wctype
+ _wdupenv_s
+ _wenviron
+ _wexecl
+ _wexecle
+ _wexeclp
+ _wexeclpe
+ _wexecv
+ _wexecve
+ _wexecvp
+ _wexecvpe
+ _wfdopen
+ _wfindfirst32
+ _wfindfirst32i64
+ _wfindfirst64
+ _wfindfirst64i32
+ _wfindnext32
+ _wfindnext32i64
+ _wfindnext64
+ _wfindnext64i32
+ _wfopen
+ _wfopen_s
+ _wfreopen
+ _wfreopen_s
+ _wfsopen
+ _wfullpath
+ _wgetcwd
+ _wgetdcwd
+ _wgetdcwd_nolock
+ _wgetenv
+ _wgetenv_s
+ _wmakepath
+ _wmakepath_s
+ _wmkdir
+ _wmktemp
+ _wmktemp_s
+ _wopen
+ _wperror
+ _wpgmptr
+ _wpopen
+ _wprintf_l
+ _wprintf_p
+ _wprintf_p_l
+ _wprintf_s_l
+ _wputenv
+ _wputenv_s
+ _wremove
+ _wrename
+ _write
+ _wrmdir
+ _wscanf_l
+ _wscanf_s_l
+ _wsearchenv
+ _wsearchenv_s
+ _wsetlocale
+ _wsopen
+ _wsopen_s
+ _wspawnl
+ _wspawnle
+ _wspawnlp
+ _wspawnlpe
+ _wspawnv
+ _wspawnve
+ _wspawnvp
+ _wspawnvpe
+ _wsplitpath
+ _wsplitpath_s
+ _wstat32
+ _wstat32i64
+ _wstat64
+ _wstat64i32
+ _wstrdate
+ _wstrdate_s
+ _wstrtime
+ _wstrtime_s
+ _wsystem
+ _wtempnam
+ _wtmpnam
+ _wtmpnam_s
+ _wtof
+ _wtof_l
+ _wtoi
+ _wtoi64
+ _wtoi64_l
+ _wtoi_l
+ _wtol
+ _wtol_l
+ _wunlink
+ _wutime32
+ _wutime64
+ _y0
+ _y1
+ _yn
+ abort
+ abs
+ acos
+ asctime
+ asctime_s
+ asin
+ atan
+ atan2
+ atexit
+ atof
+ atoi
+ atol
+ bsearch
+ bsearch_s
+ btowc
+ calloc
+ ceil
+ clearerr
+ clearerr_s
+ clock
+ cos
+ cosh
+ div
+ exit
+ exp
+ fabs
+ fclose
+ feof
+ ferror
+ fflush
+ fgetc
+ fgetpos
+ fgets
+ fgetwc
+ fgetws
+ floor
+ fmod
+ fopen
+ fopen_s
+ fprintf
+ fprintf_s
+ fputc
+ fputs
+ fputwc
+ fputws
+ fread
+ fread_s
+ free
+ freopen
+ freopen_s
+ frexp
+ fscanf
+ fscanf_s
+ fseek
+ fsetpos
+ ftell
+ fwprintf
+ fwprintf_s
+ fwrite
+ fwscanf
+ fwscanf_s
+ getc
+ getchar
+ getenv
+ getenv_s
+ gets
+ gets_s
+ getwc
+ getwchar
+ is_wctype
+ isalnum
+ isalpha
+ iscntrl
+ isdigit
+ isgraph
+ isleadbyte
+ islower
+ isprint
+ ispunct
+ isspace
+ isupper
+ iswalnum
+ iswalpha
+ iswascii
+ iswcntrl
+ iswctype
+ iswdigit
+ iswgraph
+ iswlower
+ iswprint
+ iswpunct
+ iswspace
+ iswupper
+ iswxdigit
+ isxdigit
+ labs
+ ldexp
+ ldiv
+ llabs
+ lldiv
+ localeconv
+ log
+ log10
+ longjmp
+ malloc
+ mblen
+ mbrlen
+ mbrtowc
+ mbsrtowcs
+ mbsrtowcs_s
+ mbstowcs
+ mbstowcs_s
+ mbtowc
+ memchr
+ memcmp
+ memcpy
+ memcpy_s
+ memmove
+ memmove_s
+ memset
+ modf
+ perror
+ pow
+ printf
+ printf_s
+ putc
+ putchar
+ puts
+ putwc
+ putwchar
+ qsort
+ qsort_s
+ raise
+ rand
+ rand_s
+ realloc
+ remove
+ rename
+ rewind
+ scanf
+ scanf_s
+ setbuf
+ setlocale
+ setvbuf
+ signal
+ sin
+ sinh
+ sprintf
+ sprintf_s
+ sqrt
+ srand
+ sscanf
+ sscanf_s
+ strcat
+ strcat_s
+ strchr
+ strcmp
+ strcoll
+ strcpy
+ strcpy_s
+ strcspn
+ strerror
+ strerror_s
+ strftime
+ strlen
+ strncat
+ strncat_s
+ strncmp
+ strncpy
+ strncpy_s
+ strnlen
+ strpbrk
+ strrchr
+ strspn
+ strstr
+ strtod
+ strtok
+ strtok_s
+ strtol
+ strtoul
+ strxfrm
+ swprintf_s
+ swscanf
+ swscanf_s
+ system
+ tan
+ tanh
+ tmpfile
+ tmpfile_s
+ tmpnam
+ tmpnam_s
+ tolower
+ toupper
+ towlower
+ towupper
+ ungetc
+ ungetwc
+ vfprintf
+ vfprintf_s
+ vfwprintf
+ vfwprintf_s
+ vprintf
+ vprintf_s
+ vsprintf
+ vsprintf_s
+ vswprintf_s
+ vwprintf
+ vwprintf_s
+ wcrtomb
+ wcrtomb_s
+ wcscat
+ wcscat_s
+ wcschr
+ wcscmp
+ wcscoll
+ wcscpy
+ wcscpy_s
+ wcscspn
+ wcsftime
+ wcslen
+ wcsncat
+ wcsncat_s
+ wcsncmp
+ wcsncpy
+ wcsncpy_s
+ wcsnlen
+ wcspbrk
+ wcsrchr
+ wcsrtombs
+ wcsrtombs_s
+ wcsspn
+ wcsstr
+ wcstod
+ wcstok
+ wcstok_s
+ wcstol
+ wcstombs
+ wcstombs_s
+ wcstoul
+ wcsxfrm
+ wctob
+ wctomb
+ wctomb_s
+ wmemcpy_s
+ wmemmove_s
+ wprintf
+ wprintf_s
+ wscanf
+ wscanf_s
+
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def b/src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def
new file mode 100644
index 00000000..8787224f
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def
@@ -0,0 +1,395 @@
+; $Id: VBoxRT-openssl-1.1plus.def $
+;; @file
+; IPRT - Windows OpenSSL exports we use outside VBoxRT (keep them few!).
+;
+; This file is appended to the architecture specific .def file.
+;
+
+;
+; Copyright (C) 2009-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+ ; ConsoleImpl.cpp uses this when VBOX_OPENSSL_FIPS is enabled.
+ FIPS_mode
+
+ ; VBoxVRDP.dll - secure.cpp
+ BIO_free
+ BIO_new_file
+ BN_bin2bn
+ BN_bn2bin
+ BN_CTX_free
+ BN_CTX_new
+ BN_free
+ BN_mod_exp
+ BN_new
+ BN_num_bits
+ BN_set_word
+ EVP_PKEY_get0_RSA
+ i2d_X509
+ MD5_Final
+ MD5_Init
+ MD5_Update
+ OBJ_obj2nid
+ PEM_read_bio_PrivateKey
+ PEM_read_bio_X509
+ RAND_bytes
+ RC4
+ RC4_set_key
+ RSA_free
+ RSA_generate_key_ex
+ RSA_get0_key
+ RSA_new
+ SHA1_Final
+ SHA1_Init
+ SHA1_Update
+ X509_free
+ X509_get_X509_PUBKEY
+ X509_PUBKEY_get0_param
+
+ ; VBoxVRDP.dll - tcp_vrdp.cpp
+ BIO_new_socket
+ BIO_test_flags
+ OPENSSL_init_ssl
+ SSL_accept
+ SSL_CTX_free
+ SSL_CTX_load_verify_locations
+ SSL_CTX_new
+ SSL_CTX_set_verify
+ SSL_CTX_use_certificate_file
+ SSL_CTX_use_PrivateKey_file
+ SSL_free
+ SSL_get_certificate
+ SSL_new
+ SSL_pending
+ SSL_read
+ SSL_set_bio
+ SSL_set_read_ahead
+ SSL_write
+ TLSv1_server_method
+ X509_get_issuer_name
+ X509_NAME_oneline
+
+ ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDKeyStore.cpp:
+ EVP_aes_128_xts
+ EVP_aes_256_xts
+ EVP_CIPHER_CTX_free
+ EVP_CIPHER_CTX_new
+ EVP_DecryptFinal
+ EVP_DecryptInit
+ EVP_DecryptUpdate
+ EVP_EncryptFinal
+ EVP_EncryptInit
+ EVP_EncryptUpdate
+ EVP_MD_size
+ EVP_sha1
+ EVP_sha256
+ EVP_sha512
+ PKCS5_PBKDF2_HMAC
+ ;exported above: RAND_bytes
+
+ ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDFilterCrypt.cpp:
+ ;exported above: EVP_aes_128_xts
+ ;exported above: EVP_aes_256_xts
+ ;exported above: EVP_CIPHER_CTX_free
+ ;exported above: EVP_CIPHER_CTX_new
+ ;exported above: EVP_DecryptFinal
+ ;exported above: EVP_DecryptInit
+ ;exported above: EVP_DecryptUpdate
+ ;exported above: EVP_EncryptFinal
+ ;exported above: EVP_EncryptInit
+ ;exported above: EVP_EncryptUpdate
+ ;exported above: RAND_bytes
+
+ ; vboxwebsrv needs SSL support.
+ ASN1_STRING_data
+ ASN1_STRING_get0_data
+ ASN1_STRING_to_UTF8
+ ;exported above: BIO_free
+ ;exported above: BIO_new_file
+ ;exported above: BIO_new_socket
+ BIO_read
+ BIO_write
+ CRYPTO_free
+ DH_check
+ DH_free
+ DH_generate_parameters
+ DH_generate_parameters_ex
+ DH_new
+ ERR_clear_error
+ ERR_error_string
+ ERR_error_string_n
+ ERR_get_error
+ ERR_peek_error
+ GENERAL_NAME_free
+ i2v_GENERAL_NAMES
+ OpenSSL_version_num
+ OPENSSL_init_crypto
+ ;exported above: OPENSSL_init_ssl
+ OPENSSL_sk_num
+ OPENSSL_sk_pop_free
+ OPENSSL_sk_value
+ PEM_read_bio_DHparams
+ RAND_load_file
+ RAND_pseudo_bytes
+ RAND_seed
+ RAND_status
+ ;exported above: RSA_free
+ RSA_generate_key
+ ;exported above: SSL_accept
+ SSL_clear
+ SSL_connect
+ SSL_ctrl
+ SSL_CTX_ctrl
+ ;exported above: SSL_CTX_free
+ SSL_CTX_get_cert_store
+ ;exported above: SSL_CTX_load_verify_locations
+ ;exported above: SSL_CTX_new
+ SSL_CTX_set_client_CA_list
+ SSL_CTX_set_default_passwd_cb
+ SSL_CTX_set_default_passwd_cb_userdata
+ SSL_CTX_set_default_verify_paths
+ SSL_CTX_set_options
+ SSL_CTX_set_session_id_context
+ ;exported above: SSL_CTX_set_verify
+ SSL_CTX_set_verify_depth
+ SSL_CTX_use_certificate_chain_file
+ ;exported above: SSL_CTX_use_PrivateKey_file
+ ;exported above: SSL_free
+ SSL_get_error
+ SSL_get_peer_certificate
+ SSL_get_verify_result
+ SSL_get1_session
+ SSL_is_init_finished
+ SSL_load_client_CA_file
+ ;exported above: SSL_new
+ SSL_peek
+ ;exported above: SSL_read
+ SSL_SESSION_free
+ ;exported above: SSL_set_bio
+ SSL_set_session
+ SSL_shutdown
+ SSL_want
+ ;exported above: SSL_write
+ TLS_method
+ ;exported above: X509_free
+ X509_get_ext_d2i
+ ;exported above: X509_get_issuer_name
+ X509_get_subject_name
+ X509_load_crl_file
+ X509_LOOKUP_file
+ X509_NAME_ENTRY_get_data
+ X509_NAME_get_entry
+ X509_NAME_get_index_by_NID
+ ;exported above: X509_NAME_oneline
+ X509_STORE_add_lookup
+ X509_STORE_CTX_get_current_cert
+ X509_STORE_CTX_get_error
+ X509_STORE_CTX_get_error_depth
+ X509_STORE_CTX_set_error
+ X509_STORE_set1_param
+ X509_STORE_set_flags
+ X509_verify_cert_error_string
+ X509_VERIFY_PARAM_free
+ X509_VERIFY_PARAM_new
+ X509_VERIFY_PARAM_set_flags
+ X509V3_conf_free
+
+ ; tstRTBigNum.cpp
+ BN_div
+ BN_mul
+ BN_mod_exp_simple
+ BN_ucmp
+
+ ; VBox-libtpms
+ BN_set_flags
+ BN_clear_free
+ BN_cmp
+ BN_dup
+ BN_sub
+ BN_add
+ BN_copy
+ RAND_add
+ BN_is_zero
+ BN_is_one
+ BN_value_one
+ BN_CTX_start
+ BN_CTX_get
+ BN_CTX_end
+ BN_mod_add
+ BN_mod_mul
+ BN_lshift
+ BN_mask_bits
+ BN_rshift
+ BN_mod_inverse
+ RSA_size
+ RSA_set0_key
+ RSA_get0_factors
+ RSA_public_encrypt
+ RSA_private_encrypt
+ RSA_private_decrypt
+ RSA_sign
+ RSA_verify
+ RSA_padding_add_PKCS1_type_1
+ RSA_padding_add_PKCS1_type_2
+ RSA_padding_check_PKCS1_type_1
+ RSA_padding_check_PKCS1_type_2
+ RSA_padding_add_PKCS1_OAEP
+ RSA_padding_check_PKCS1_OAEP
+ ERR_get_error_line_data
+ AES_set_encrypt_key
+ AES_set_decrypt_key
+ AES_decrypt
+ AES_encrypt
+ AES_cbc_encrypt
+ AES_ofb128_encrypt
+ SHA256_Init
+ SHA256_Update
+ SHA256_Final
+ SHA384_Init
+ SHA384_Update
+ SHA384_Final
+ SHA512_Init
+ SHA512_Update
+ SHA512_Final
+ EC_GROUP_free
+ EC_GROUP_set_generator
+ EC_GROUP_new_curve_GFp
+ EC_POINT_new
+ EC_POINT_free
+ EC_POINT_clear_free
+ EC_POINT_set_affine_coordinates
+ EC_POINT_get_affine_coordinates
+ EC_POINT_add
+ EC_POINTs_mul
+ EC_POINT_mul
+ Camellia_set_key
+ Camellia_encrypt
+ Camellia_decrypt
+ DES_ecb3_encrypt
+ DES_set_key_unchecked
+
+ ; VBox-libssh
+ BIO_ctrl
+ BIO_new
+ BIO_new_mem_buf
+ BIO_s_mem
+ BN_bn2hex
+ BN_is_bit_set
+ d2i_DSA_SIG
+ d2i_ECDSA_SIG
+ DH_compute_key
+ DH_generate_key
+ DH_get0_key
+ DH_get0_pqg
+ DH_set0_key
+ DH_set0_pqg
+ DH_size
+ DSA_free
+ DSA_generate_key
+ DSA_generate_parameters_ex
+ DSA_get0_key
+ DSA_get0_pqg
+ DSA_new
+ DSA_set0_key
+ DSA_set0_pqg
+ DSA_SIG_free
+ DSA_SIG_get0
+ DSA_SIG_new
+ DSA_SIG_set0
+ DSA_size
+ EC_GROUP_cmp
+ EC_GROUP_get_curve_name
+ EC_GROUP_get_degree
+ EC_KEY_dup
+ EC_KEY_free
+ EC_KEY_generate_key
+ EC_KEY_get0_group
+ EC_KEY_get0_private_key
+ EC_KEY_get0_public_key
+ EC_KEY_new_by_curve_name
+ EC_KEY_set_asn1_flag
+ EC_KEY_set_private_key
+ EC_KEY_set_public_key
+ EC_POINT_cmp
+ EC_POINT_oct2point
+ EC_POINT_point2oct
+ ECDH_compute_key
+ ECDSA_SIG_free
+ ECDSA_SIG_get0
+ ECDSA_SIG_new
+ ECDSA_SIG_set0
+ EVP_aes_128_cbc
+ EVP_aes_128_ctr
+ EVP_aes_128_gcm
+ EVP_aes_192_cbc
+ EVP_aes_192_ctr
+ EVP_aes_256_cbc
+ EVP_aes_256_ctr
+ EVP_aes_256_gcm
+ EVP_CIPHER_CTX_ctrl
+ EVP_CIPHER_CTX_reset
+ EVP_CIPHER_CTX_set_padding
+ EVP_DecryptInit_ex
+ EVP_DigestFinal
+ EVP_DigestInit
+ EVP_DigestInit_ex
+ EVP_DigestSignFinal
+ EVP_DigestSignInit
+ EVP_DigestUpdate
+ EVP_DigestVerifyFinal
+ EVP_DigestVerifyInit
+ EVP_EncryptInit_ex
+ EVP_MD_CTX_free
+ EVP_MD_CTX_new
+ EVP_MD_CTX_reset
+ EVP_md5
+ EVP_PKEY_base_id
+ EVP_PKEY_free
+ EVP_PKEY_get1_DSA
+ EVP_PKEY_get1_EC_KEY
+ EVP_PKEY_get1_RSA
+ EVP_PKEY_new
+ EVP_PKEY_set1_DSA
+ EVP_PKEY_set1_EC_KEY
+ EVP_PKEY_set1_RSA
+ EVP_PKEY_size
+ EVP_sha384
+ HMAC_CTX_free
+ HMAC_CTX_new
+ HMAC_Final
+ HMAC_Init_ex
+ HMAC_Update
+ i2d_DSA_SIG
+ i2d_ECDSA_SIG
+ OpenSSL_version
+ PEM_write_bio_PrivateKey
+ RSA_get0_crt_params
+ RSA_set0_crt_params
+ RSA_set0_factors
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def b/src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def
new file mode 100644
index 00000000..ea9407a8
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def
@@ -0,0 +1,430 @@
+; $Id: VBoxRT-openssl-3.0.def $
+;; @file
+; IPRT - Windows OpenSSL exports we use outside VBoxRT (keep them few!).
+;
+; This file is appended to the architecture specific .def file.
+;
+
+;
+; Copyright (C) 2009-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+ ; VBoxVRDP.dll - secure.cpp
+ BIO_free
+ BIO_new_file
+ BN_bin2bn
+ BN_bn2bin
+ BN_CTX_free
+ BN_CTX_new
+ BN_free
+ BN_mod_exp
+ BN_new
+ BN_num_bits
+ BN_set_word
+ EVP_PKEY_get0_RSA
+ i2d_X509
+ MD5_Final
+ MD5_Init
+ MD5_Update
+ OBJ_obj2nid
+ PEM_read_bio_PrivateKey
+ PEM_read_bio_X509
+ RAND_bytes
+ RC4
+ RC4_set_key
+ RSA_free
+ RSA_generate_key_ex
+ RSA_get0_key
+ RSA_new
+ SHA1_Final
+ SHA1_Init
+ SHA1_Update
+ X509_free
+ X509_get_X509_PUBKEY
+ X509_PUBKEY_get0_param
+
+ ; VBoxVRDP.dll - tcp_vrdp.cpp
+ BIO_new_socket
+ BIO_test_flags
+ OPENSSL_init_ssl
+ SSL_accept
+ SSL_CTX_free
+ SSL_CTX_load_verify_locations
+ SSL_CTX_new
+ SSL_CTX_set_verify
+ SSL_CTX_use_certificate_file
+ SSL_CTX_use_PrivateKey_file
+ SSL_free
+ SSL_get_certificate
+ SSL_new
+ SSL_pending
+ SSL_read
+ SSL_set_bio
+ SSL_set_read_ahead
+ SSL_write
+ TLSv1_server_method
+ X509_get_issuer_name
+ X509_NAME_oneline
+
+ ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDKeyStore.cpp:
+ EVP_aes_128_xts
+ EVP_aes_256_xts
+ EVP_CIPHER_CTX_free
+ EVP_CIPHER_CTX_new
+ EVP_DecryptFinal
+ EVP_DecryptInit
+ EVP_DecryptUpdate
+ EVP_EncryptFinal
+ EVP_EncryptInit
+ EVP_EncryptUpdate
+ EVP_MD_get_size
+ EVP_sha1
+ EVP_sha256
+ EVP_sha512
+ PKCS5_PBKDF2_HMAC
+ ;exported above: RAND_bytes
+
+ ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDFilterCrypt.cpp:
+ ;exported above: EVP_aes_128_xts
+ ;exported above: EVP_aes_256_xts
+ ;exported above: EVP_CIPHER_CTX_free
+ ;exported above: EVP_CIPHER_CTX_new
+ ;exported above: EVP_DecryptFinal
+ ;exported above: EVP_DecryptInit
+ ;exported above: EVP_DecryptUpdate
+ ;exported above: EVP_EncryptFinal
+ ;exported above: EVP_EncryptInit
+ ;exported above: EVP_EncryptUpdate
+ ;exported above: RAND_bytes
+
+ ; vboxwebsrv needs SSL support.
+ ASN1_STRING_get0_data
+ ASN1_STRING_to_UTF8
+ ;exported above: BIO_free
+ ;exported above: BIO_new_file
+ ;exported above: BIO_new_socket
+ BIO_read
+ BIO_write
+ CRYPTO_free
+ DH_check
+ DH_free
+ DH_generate_parameters
+ DH_generate_parameters_ex
+ DH_new
+ ERR_clear_error
+ ERR_error_string
+ ERR_error_string_n
+ ERR_get_error
+ ERR_peek_error
+ GENERAL_NAME_free
+ i2v_GENERAL_NAMES
+ OpenSSL_version_num
+ OPENSSL_init_crypto
+ ;exported above: OPENSSL_init_ssl
+ OPENSSL_sk_num
+ OPENSSL_sk_pop_free
+ OPENSSL_sk_value
+ PEM_read_bio_DHparams
+ RAND_load_file
+ RAND_seed
+ RAND_status
+ ;exported above: RSA_free
+ RSA_generate_key
+ ;exported above: SSL_accept
+ SSL_clear
+ SSL_connect
+ SSL_ctrl
+ SSL_CTX_ctrl
+ ;exported above: SSL_CTX_free
+ SSL_CTX_get_cert_store
+ ;exported above: SSL_CTX_load_verify_locations
+ ;exported above: SSL_CTX_new
+ SSL_CTX_set_client_CA_list
+ SSL_CTX_set_default_passwd_cb
+ SSL_CTX_set_default_passwd_cb_userdata
+ SSL_CTX_set_default_verify_paths
+ SSL_CTX_set_options
+ SSL_CTX_set_session_id_context
+ ;exported above: SSL_CTX_set_verify
+ SSL_CTX_set_verify_depth
+ SSL_CTX_use_certificate_chain_file
+ ;exported above: SSL_CTX_use_PrivateKey_file
+ ;exported above: SSL_free
+ SSL_get_error
+ SSL_get1_peer_certificate
+ SSL_get_verify_result
+ SSL_get1_session
+ SSL_is_init_finished
+ SSL_load_client_CA_file
+ ;exported above: SSL_new
+ SSL_peek
+ ;exported above: SSL_read
+ SSL_SESSION_free
+ ;exported above: SSL_set_bio
+ SSL_set_session
+ SSL_shutdown
+ SSL_want
+ ;exported above: SSL_write
+ TLS_method
+ ;exported above: X509_free
+ X509_get_ext_d2i
+ ;exported above: X509_get_issuer_name
+ X509_get_subject_name
+ X509_load_crl_file
+ X509_LOOKUP_file
+ X509_NAME_ENTRY_get_data
+ X509_NAME_get_entry
+ X509_NAME_get_index_by_NID
+ ;exported above: X509_NAME_oneline
+ X509_STORE_add_lookup
+ X509_STORE_CTX_get_current_cert
+ X509_STORE_CTX_get_error
+ X509_STORE_CTX_get_error_depth
+ X509_STORE_CTX_set_error
+ X509_STORE_set1_param
+ X509_STORE_set_flags
+ X509_verify_cert_error_string
+ X509_VERIFY_PARAM_free
+ X509_VERIFY_PARAM_new
+ X509_VERIFY_PARAM_set_flags
+ X509V3_conf_free
+
+ ; tstRTBigNum.cpp
+ BN_div
+ BN_mul
+ BN_mod_exp_simple
+ BN_ucmp
+
+ ; VBox-libtpms
+ BN_set_flags
+ BN_clear_free
+ BN_cmp
+ BN_dup
+ BN_sub
+ BN_add
+ BN_copy
+ RAND_add
+ BN_is_zero
+ BN_is_one
+ BN_value_one
+ BN_CTX_start
+ BN_CTX_get
+ BN_CTX_end
+ BN_mod_add
+ BN_mod_mul
+ BN_lshift
+ BN_mask_bits
+ BN_rshift
+ BN_mod_inverse
+ RSA_size
+ RSA_set0_key
+ RSA_get0_factors
+ RSA_public_encrypt
+ RSA_private_encrypt
+ RSA_private_decrypt
+ RSA_sign
+ RSA_verify
+ RSA_padding_add_PKCS1_type_1
+ RSA_padding_add_PKCS1_type_2
+ RSA_padding_check_PKCS1_type_1
+ RSA_padding_check_PKCS1_type_2
+ RSA_padding_add_PKCS1_OAEP
+ RSA_padding_check_PKCS1_OAEP
+ ERR_get_error_line_data
+ AES_set_encrypt_key
+ AES_set_decrypt_key
+ AES_decrypt
+ AES_encrypt
+ AES_cbc_encrypt
+ AES_ofb128_encrypt
+ SHA256_Init
+ SHA256_Update
+ SHA256_Final
+ SHA384_Init
+ SHA384_Update
+ SHA384_Final
+ SHA512_Init
+ SHA512_Update
+ SHA512_Final
+ EC_GROUP_free
+ EC_GROUP_set_generator
+ EC_GROUP_new_curve_GFp
+ EC_POINT_new
+ EC_POINT_free
+ EC_POINT_clear_free
+ EC_POINT_set_affine_coordinates
+ EC_POINT_get_affine_coordinates
+ EC_POINT_add
+ EC_POINTs_mul
+ EC_POINT_mul
+ Camellia_set_key
+ Camellia_encrypt
+ Camellia_decrypt
+ DES_ecb3_encrypt
+ DES_set_key_unchecked
+
+ ; VBox-libssh
+ BIO_ctrl
+ BIO_new
+ BIO_new_mem_buf
+ BIO_s_mem
+ BN_bn2hex
+ BN_is_bit_set
+ d2i_DSA_SIG
+ d2i_ECDSA_SIG
+ DH_compute_key
+ DH_generate_key
+ DH_get0_key
+ DH_get0_pqg
+ DH_set0_key
+ DH_set0_pqg
+ DH_size
+ DSA_free
+ DSA_generate_key
+ DSA_generate_parameters_ex
+ DSA_get0_key
+ DSA_get0_pqg
+ DSA_new
+ DSA_set0_key
+ DSA_set0_pqg
+ DSA_SIG_free
+ DSA_SIG_get0
+ DSA_SIG_new
+ DSA_SIG_set0
+ DSA_size
+ EC_GROUP_cmp
+ EC_GROUP_get_curve_name
+ EC_GROUP_get_degree
+ EC_KEY_dup
+ EC_KEY_free
+ EC_KEY_generate_key
+ EC_KEY_get0_group
+ EC_KEY_get0_private_key
+ EC_KEY_get0_public_key
+ EC_KEY_new_by_curve_name
+ EC_KEY_set_asn1_flag
+ EC_KEY_set_private_key
+ EC_KEY_set_public_key
+ EC_POINT_cmp
+ EC_POINT_oct2point
+ EC_POINT_point2oct
+ ECDH_compute_key
+ ECDSA_SIG_free
+ ECDSA_SIG_get0
+ ECDSA_SIG_new
+ ECDSA_SIG_set0
+ EVP_aes_128_cbc
+ EVP_aes_128_ctr
+ EVP_aes_128_gcm
+ EVP_aes_192_cbc
+ EVP_aes_192_ctr
+ EVP_aes_256_cbc
+ EVP_aes_256_ctr
+ EVP_aes_256_gcm
+ EVP_CIPHER_CTX_ctrl
+ EVP_CIPHER_CTX_reset
+ EVP_CIPHER_CTX_set_padding
+ EVP_DecryptInit_ex
+ EVP_DigestFinal
+ EVP_DigestInit
+ EVP_DigestInit_ex
+ EVP_DigestSignFinal
+ EVP_DigestSignInit
+ EVP_DigestSignUpdate
+ EVP_DigestUpdate
+ EVP_DigestVerifyFinal
+ EVP_DigestVerifyInit
+ EVP_DigestVerifyUpdate
+ EVP_EncryptInit_ex
+ EVP_MD_CTX_free
+ EVP_MD_CTX_new
+ EVP_MD_CTX_reset
+ EVP_md5
+ EVP_PKEY_get_base_id
+ EVP_PKEY_free
+ EVP_PKEY_get1_DSA
+ EVP_PKEY_get1_EC_KEY
+ EVP_PKEY_get1_RSA
+ EVP_PKEY_new
+ EVP_PKEY_set1_DSA
+ EVP_PKEY_set1_EC_KEY
+ EVP_PKEY_set1_RSA
+ EVP_PKEY_get_size
+ EVP_sha384
+ HMAC_CTX_free
+ HMAC_CTX_new
+ HMAC_Final
+ HMAC_Init_ex
+ HMAC_Update
+ i2d_DSA_SIG
+ i2d_ECDSA_SIG
+ OpenSSL_version
+ PEM_write_bio_PrivateKey
+ RSA_get0_crt_params
+ RSA_set0_crt_params
+ RSA_set0_factors
+ ; since 0.10.5 also:
+ EC_KEY_up_ref
+ ENGINE_by_id
+ ENGINE_free
+ ENGINE_init
+ ENGINE_load_builtin_engines
+ EVP_default_properties_is_fips_enabled
+ EVP_PKEY_CTX_free
+ EVP_PKEY_CTX_new_from_name
+ EVP_PKEY_CTX_new_from_pkey
+ EVP_PKEY_CTX_set_params
+ EVP_PKEY_derive
+ EVP_PKEY_derive_init
+ EVP_PKEY_derive_set_peer
+ EVP_PKEY_eq
+ EVP_PKEY_fromdata
+ EVP_PKEY_fromdata_init
+ EVP_PKEY_generate
+ EVP_PKEY_get_bits
+ EVP_PKEY_get_bn_param
+ EVP_PKEY_keygen_init
+ EVP_PKEY_new_mac_key
+ EVP_PKEY_paramgen_init
+ EVP_PKEY_set_bn_param
+ EVP_PKEY_todata
+ EVP_PKEY_up_ref
+ OSSL_PARAM_BLD_free
+ OSSL_PARAM_BLD_new
+ OSSL_PARAM_BLD_push_BN
+ OSSL_PARAM_BLD_to_param
+ OSSL_PARAM_construct_end
+ OSSL_PARAM_construct_int
+ OSSL_PARAM_construct_uint
+ OSSL_PARAM_free
+ OSSL_PARAM_get_BN
+ OSSL_PARAM_locate_const
+ OSSL_PARAM_merge
+
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-win32.def b/src/VBox/Runtime/r3/win/VBoxRT-win32.def
new file mode 100644
index 00000000..261b3ce1
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-win32.def
@@ -0,0 +1,62 @@
+; $Id: VBoxRT-win32.def $
+;; @file
+; IPRT - Win32 ASM exports.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+EXPORTS
+ ASMMultU64ByU32DivByU32
+
+ RTTimeNanoTSLegacySyncInvarNoDelta
+ RTTimeNanoTSLFenceSyncInvarNoDelta
+ RTTimeNanoTSLegacyAsyncUseApicId
+ RTTimeNanoTSLegacyAsyncUseRdtscp
+ RTTimeNanoTSLegacyAsyncUseIdtrLim
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseApicId
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseRdtscp
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseIdtrLim
+ RTTimeNanoTSLFenceAsyncUseApicId
+ RTTimeNanoTSLFenceAsyncUseRdtscp
+ RTTimeNanoTSLFenceAsyncUseIdtrLim
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim
+
+ RTStrMemFind32
+
+ ; Export aliases for noexcept affected methods.
+ ?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@_EPAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@ZPAX@Z ; before-noexcept ; (public: void __thiscall RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse *,void const *,unsigned int,unsigned int,unsigned __int64,unsigned __int64) noexcept,void *))
+ ?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@ZPAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@_EPAX@Z ; after-noexcept ; (public: void __thiscall RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse *,void const *,unsigned int,unsigned int,unsigned __int64,unsigned __int64),void *))
+ ?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@_E12@Z=?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@Z12@Z ; before-noexcept ; (public: void __thiscall RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter *,void *,unsigned int,unsigned __int64,unsigned int *) noexcept,void *,unsigned __int64))
+ ?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@Z12@Z=?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@_E12@Z ; after-noexcept ; (public: void __thiscall RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter *,void *,unsigned int,unsigned __int64,unsigned int *),void *,unsigned __int64))
+
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-win64.def b/src/VBox/Runtime/r3/win/VBoxRT-win64.def
new file mode 100644
index 00000000..e3a83833
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-win64.def
@@ -0,0 +1,74 @@
+; $Id: VBoxRT-win64.def $
+;; @file
+; IPRT - Win64 ASM exports.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+EXPORTS
+ ASMAtomicBitClear
+ ASMAtomicBitToggle
+ ASMAtomicBitTestAndToggle
+ ASMBitFirstClear
+ ASMBitFirstSet
+ ASMAtomicReadU64
+ ASMAtomicXchgU8
+ ASMAtomicXchgU16
+ ASMGetFlags
+ ASMProbeReadByte
+ ASMSetFlags
+ ASMMultU64ByU32DivByU32
+ ASMNopPause
+
+ RTTimeNanoTSLegacySyncInvarNoDelta
+ RTTimeNanoTSLFenceSyncInvarNoDelta
+ RTTimeNanoTSLegacyAsyncUseApicId
+ RTTimeNanoTSLegacyAsyncUseRdtscp
+ RTTimeNanoTSLegacyAsyncUseIdtrLim
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseApicId
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseRdtscp
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseIdtrLim
+ RTTimeNanoTSLFenceAsyncUseApicId
+ RTTimeNanoTSLFenceAsyncUseRdtscp
+ RTTimeNanoTSLFenceAsyncUseIdtrLim
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim
+
+ RTStrMemFind32
+
+ ; Export aliases for noexcept affected methods.
+ ?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@_EPEAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@ZPEAX@Z ; before-noexcept ; (public: void __cdecl RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse * __ptr64,void const * __ptr64,unsigned __int64,unsigned int,unsigned __int64,unsigned __int64) noexcept,void * __ptr64) __ptr64
+ ?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@ZPEAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@_EPEAX@Z ; after-noexcept ; (public: void __cdecl RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse * __ptr64,void const * __ptr64,unsigned __int64,unsigned int,unsigned __int64,unsigned __int64),void * __ptr64) __ptr64)
+ ?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@_E12@Z=?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@Z12@Z ; before-noexcept ; (public: void __cdecl RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter * __ptr64,void * __ptr64,unsigned __int64,unsigned __int64,unsigned __int64 * __ptr64) noexcept,void * __ptr64,unsigned __int64) __ptr64)
+ ?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@Z12@Z=?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@_E12@Z ; after-noexcept ; (public: void __cdecl RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter * __ptr64,void * __ptr64,unsigned __int64,unsigned __int64,unsigned __int64 * __ptr64),void * __ptr64,unsigned __int64) __ptr64)
+
diff --git a/src/VBox/Runtime/r3/win/alloc-win.cpp b/src/VBox/Runtime/r3/win/alloc-win.cpp
new file mode 100644
index 00000000..a9ebe7f0
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/alloc-win.cpp
@@ -0,0 +1,210 @@
+/* $Id: alloc-win.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef IPRT_NO_CRT
+# define USE_VIRTUAL_ALLOC
+#endif
+#define LOG_GROUP RTLOGGROUP_MEM
+#include <iprt/win/windows.h>
+
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/errcore.h>
+
+#ifndef USE_VIRTUAL_ALLOC
+# include <malloc.h>
+#endif
+
+
+RTDECL(void *) RTMemPageAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ RT_NOREF_PV(pszTag);
+
+#ifdef USE_VIRTUAL_ALLOC
+ void *pv = VirtualAlloc(NULL, RT_ALIGN_Z(cb, PAGE_SIZE), MEM_COMMIT, PAGE_READWRITE);
+#else
+ void *pv = _aligned_malloc(RT_ALIGN_Z(cb, PAGE_SIZE), PAGE_SIZE);
+#endif
+ AssertMsg(pv, ("cb=%d lasterr=%d\n", cb, GetLastError()));
+ return pv;
+}
+
+
+RTDECL(void *) RTMemPageAllocExTag(size_t cb, uint32_t fFlags, const char *pszTag) RT_NO_THROW_DEF
+{
+ size_t const cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ RT_NOREF_PV(pszTag);
+ AssertReturn(!(fFlags & ~RTMEMPAGEALLOC_F_VALID_MASK), NULL);
+
+#ifdef USE_VIRTUAL_ALLOC
+ void *pv = VirtualAlloc(NULL, cbAligned, MEM_COMMIT, PAGE_READWRITE);
+#else
+ void *pv = _aligned_malloc(cbAligned, PAGE_SIZE);
+#endif
+ AssertMsgReturn(pv, ("cb=%d lasterr=%d\n", cb, GetLastError()), NULL);
+
+ if (fFlags & RTMEMPAGEALLOC_F_ADVISE_LOCKED)
+ {
+ /** @todo check why we get ERROR_WORKING_SET_QUOTA here. */
+ BOOL const fOkay = VirtualLock(pv, cbAligned);
+ AssertMsg(fOkay || GetLastError() == ERROR_WORKING_SET_QUOTA, ("pv=%p cb=%d lasterr=%d\n", pv, cb, GetLastError()));
+ NOREF(fOkay);
+ }
+
+ if (fFlags & RTMEMPAGEALLOC_F_ZERO)
+ RT_BZERO(pv, cbAligned);
+
+ return pv;
+}
+
+
+RTDECL(void *) RTMemPageAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ RT_NOREF_PV(pszTag);
+
+#ifdef USE_VIRTUAL_ALLOC
+ void *pv = VirtualAlloc(NULL, RT_ALIGN_Z(cb, PAGE_SIZE), MEM_COMMIT, PAGE_READWRITE);
+#else
+ void *pv = _aligned_malloc(RT_ALIGN_Z(cb, PAGE_SIZE), PAGE_SIZE);
+#endif
+ if (pv)
+ {
+ memset(pv, 0, RT_ALIGN_Z(cb, PAGE_SIZE));
+ return pv;
+ }
+ AssertMsgFailed(("cb=%d lasterr=%d\n", cb, GetLastError()));
+ return NULL;
+}
+
+
+RTDECL(void) RTMemPageFree(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ RT_NOREF_PV(cb);
+
+ if (pv)
+ {
+#ifdef USE_VIRTUAL_ALLOC
+ if (!VirtualFree(pv, 0, MEM_RELEASE))
+ AssertMsgFailed(("pv=%p lasterr=%d\n", pv, GetLastError()));
+#else
+ _aligned_free(pv);
+#endif
+ }
+}
+
+
+RTDECL(int) RTMemProtect(void *pv, size_t cb, unsigned fProtect) RT_NO_THROW_DEF
+{
+ /*
+ * Validate input.
+ */
+ if (cb == 0)
+ {
+ AssertMsgFailed(("!cb\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (fProtect & ~(RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC))
+ {
+ AssertMsgFailed(("fProtect=%#x\n", fProtect));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Convert the flags.
+ */
+ int fProt;
+ Assert(!RTMEM_PROT_NONE);
+ switch (fProtect & (RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC))
+ {
+ case RTMEM_PROT_NONE:
+ fProt = PAGE_NOACCESS;
+ break;
+
+ case RTMEM_PROT_READ:
+ fProt = PAGE_READONLY;
+ break;
+
+ case RTMEM_PROT_READ | RTMEM_PROT_WRITE:
+ fProt = PAGE_READWRITE;
+ break;
+
+ case RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC:
+ fProt = PAGE_EXECUTE_READWRITE;
+ break;
+
+ case RTMEM_PROT_READ | RTMEM_PROT_EXEC:
+ fProt = PAGE_EXECUTE_READWRITE;
+ break;
+
+ case RTMEM_PROT_WRITE:
+ fProt = PAGE_READWRITE;
+ break;
+
+ case RTMEM_PROT_WRITE | RTMEM_PROT_EXEC:
+ fProt = PAGE_EXECUTE_READWRITE;
+ break;
+
+ case RTMEM_PROT_EXEC:
+ fProt = PAGE_EXECUTE_READWRITE;
+ break;
+
+ /* If the compiler had any brains it would warn about this case. */
+ default:
+ AssertMsgFailed(("fProtect=%#x\n", fProtect));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ /*
+ * Align the request.
+ */
+ cb += (uintptr_t)pv & PAGE_OFFSET_MASK;
+ pv = (void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK);
+
+ /*
+ * Change the page attributes.
+ */
+ DWORD fFlags = 0;
+ if (VirtualProtect(pv, cb, fProt, &fFlags))
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
diff --git a/src/VBox/Runtime/r3/win/allocex-win.cpp b/src/VBox/Runtime/r3/win/allocex-win.cpp
new file mode 100644
index 00000000..dca6d053
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/allocex-win.cpp
@@ -0,0 +1,133 @@
+/* $Id: allocex-win.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Extended Alloc Workers, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTMEM_NO_WRAP_TO_EF_APIS
+#include <iprt/mem.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/param.h>
+#include "../allocex.h"
+
+#include <iprt/win/windows.h>
+
+
+static int rtMemAllocExInRange(size_t cbAlloc, uint32_t fFlags, void **ppv, uintptr_t uAddr, uintptr_t uAddrLast)
+{
+ /*
+ * Try with every possible address hint since the possible range is very limited.
+ */
+ DWORD fPageProt = (fFlags & RTMEMALLOCEX_FLAGS_EXEC ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
+ while (uAddr <= uAddrLast)
+ {
+ MEMORY_BASIC_INFORMATION MemInfo;
+ SIZE_T cbRange = VirtualQuery((void *)uAddr, &MemInfo, sizeof(MemInfo));
+ AssertReturn(cbRange == sizeof(MemInfo), VERR_NOT_SUPPORTED);
+ Assert(MemInfo.RegionSize > 0);
+
+ if ( MemInfo.State == MEM_FREE
+ && MemInfo.RegionSize >= cbAlloc)
+ {
+ void *pv = VirtualAlloc((void *)uAddr, cbAlloc, MEM_RESERVE | MEM_COMMIT, fPageProt);
+ if ((uintptr_t)pv == uAddr)
+ {
+ *ppv = pv;
+ return VINF_SUCCESS;
+ }
+ AssertStmt(!pv, VirtualFree(pv, cbAlloc, MEM_RELEASE));
+ }
+
+ /* skip ahead */
+ uintptr_t uAddrNext = (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize;
+ if (uAddrNext <= uAddr)
+ break;
+ uAddr += uAddrNext;
+ }
+
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtMemAllocEx16BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv)
+{
+ cbAlloc = RT_ALIGN_Z(cbAlloc, PAGE_SIZE);
+ AssertReturn(cbAlloc <= _64K - PAGE_SIZE, VERR_NO_MEMORY);
+
+ /* Seems this doesn't work on W7/64... */
+ return rtMemAllocExInRange(cbAlloc, fFlags, ppv, PAGE_SIZE, _64K - cbAlloc);
+}
+
+
+DECLHIDDEN(int) rtMemAllocEx32BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv)
+{
+ cbAlloc = RT_ALIGN_Z(cbAlloc, PAGE_SIZE);
+ AssertReturn(cbAlloc <= _2G+_1G, VERR_NO_MEMORY);
+
+ /*
+ * Just try first.
+ */
+ DWORD fPageProt = (fFlags & RTMEMALLOCEX_FLAGS_EXEC ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
+ void *pv = VirtualAlloc(NULL, cbAlloc, MEM_RESERVE | MEM_COMMIT, fPageProt);
+ if (!pv)
+ return VERR_NO_MEMORY;
+ if ((uintptr_t)pv + cbAlloc - 1 < _4G)
+ {
+ *ppv = pv;
+ return VINF_SUCCESS;
+ }
+ VirtualFree(pv, cbAlloc, MEM_RELEASE);
+
+ /*
+ * No luck, do address scan based allocation.
+ */
+ return rtMemAllocExInRange(cbAlloc, fFlags, ppv, _64K, _4G - cbAlloc);
+}
+
+
+DECLHIDDEN(void) rtMemFreeExYyBitReach(void *pv, size_t cb, uint32_t fFlags)
+{
+ RT_NOREF_PV(fFlags);
+
+ BOOL fRc = VirtualFree(pv, cb, MEM_RELEASE);
+ Assert(fRc); RT_NOREF_PV(fRc);
+}
+
diff --git a/src/VBox/Runtime/r3/win/dir-win.cpp b/src/VBox/Runtime/r3/win/dir-win.cpp
new file mode 100644
index 00000000..f0a193ff
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/dir-win.cpp
@@ -0,0 +1,169 @@
+/* $Id: dir-win.cpp $ */
+/** @file
+ * IPRT - Directory, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/win/windows.h>
+
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include "internal/fs.h"
+#include "internal/path.h"
+
+
+
+RTDECL(int) RTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate)
+{
+ /*
+ * Validate the file mode.
+ */
+ int rc;
+ fMode = rtFsModeNormalize(fMode, pszPath, 0, RTFS_TYPE_DIRECTORY);
+ if (rtFsModeIsValidPermissions(fMode))
+ {
+ /*
+ * Convert to UTF-16.
+ */
+ PRTUTF16 pwszString;
+ rc = RTPathWinFromUtf8(&pwszString, pszPath, 0 /*fFlags*/);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the directory.
+ */
+ if (CreateDirectoryW((LPCWSTR)pwszString, NULL))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ /*
+ * Turn off indexing of directory through Windows Indexing Service
+ */
+ /** @todo This FILE_ATTRIBUTE_NOT_CONTENT_INDEXED hack (for .VDI files,
+ * really) may cause failures on samba shares. That really sweet and
+ * need to be addressed differently. We shouldn't be doing this
+ * unless the caller actually asks for it, must less returning failure,
+ * for crying out loud! This is only important a couple of places in
+ * main, if important is the right way to put it... */
+ if ( RT_SUCCESS(rc)
+ && !(fCreate & RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET))
+ {
+ if ( SetFileAttributesW((LPCWSTR)pwszString, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
+ || (fCreate & RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL) )
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+
+ RTPathWinFree(pwszString);
+ }
+ }
+ else
+ {
+ AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
+ rc = VERR_INVALID_FMODE;
+ }
+
+ LogFlow(("RTDirCreate(%p:{%s}, %RTfmode): returns %Rrc\n", pszPath, pszPath, fMode, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTDirRemove(const char *pszPath)
+{
+ /*
+ * Convert to UTF-16.
+ */
+ PRTUTF16 pwszString;
+ int rc = RTPathWinFromUtf8(&pwszString, pszPath, 0 /*fFlags*/);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Remove the directory.
+ */
+ if (RemoveDirectoryW((LPCWSTR)pwszString))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTPathWinFree(pwszString);
+ }
+
+ LogFlow(("RTDirRemove(%p:{%s}): returns %Rrc\n", pszPath, pszPath, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTDirFlush(const char *pszPath)
+{
+ RT_NOREF_PV(pszPath);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTDirRename(const char *pszSrc, const char *pszDst, unsigned fRename)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Call the worker.
+ */
+ int rc = rtPathWin32MoveRename(pszSrc, pszDst,
+ fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0,
+ RTFS_TYPE_DIRECTORY);
+
+ LogFlow(("RTDirRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/direnum-win.cpp b/src/VBox/Runtime/r3/win/direnum-win.cpp
new file mode 100644
index 00000000..80d36864
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/direnum-win.cpp
@@ -0,0 +1,404 @@
+/* $Id: direnum-win.cpp $ */
+/** @file
+ * IPRT - Directory Enumeration, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/win/windows.h>
+
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include "internal/fs.h"
+#include "internal/dir.h"
+
+
+size_t rtDirNativeGetStructSize(const char *pszPath)
+{
+ NOREF(pszPath);
+ return sizeof(RTDIRINTERNAL);
+}
+
+
+int rtDirNativeOpen(PRTDIRINTERNAL pDir, uintptr_t hRelativeDir, void *pvNativeRelative))
+{
+ RT_NOREF(hRelativeDir, pvNativeRelative);
+
+ /*
+ * Setup the search expression.
+ *
+ * pszPathBuf is pointing to the return 4K return buffer for the RTPathReal()
+ * call in rtDirOpenCommon(), so all we gota do is check that we don't overflow
+ * it when adding the wildcard expression.
+ */
+/** @todo the pszPathBuf argument was removed in order to support paths longer than RTPATH_MAX. Rewrite this code. */
+ size_t cbExpr;
+ const char *pszExpr;
+ if (pDir->enmFilter == RTDIRFILTER_WINNT)
+ {
+ pszExpr = pDir->pszFilter;
+ cbExpr = pDir->cchFilter + 1;
+ }
+ else
+ {
+ pszExpr = "*";
+ cbExpr = sizeof("*");
+ }
+ if (pDir->cchPath + cbExpr > RTPATH_MAX)
+ return VERR_FILENAME_TOO_LONG;
+ memcpy(pszPathBuf + pDir->cchPath, pszExpr, cbExpr);
+
+
+ /*
+ * Attempt opening the search.
+ */
+ PRTUTF16 pwszName;
+ int rc = RTPathWinFromUtf8(pwszPathBuf, &pwszName, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ pDir->hDir = FindFirstFileW((LPCWSTR)pwszName, &pDir->Data);
+ if (pDir->hDir != INVALID_HANDLE_VALUE)
+ pDir->fDataUnread = true;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ /* Theoretical case of an empty directory or more normal case of no matches. */
+ if ( dwErr == ERROR_FILE_NOT_FOUND
+ || dwErr == ERROR_NO_MORE_FILES /* ???*/)
+ pDir->fDataUnread = false;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ RTPathWinFree(pwszName);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTDirClose(PRTDIRINTERNAL pDir)
+{
+ /*
+ * Validate input.
+ */
+ if (!pDir)
+ return VERR_INVALID_PARAMETER;
+ if (pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Close the handle.
+ */
+ pDir->u32Magic++;
+ if (pDir->hDir != INVALID_HANDLE_VALUE)
+ {
+ BOOL fRc = FindClose(pDir->hDir);
+ Assert(fRc);
+ pDir->hDir = INVALID_HANDLE_VALUE;
+ }
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+ RTMemFree(pDir);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTDirRead(RTDIR hDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
+{
+ PPRTDIRINTERNAL pDir = hDir;
+
+ /*
+ * Validate input.
+ */
+ if (!pDir || pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!pDirEntry)
+ {
+ AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry));
+ return VERR_INVALID_PARAMETER;
+ }
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ cbDirEntry = *pcbDirEntry;
+ if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRY, szName[2]))
+ {
+ AssertMsgFailed(("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRY, szName[2])));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+
+ BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data);
+ if (!fRc)
+ {
+ int iErr = GetLastError();
+ if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES)
+ return VERR_NO_MORE_FILES;
+ return RTErrConvertFromWin32(iErr);
+ }
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ if (!pDir->pszName)
+ {
+ int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName);
+ if (RT_FAILURE(rc))
+ {
+ pDir->pszName = NULL;
+ return rc;
+ }
+ pDir->cchName = strlen(pDir->pszName);
+ }
+
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRY, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired > cbDirEntry)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Setup the returned data.
+ */
+ pDir->fDataUnread = false;
+ pDirEntry->INodeId = 0; /** @todo we can use the fileid here if we must (see GetFileInformationByHandle). */
+ pDirEntry->enmType = pDir->Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
+ ? RTDIRENTRYTYPE_DIRECTORY : RTDIRENTRYTYPE_FILE;
+ pDirEntry->cbName = (uint16_t)cchName;
+ Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ PPRTDIRINTERNAL pDir = hDir;
+ /** @todo Symlinks: Find[First|Next]FileW will return info about
+ the link, so RTPATH_F_FOLLOW_LINK is not handled correctly. */
+ /*
+ * Validate input.
+ */
+ if (!pDir || pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!pDirEntry)
+ {
+ AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING
+ || enmAdditionalAttribs > RTFSOBJATTRADD_LAST)
+ {
+ AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs));
+ return VERR_INVALID_PARAMETER;
+ }
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ cbDirEntry = *pcbDirEntry;
+ if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName[2]))
+ {
+ AssertMsgFailed(("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+
+ BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data);
+ if (!fRc)
+ {
+ int iErr = GetLastError();
+ if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES)
+ return VERR_NO_MORE_FILES;
+ return RTErrConvertFromWin32(iErr);
+ }
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ if (!pDir->pszName)
+ {
+ int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName);
+ if (RT_FAILURE(rc))
+ {
+ pDir->pszName = NULL;
+ return rc;
+ }
+ pDir->cchName = strlen(pDir->pszName);
+ }
+
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired > cbDirEntry)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Setup the returned data.
+ */
+ pDir->fDataUnread = false;
+ pDirEntry->cbName = (uint16_t)cchName;
+ Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+ if (pDir->Data.cAlternateFileName[0])
+ {
+ /* copy and calc length */
+ PCRTUTF16 pwszSrc = (PCRTUTF16)pDir->Data.cAlternateFileName;
+ PRTUTF16 pwszDst = pDirEntry->wszShortName;
+ uint32_t off = 0;
+ while (off < RT_ELEMENTS(pDirEntry->wszShortName) - 1U && pwszSrc[off])
+ {
+ pwszDst[off] = pwszSrc[off];
+ off++;
+ }
+ pDirEntry->cwcShortName = (uint16_t)off;
+
+ /* zero the rest */
+ do
+ pwszDst[off++] = '\0';
+ while (off < RT_ELEMENTS(pDirEntry->wszShortName));
+ }
+ else
+ {
+ memset(pDirEntry->wszShortName, 0, sizeof(pDirEntry->wszShortName));
+ pDirEntry->cwcShortName = 0;
+ }
+
+ pDirEntry->Info.cbObject = ((uint64_t)pDir->Data.nFileSizeHigh << 32)
+ | (uint64_t)pDir->Data.nFileSizeLow;
+ pDirEntry->Info.cbAllocated = pDirEntry->Info.cbObject;
+
+ Assert(sizeof(uint64_t) == sizeof(pDir->Data.ftCreationTime));
+ RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, *(uint64_t *)&pDir->Data.ftCreationTime);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, *(uint64_t *)&pDir->Data.ftLastAccessTime);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, *(uint64_t *)&pDir->Data.ftLastWriteTime);
+ pDirEntry->Info.ChangeTime = pDirEntry->Info.ModificationTime;
+
+ pDirEntry->Info.Attr.fMode = rtFsModeFromDos((pDir->Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszName, cchName, pDir->Data.dwReserved0, 0);
+
+ /*
+ * Requested attributes (we cannot provide anything actually).
+ */
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_EASIZE:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pDirEntry->Info.Attr.u.EASize.cb = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pDirEntry->Info.Attr.u.Unix.uid = ~0U;
+ pDirEntry->Info.Attr.u.Unix.gid = ~0U;
+ pDirEntry->Info.Attr.u.Unix.cHardlinks = 1;
+ pDirEntry->Info.Attr.u.Unix.INodeIdDevice = 0; /** @todo Use the volume serial number (see GetFileInformationByHandle). */
+ pDirEntry->Info.Attr.u.Unix.INodeId = 0; /** @todo Use the fileid (see GetFileInformationByHandle). */
+ pDirEntry->Info.Attr.u.Unix.fFlags = 0;
+ pDirEntry->Info.Attr.u.Unix.GenerationId = 0;
+ pDirEntry->Info.Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_NOTHING:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pDirEntry->Info.Attr.u.UnixOwner.uid = ~0U;
+ pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pDirEntry->Info.Attr.u.UnixGroup.gid = ~0U;
+ pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/win/dllmain-win.cpp b/src/VBox/Runtime/r3/win/dllmain-win.cpp
new file mode 100644
index 00000000..b52d059b
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/dllmain-win.cpp
@@ -0,0 +1,100 @@
+/* $Id: dllmain-win.cpp $ */
+/** @file
+ * IPRT - Win32 DllMain (Ring-3).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/thread.h>
+#include <iprt/param.h>
+#include "internal/thread.h"
+
+
+
+/**
+ * Increases the load count on the IPRT DLL so it won't unload.
+ *
+ * This is a separate function so as to not overflow the stack of threads with
+ * very little of it.
+ *
+ * @param hModule The IPRT DLL module handle.
+ */
+DECL_NO_INLINE(static, void) EnsureNoUnload(HMODULE hModule)
+{
+ WCHAR wszName[RTPATH_MAX];
+ SetLastError(NO_ERROR);
+ if ( GetModuleFileNameW(hModule, wszName, RT_ELEMENTS(wszName)) > 0
+ && GetLastError() == NO_ERROR)
+ {
+ int cExtraLoads = 32;
+ while (cExtraLoads-- > 0)
+ LoadLibraryW(wszName);
+ }
+}
+
+
+/**
+ * The Dll main entry point.
+ */
+BOOL __stdcall DllMain(HANDLE hModule, DWORD dwReason, PVOID pvReserved)
+{
+ RT_NOREF_PV(pvReserved);
+
+ switch (dwReason)
+ {
+ /*
+ * When attaching to a process, we'd like to make sure IPRT stays put
+ * and doesn't get unloaded.
+ */
+ case DLL_PROCESS_ATTACH:
+ EnsureNoUnload((HMODULE)hModule);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ case DLL_THREAD_ATTACH:
+ default:
+ /* ignore */
+ break;
+
+ case DLL_THREAD_DETACH:
+ rtThreadWinTlsDestruction();
+ rtThreadNativeDetach();
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/src/VBox/Runtime/r3/win/env-win.cpp b/src/VBox/Runtime/r3/win/env-win.cpp
new file mode 100644
index 00000000..89577098
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/env-win.cpp
@@ -0,0 +1,520 @@
+/* $Id: env-win.cpp $ */
+/** @file
+ * IPRT - Environment, Posix.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/env.h>
+
+#ifdef IPRT_NO_CRT
+# include <iprt/asm.h>
+#endif
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include <iprt/utf16.h>
+
+#ifndef IPRT_NO_CRT
+# include <stdlib.h>
+# include <errno.h>
+#endif
+#include <iprt/win/windows.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef IPRT_NO_CRT
+static uint32_t volatile g_idxGetEnvBufs = 0;
+static char *g_apszGetEnvBufs[64]; /* leak */
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue);
+
+
+#if defined(RT_ARCH_X86) && defined(RT_OS_WINDOWS)
+/**
+ * This is a workaround for NT 3.x not setting the last error.
+ */
+static DWORD rtEnvNt31CheckEmpty(PCRTUTF16 pwszVar)
+{
+ /* Check the version first: */
+ DWORD dwVersion = GetVersion();
+ if (RT_BYTE1(dwVersion) != 3)
+ return 0;
+
+ /* When called with an empty buffer, we should get 1 if empty value
+ and 0 if not found: */
+ DWORD cwcNeeded = GetEnvironmentVariableW(pwszVar, NULL, 0);
+ return cwcNeeded == 0 ? ERROR_ENVVAR_NOT_FOUND : NO_ERROR;
+}
+#endif
+
+
+RTDECL(bool) RTEnvExistsBad(const char *pszVar)
+{
+#ifndef IPRT_NO_CRT
+ return RTEnvGetBad(pszVar) != NULL;
+#else
+ return RTEnvExistsUtf8(pszVar);
+#endif
+}
+
+
+RTDECL(bool) RTEnvExist(const char *pszVar)
+{
+#ifndef IPRT_NO_CRT
+ return RTEnvExistsBad(pszVar);
+#else
+ return RTEnvExistsUtf8(pszVar);
+#endif
+}
+
+
+RTDECL(bool) RTEnvExistsUtf8(const char *pszVar)
+{
+ AssertReturn(strchr(pszVar, '=') == NULL, false);
+
+ PRTUTF16 pwszVar;
+ int rc = RTStrToUtf16(pszVar, &pwszVar);
+ AssertRCReturn(rc, false);
+
+#ifndef IPRT_NO_CRT
+ bool fRet = _wgetenv(pwszVar) != NULL;
+#else
+ DWORD dwRet = GetEnvironmentVariableW(pwszVar, NULL, 0);
+ bool fRet = dwRet != 0;
+#endif
+
+ RTUtf16Free(pwszVar);
+ return fRet;
+}
+
+
+RTDECL(const char *) RTEnvGetBad(const char *pszVar)
+{
+ AssertReturn(strchr(pszVar, '=') == NULL, NULL);
+#ifndef IPRT_NO_CRT
+ return getenv(pszVar);
+#else
+ /*
+ * Query the value into heap buffer which we give a lifetime of 64
+ * RTEnvGetBad calls.
+ */
+ char *pszValue = RTEnvDup(pszVar);
+ if (pszValue)
+ {
+ RTMEM_MAY_LEAK(pszValue); /* Quite possible we'll leak this, but the leak is limited to 64 values. */
+
+ uint32_t idx = ASMAtomicIncU32(&g_idxGetEnvBufs) % RT_ELEMENTS(g_apszGetEnvBufs);
+ char *pszOld = (char *)ASMAtomicXchgPtr((void * volatile *)&g_apszGetEnvBufs[idx], pszValue);
+ RTStrFree(pszOld);
+ }
+ return pszValue;
+#endif
+}
+
+
+RTDECL(const char *) RTEnvGet(const char *pszVar)
+{
+ return RTEnvGetBad(pszVar);
+}
+
+RTDECL(int) RTEnvGetUtf8(const char *pszVar, char *pszValue, size_t cbValue, size_t *pcchActual)
+{
+ AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pszValue, VERR_INVALID_POINTER);
+ AssertReturn(pszValue || !cbValue, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pcchActual, VERR_INVALID_POINTER);
+ AssertReturn(pcchActual || (pszValue && cbValue), VERR_INVALID_PARAMETER);
+ AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+
+ if (pcchActual)
+ *pcchActual = 0;
+
+ /*
+ * Convert the name to UTF-16.
+ */
+ PRTUTF16 pwszVar;
+ int rc = RTStrToUtf16(pszVar, &pwszVar);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Query the variable. First try with a medium sized stack buffer (too
+ * small for your typical PATH, but large enough for most other things).
+ */
+ RTUTF16 wszValue[512];
+ uint32_t cwcValueBuf = RT_ELEMENTS(wszValue);
+ PRTUTF16 pwszValue = wszValue;
+ PRTUTF16 pwszValueFree = NULL;
+
+ for (unsigned iTry = 0;; iTry++)
+ {
+ /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW nor ERROR_ENVVAR_NOT_FOUND.
+ Note! Assume that the CRT transparently updates the process
+ environment and that we don't need to use _wgetenv_s here. */
+ SetLastError(NO_ERROR);
+ DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf);
+ DWORD dwErr = GetLastError();
+
+ if (cwcValueRet < cwcValueBuf)
+ {
+#ifdef RT_ARCH_X86
+ if (cwcValueRet == 0 && dwErr == NO_ERROR)
+ dwErr = rtEnvNt31CheckEmpty(pwszVar);
+#endif
+ if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */
+ {
+ if (cbValue)
+ rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszValue, cbValue, pcchActual);
+ else
+ rc = RTUtf16CalcUtf8LenEx(pwszValue, cwcValueRet, pcchActual);
+ }
+ else
+ {
+ Assert(cwcValueRet == 0);
+ Assert(dwErr != NO_ERROR);
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ break;
+ }
+
+ /*
+ * Insufficient buffer, so increase it. The first re-try will use the
+ * returned size, further re-tries will max out with a multiple of the
+ * stack buffer till we reaches 32KB chars (128 loops).
+ */
+ Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW);
+ RTMemTmpFree(pwszValueFree);
+ AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */);
+
+ cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry);
+ pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16));
+ AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY);
+ }
+
+ RTMemTmpFree(pwszValueFree);
+ RTUtf16Free(pwszVar);
+ return rc;
+}
+
+
+RTDECL(char *) RTEnvDup(const char *pszVar)
+{
+ AssertPtrReturn(pszVar, NULL);
+
+ /*
+ * Convert the name to UTF-16.
+ */
+ PRTUTF16 pwszVar;
+ int rc = RTStrToUtf16(pszVar, &pwszVar);
+ AssertRCReturn(rc, NULL);
+
+ /*
+ * Query the variable. First try with a medium sized stack buffer (too
+ * small for your typical PATH, but large enough for most other things).
+ */
+ char *pszRet = NULL;
+ RTUTF16 wszValue[512];
+ uint32_t cwcValueBuf = RT_ELEMENTS(wszValue);
+ PRTUTF16 pwszValue = wszValue;
+ PRTUTF16 pwszValueFree = NULL;
+
+ for (unsigned iTry = 0;; iTry++)
+ {
+ /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW nor ERROR_ENVVAR_NOT_FOUND.
+ Note! Assume that the CRT transparently updates the process
+ environment and that we don't need to use _wgetenv_s here. */
+ SetLastError(NO_ERROR);
+ DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf);
+ DWORD dwErr = GetLastError();
+
+ if (cwcValueRet < cwcValueBuf)
+ {
+#ifdef RT_ARCH_X86
+ if (cwcValueRet == 0 && dwErr == NO_ERROR)
+ dwErr = rtEnvNt31CheckEmpty(pwszVar);
+#endif
+ if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */
+ {
+ rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszRet, 0, NULL);
+ if (RT_FAILURE(rc))
+ pszRet = NULL;
+ }
+ else
+ {
+ Assert(cwcValueRet == 0);
+ Assert(dwErr != NO_ERROR);
+ }
+ break;
+ }
+
+ /*
+ * Insufficient buffer, so increase it. The first re-try will use the
+ * returned size, further re-tries will max out with a multiple of the
+ * stack buffer till we reaches 32KB chars (128 loops).
+ */
+ Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW);
+ RTMemTmpFree(pwszValueFree);
+ AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */);
+
+ cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry);
+ pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16));
+ AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY);
+ }
+
+ RTMemTmpFree(pwszValueFree);
+ RTUtf16Free(pwszVar);
+ return pszRet;
+}
+
+
+RTDECL(int) RTEnvPutBad(const char *pszVarEqualValue)
+{
+#ifndef IPRT_NO_CRT
+ /** @todo putenv is a source memory leaks. deal with this on a per system basis. */
+ if (!putenv((char *)pszVarEqualValue))
+ return 0;
+ return RTErrConvertFromErrno(errno);
+#else
+ return RTEnvPutUtf8(pszVarEqualValue);
+#endif
+}
+
+
+RTDECL(int) RTEnvPut(const char *pszVarEqualValue)
+{
+#ifndef IPRT_NO_CRT
+ return RTEnvPutBad(pszVarEqualValue);
+#else
+ return RTEnvPutUtf8(pszVarEqualValue);
+#endif
+}
+
+
+RTDECL(int) RTEnvPutUtf8(const char *pszVarEqualValue)
+{
+ PRTUTF16 pwszVarEqualValue;
+ int rc = RTStrToUtf16(pszVarEqualValue, &pwszVarEqualValue);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef IPRT_NO_CRT
+ if (!_wputenv(pwszVarEqualValue))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(errno);
+#else
+ PRTUTF16 pwszValue = RTUtf16Chr(pwszVarEqualValue, '=');
+ if (pwszValue)
+ {
+ *pwszValue++ = '\0';
+
+ SetLastError(*pwszValue ? ERROR_OUTOFMEMORY : ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */
+ if (SetEnvironmentVariableW(pwszVarEqualValue, *pwszValue ? pwszValue : NULL))
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_ENVVAR_NOT_FOUND)
+ {
+ Assert(!*pwszValue);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Assert(*pwszValue);
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+#endif
+ RTUtf16Free(pwszVarEqualValue);
+ }
+ return rc;
+}
+
+
+
+RTDECL(int) RTEnvSetBad(const char *pszVar, const char *pszValue)
+{
+#ifndef IPRT_NO_CRT
+ AssertMsgReturn(strchr(pszVar, '=') == NULL, ("'%s'\n", pszVar), VERR_ENV_INVALID_VAR_NAME);
+ int rc;
+ if (!RTEnvExist(pszVar))
+ rc = VINF_ENV_VAR_NOT_FOUND;
+ else
+ {
+ errno_t rcErrno = _putenv_s(pszVar, *pszValue ? pszValue : " " /* wrong, but will be treated as unset otherwise */);
+ if (rcErrno == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(rcErrno);
+ }
+ return rc;
+#else
+ return RTEnvSetUtf8(pszVar, pszValue);
+#endif
+}
+
+
+RTDECL(int) RTEnvSet(const char *pszVar, const char *pszValue)
+{
+#ifndef IPRT_NO_CRT
+ return RTEnvSetBad(pszVar, pszValue);
+#else
+ return RTEnvSetUtf8(pszVar, pszValue);
+#endif
+}
+
+
+/**
+ * Worker common to RTEnvSetUtf8() and rtEnvSetExWorker().
+ */
+int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue)
+{
+ PRTUTF16 pwszVar;
+ int rc = RTStrToUtf16Ex(pchVar, cchVar, &pwszVar, 0, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszValue;
+ rc = RTStrToUtf16(pszValue, &pwszValue);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef IPRT_NO_CRT
+ errno_t rcErrno = _wputenv_s(pwszVar,
+ *pwszValue ? pwszValue : L" " /* wrong, but will be treated as unset otherwise */);
+ if (rcErrno == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(rcErrno);
+#else
+ SetLastError(ERROR_OUTOFMEMORY); /* The API did not always set the last error. */
+ if (SetEnvironmentVariableW(pwszVar, pwszValue))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+#endif
+ RTUtf16Free(pwszValue);
+ }
+ RTUtf16Free(pwszVar);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTEnvSetUtf8(const char *pszVar, const char *pszValue)
+{
+ size_t cchVar = strlen(pszVar);
+ AssertReturn(memchr(pszVar, '=', cchVar) == NULL, VERR_ENV_INVALID_VAR_NAME);
+ return rtEnvSetUtf8Worker(pszVar, cchVar, pszValue);
+}
+
+
+RTDECL(int) RTEnvUnsetBad(const char *pszVar)
+{
+#ifndef IPRT_NO_CRT
+ AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+ int rc;
+ if (!RTEnvExist(pszVar))
+ rc = VINF_ENV_VAR_NOT_FOUND;
+ else
+ {
+ errno_t rcErrno = _putenv_s(pszVar, NULL);
+ if (rcErrno == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(rcErrno);
+ }
+ return rc;
+#else
+ return RTEnvUnsetUtf8(pszVar);
+#endif
+}
+
+
+RTDECL(int) RTEnvUnset(const char *pszVar)
+{
+#ifndef IPRT_NO_CRT
+ return RTEnvUnsetBad(pszVar);
+#else
+ return RTEnvUnsetUtf8(pszVar);
+#endif
+}
+
+
+RTDECL(int) RTEnvUnsetUtf8(const char *pszVar)
+{
+ AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+
+ PRTUTF16 pwszVar;
+ int rc = RTStrToUtf16(pszVar, &pwszVar);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef IPRT_NO_CRT
+ if (_wgetenv(pwszVar))
+ {
+ errno_t rcErrno = _wputenv_s(pwszVar, NULL);
+ if (rcErrno == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(rcErrno);
+ }
+ else
+ rc = VINF_ENV_VAR_NOT_FOUND;
+#else
+ SetLastError(ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */
+ if (SetEnvironmentVariableW(pwszVar, NULL))
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = dwErr == ERROR_ENVVAR_NOT_FOUND ? VINF_ENV_VAR_NOT_FOUND : RTErrConvertFromWin32(dwErr);
+ }
+#endif
+ RTUtf16Free(pwszVar);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/errvars-win.cpp b/src/VBox/Runtime/r3/win/errvars-win.cpp
new file mode 100644
index 00000000..6d8b4814
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/errvars-win.cpp
@@ -0,0 +1,108 @@
+/* $Id: errvars-win.cpp $ */
+/** @file
+ * IPRT - Save and Restore Error Variables, Windows Ring-3.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/winsock2.h>
+#ifndef IPRT_NO_CRT
+# include <errno.h>
+#endif
+
+#include <iprt/errcore.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include "internal/magics.h"
+#include "internal-r3-win.h"
+
+
+
+RTDECL(PRTERRVARS) RTErrVarsSave(PRTERRVARS pVars)
+{
+ pVars->ai32Vars[0] = RTERRVARS_MAGIC;
+ pVars->ai32Vars[1] = GetLastError();
+ pVars->ai32Vars[2] = g_pfnWSAGetLastError ? g_pfnWSAGetLastError() : WSANOTINITIALISED;
+#ifndef IPRT_NO_CRT
+ pVars->ai32Vars[3] = errno;
+#endif
+ return pVars;
+}
+
+
+RTDECL(void) RTErrVarsRestore(PCRTERRVARS pVars)
+{
+ AssertReturnVoid(pVars->ai32Vars[0] == RTERRVARS_MAGIC);
+#ifndef IPRT_NO_CRT
+ errno = pVars->ai32Vars[3];
+#endif
+ if ( pVars->ai32Vars[2] != WSANOTINITIALISED
+ && g_pfnWSASetLastError)
+ g_pfnWSASetLastError(pVars->ai32Vars[2]);
+ SetLastError(pVars->ai32Vars[1]);
+}
+
+
+RTDECL(bool) RTErrVarsAreEqual(PCRTERRVARS pVars1, PCRTERRVARS pVars2)
+{
+ Assert(pVars1->ai32Vars[0] == RTERRVARS_MAGIC);
+ Assert(pVars2->ai32Vars[0] == RTERRVARS_MAGIC);
+
+ return pVars1->ai32Vars[0] == pVars2->ai32Vars[0]
+ && pVars1->ai32Vars[1] == pVars2->ai32Vars[1]
+ && pVars1->ai32Vars[2] == pVars2->ai32Vars[2]
+#ifndef IPRT_NO_CRT
+ && pVars1->ai32Vars[3] == pVars2->ai32Vars[3]
+#endif
+ ;
+}
+
+
+RTDECL(bool) RTErrVarsHaveChanged(PCRTERRVARS pVars)
+{
+ Assert(pVars->ai32Vars[0] == RTERRVARS_MAGIC);
+
+ return pVars->ai32Vars[0] != RTERRVARS_MAGIC
+ || (uint32_t)pVars->ai32Vars[1] != GetLastError()
+ || pVars->ai32Vars[2] != (g_pfnWSAGetLastError ? g_pfnWSAGetLastError() : WSANOTINITIALISED)
+#ifndef IPRT_NO_CRT
+ || pVars->ai32Vars[3] != errno
+#endif
+ ;
+
+}
+
diff --git a/src/VBox/Runtime/r3/win/fileaio-win.cpp b/src/VBox/Runtime/r3/win/fileaio-win.cpp
new file mode 100644
index 00000000..a3fb934d
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/fileaio-win.cpp
@@ -0,0 +1,554 @@
+/* $Id: fileaio-win.cpp $ */
+/** @file
+ * IPRT - File async I/O, native implementation for the Windows host platform.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+
+#include <iprt/asm.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/fileaio.h"
+
+#include <iprt/win/windows.h>
+
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Transfer direction.
+ */
+typedef enum TRANSFERDIRECTION
+{
+ TRANSFERDIRECTION_INVALID = 0,
+ /** Read. */
+ TRANSFERDIRECTION_READ,
+ /** Write. */
+ TRANSFERDIRECTION_WRITE,
+ /** The usual 32-bit hack. */
+ TRANSFERDIRECTION_32BIT_HACK = 0x7fffffff
+} TRANSFERDIRECTION;
+
+/**
+ * Async I/O completion context state.
+ */
+typedef struct RTFILEAIOCTXINTERNAL
+{
+ /** handle to I/O completion port. */
+ HANDLE hIoCompletionPort;
+ /** Current number of requests pending. */
+ volatile int32_t cRequests;
+ /** Flag whether the thread was woken up. */
+ volatile bool fWokenUp;
+ /** Flag whether the thread is currently waiting. */
+ volatile bool fWaiting;
+ /** Flags given during creation. */
+ uint32_t fFlags;
+ /** Magic value (RTFILEAIOCTX_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOCTXINTERNAL;
+/** Pointer to an internal context structure. */
+typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
+
+/**
+ * Async I/O request state.
+ */
+typedef struct RTFILEAIOREQINTERNAL
+{
+ /** Overlapped structure. */
+ OVERLAPPED Overlapped;
+ /** Current state the request is in. */
+ RTFILEAIOREQSTATE enmState;
+ /** The file handle. */
+ HANDLE hFile;
+ /** Kind of transfer Read/Write. */
+ TRANSFERDIRECTION enmTransferDirection;
+ /** Number of bytes to transfer. */
+ size_t cbTransfer;
+ /** Pointer to the buffer. */
+ void *pvBuf;
+ /** Opaque user data. */
+ void *pvUser;
+ /** Flag whether the request completed. */
+ bool fCompleted;
+ /** Number of bytes transferred successfully. */
+ size_t cbTransfered;
+ /** Error code of the completed request. */
+ int Rc;
+ /** Completion context we are assigned to. */
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ /** Magic value (RTFILEAIOREQ_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOREQINTERNAL;
+/** Pointer to an internal request structure. */
+typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Id for the wakeup event. */
+#define AIO_CONTEXT_WAKEUP_EVENT 1
+/** Converts a pointer to an OVERLAPPED structure to a internal request. */
+#define OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped) ( (PRTFILEAIOREQINTERNAL)((uintptr_t)(pOverlapped) - RT_UOFFSETOF(RTFILEAIOREQINTERNAL, Overlapped)) )
+
+RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
+{
+ AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
+
+ /* No limits known. */
+ pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS;
+ pAioLimits->cbBufferAlignment = 0;
+
+ return VINF_SUCCESS;
+}
+
+RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
+{
+ AssertPtrReturn(phReq, VERR_INVALID_POINTER);
+
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL));
+ if (RT_UNLIKELY(!pReqInt))
+ return VERR_NO_MEMORY;
+
+ pReqInt->pCtxInt = NULL;
+ pReqInt->fCompleted = false;
+ pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ *phReq = (RTFILEAIOREQ)pReqInt;
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
+{
+ /*
+ * Validate the handle and ignore nil.
+ */
+ if (hReq == NIL_RTFILEAIOREQ)
+ return VINF_SUCCESS;
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+
+ /*
+ * Trash the magic and free it.
+ */
+ ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
+ RTMemFree(pReqInt);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Worker setting up the request.
+ */
+DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
+ TRANSFERDIRECTION enmTransferDirection,
+ RTFOFF off, void *pvBuf, size_t cbTransfer,
+ void *pvUser)
+{
+ /*
+ * Validate the input.
+ */
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ Assert(hFile != NIL_RTFILE);
+ AssertPtr(pvBuf);
+ Assert(off >= 0);
+ Assert(cbTransfer > 0);
+
+ pReqInt->enmTransferDirection = enmTransferDirection;
+ pReqInt->hFile = (HANDLE)RTFileToNative(hFile);
+ pReqInt->Overlapped.Offset = (DWORD)(off & 0xffffffff);
+ pReqInt->Overlapped.OffsetHigh = (DWORD)(off >> 32);
+ pReqInt->cbTransfer = cbTransfer;
+ pReqInt->pvBuf = pvBuf;
+ pReqInt->pvUser = pvUser;
+ pReqInt->fCompleted = false;
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void *pvBuf, size_t cbRead, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_READ,
+ off, pvBuf, cbRead, pvUser);
+}
+
+RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void const *pvBuf, size_t cbWrite, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_WRITE,
+ off, (void *)pvBuf, cbWrite, pvUser);
+}
+
+RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_HANDLE);
+ RT_NOREF_PV(pvUser);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
+
+ return pReqInt->pvUser;
+}
+
+RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ /**
+ * @todo r=aeichner It is not possible to cancel specific
+ * requests on Windows before Vista.
+ * CancelIo cancels all requests for a file issued by the
+ * calling thread and CancelIoEx which does what we need
+ * is only available from Vista and up.
+ * The solution is to return VERR_FILE_AIO_IN_PROGRESS
+ * if the request didn't completed yet (checked above).
+ * Shouldn't be a big issue because a request is normally
+ * only canceled if it exceeds a timeout which is quite huge.
+ */
+ return VERR_FILE_AIO_COMPLETED;
+}
+
+RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ rc = pReqInt->Rc;
+ if (pcbTransfered && RT_SUCCESS(rc))
+ *pcbTransfered = pReqInt->cbTransfered;
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax, uint32_t fFlags)
+{
+ AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+ RT_NOREF_PV(cAioReqsMax);
+
+ if ( g_pfnCreateIoCompletionPort
+ && g_pfnGetQueuedCompletionStatus
+ && g_pfnPostQueuedCompletionStatus)
+ {
+ PRTFILEAIOCTXINTERNAL pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
+ if (RT_UNLIKELY(!pCtxInt))
+ return VERR_NO_MEMORY;
+
+ pCtxInt->hIoCompletionPort = g_pfnCreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
+ if (RT_UNLIKELY(!pCtxInt->hIoCompletionPort))
+ {
+ RTMemFree(pCtxInt);
+ return VERR_NO_MEMORY;
+ }
+
+ pCtxInt->fFlags = fFlags;
+ pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
+
+ *phAioCtx = (RTFILEAIOCTX)pCtxInt;
+
+ return VINF_SUCCESS;
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
+{
+ /* Validate the handle and ignore nil. */
+ if (hAioCtx == NIL_RTFILEAIOCTX)
+ return VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ /* Cannot destroy a busy context. */
+ if (RT_UNLIKELY(pCtxInt->cRequests))
+ return VERR_FILE_AIO_BUSY;
+
+ CloseHandle(pCtxInt->hIoCompletionPort);
+ ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
+ RTMemFree(pCtxInt);
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
+{
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ if ( g_pfnCreateIoCompletionPort
+ && g_pfnGetQueuedCompletionStatus
+ && g_pfnPostQueuedCompletionStatus)
+ {
+ int rc = VINF_SUCCESS;
+ HANDLE hTemp = g_pfnCreateIoCompletionPort((HANDLE)RTFileToNative(hFile), pCtxInt->hIoCompletionPort, 0, 1);
+ if (hTemp != pCtxInt->hIoCompletionPort)
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
+{
+ RT_NOREF_PV(hAioCtx);
+ return RTFILEAIO_UNLIMITED_REQS;
+}
+
+RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
+{
+ /*
+ * Parameter validation.
+ */
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
+ Assert(cReqs <= INT32_MAX);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ size_t i;
+
+ for (i = 0; i < cReqs; i++)
+ {
+ PRTFILEAIOREQINTERNAL pReqInt = pahReqs[i];
+ BOOL fSucceeded;
+
+ Assert(pReqInt->cbTransfer == (DWORD)pReqInt->cbTransfer);
+ if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_READ)
+ {
+ fSucceeded = ReadFile(pReqInt->hFile, pReqInt->pvBuf,
+ (DWORD)pReqInt->cbTransfer, NULL,
+ &pReqInt->Overlapped);
+ }
+ else if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_WRITE)
+ {
+ fSucceeded = WriteFile(pReqInt->hFile, pReqInt->pvBuf,
+ (DWORD)pReqInt->cbTransfer, NULL,
+ &pReqInt->Overlapped);
+ }
+ else
+ {
+ fSucceeded = false;
+ AssertMsgFailed(("Invalid transfer direction\n"));
+ }
+
+ if (RT_UNLIKELY(!fSucceeded && GetLastError() != ERROR_IO_PENDING))
+ {
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ rc = RTErrConvertFromWin32(GetLastError());
+ pReqInt->Rc = rc;
+ break;
+ }
+ RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
+ }
+
+ ASMAtomicAddS32(&pCtxInt->cRequests, (int32_t)i);
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
+ PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
+{
+ /*
+ * Validate the parameters, making sure to always set pcReqs.
+ */
+ AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
+ *pcReqs = 0; /* always set */
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
+
+ /*
+ * Can't wait if there are no requests around.
+ */
+ if ( RT_UNLIKELY(ASMAtomicUoReadS32(&pCtxInt->cRequests) == 0)
+ && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
+ return VERR_FILE_AIO_NO_REQUEST;
+
+ /* Wait for at least one. */
+ if (!cMinReqs)
+ cMinReqs = 1;
+
+ /*
+ * Loop until we're woken up, hit an error (incl timeout), or
+ * have collected the desired number of requests.
+ */
+ int rc = VINF_SUCCESS;
+ int cRequestsCompleted = 0;
+ while ( !pCtxInt->fWokenUp
+ && cMinReqs > 0)
+ {
+ uint64_t StartNanoTS = 0;
+ DWORD dwTimeout = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies;
+ DWORD cbTransfered;
+ LPOVERLAPPED pOverlapped;
+ ULONG_PTR lCompletionKey;
+ BOOL fSucceeded;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ StartNanoTS = RTTimeNanoTS();
+
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, true);
+ fSucceeded = g_pfnGetQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
+ &cbTransfered,
+ &lCompletionKey,
+ &pOverlapped,
+ dwTimeout);
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, false);
+ if ( !fSucceeded
+ && !pOverlapped)
+ {
+ /* The call failed to dequeue a completion packet, includes VERR_TIMEOUT */
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+ }
+
+ /* Check if we got woken up. */
+ if (lCompletionKey == AIO_CONTEXT_WAKEUP_EVENT)
+ {
+ Assert(fSucceeded && !pOverlapped);
+ break;
+ }
+
+ /* A request completed. */
+ PRTFILEAIOREQINTERNAL pReqInt = OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped);
+ AssertPtr(pReqInt);
+ Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
+
+ /* Mark the request as finished. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ pReqInt->cbTransfered = cbTransfered;
+ if (fSucceeded)
+ pReqInt->Rc = VINF_SUCCESS;
+ else
+ {
+ DWORD errCode = GetLastError();
+ pReqInt->Rc = RTErrConvertFromWin32(errCode);
+ if (pReqInt->Rc == VERR_UNRESOLVED_ERROR)
+ LogRel(("AIO/win: Request %#p returned rc=%Rrc (native %u\n)", pReqInt, pReqInt->Rc, errCode));
+ }
+
+ pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt;
+
+ /* Update counter. */
+ cMinReqs--;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ /* Recalculate timeout. */
+ uint64_t NanoTS = RTTimeNanoTS();
+ uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
+ if (cMilliesElapsed < cMillies)
+ cMillies -= cMilliesElapsed;
+ else
+ cMillies = 0;
+ }
+ }
+
+ /*
+ * Update the context state and set the return value.
+ */
+ *pcReqs = cRequestsCompleted;
+ ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
+
+ /*
+ * Clear the wakeup flag and set rc.
+ */
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, false);
+
+ if ( fWokenUp
+ && RT_SUCCESS(rc))
+ rc = VERR_INTERRUPTED;
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
+ bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
+
+ if ( !fWokenUp
+ && fWaiting)
+ {
+ BOOL fSucceeded = g_pfnPostQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
+ 0, AIO_CONTEXT_WAKEUP_EVENT,
+ NULL);
+
+ if (!fSucceeded)
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/fileio-win.cpp b/src/VBox/Runtime/r3/win/fileio-win.cpp
new file mode 100644
index 00000000..3feba3a2
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/fileio-win.cpp
@@ -0,0 +1,1549 @@
+/* $Id: fileio-win.cpp $ */
+/** @file
+ * IPRT - File I/O, native implementation for the Windows host platform.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0500
+#endif
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/file.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/utf16.h>
+#include "internal/file.h"
+#include "internal/fs.h"
+#include "internal/path.h"
+#include "internal-r3-win.h" /* For g_enmWinVer + kRTWinOSType_XXX */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+typedef BOOL WINAPI FNVERIFYCONSOLEIOHANDLE(HANDLE);
+typedef FNVERIFYCONSOLEIOHANDLE *PFNVERIFYCONSOLEIOHANDLE; /* No, nobody fell on the keyboard, really! */
+
+
+/**
+ * This is wrapper around the ugly SetFilePointer api.
+ *
+ * It's equivalent to SetFilePointerEx which we so unfortunately cannot use because of
+ * it not being present in NT4 GA.
+ *
+ * @returns Success indicator. Extended error information obtainable using GetLastError().
+ * @param hFile Filehandle.
+ * @param offSeek Offset to seek.
+ * @param poffNew Where to store the new file offset. NULL allowed.
+ * @param uMethod Seek method. (The windows one!)
+ */
+DECLINLINE(bool) MySetFilePointer(RTFILE hFile, uint64_t offSeek, uint64_t *poffNew, unsigned uMethod)
+{
+ bool fRc;
+ LARGE_INTEGER off;
+
+ off.QuadPart = offSeek;
+#if 1
+ if (off.LowPart != INVALID_SET_FILE_POINTER)
+ {
+ off.LowPart = SetFilePointer((HANDLE)RTFileToNative(hFile), off.LowPart, &off.HighPart, uMethod);
+ fRc = off.LowPart != INVALID_SET_FILE_POINTER;
+ }
+ else
+ {
+ SetLastError(NO_ERROR);
+ off.LowPart = SetFilePointer((HANDLE)RTFileToNative(hFile), off.LowPart, &off.HighPart, uMethod);
+ fRc = GetLastError() == NO_ERROR;
+ }
+#else
+ fRc = SetFilePointerEx((HANDLE)RTFileToNative(hFile), off, &off, uMethod);
+#endif
+ if (fRc && poffNew)
+ *poffNew = off.QuadPart;
+ return fRc;
+}
+
+
+/**
+ * Helper for checking if a VERR_DISK_FULL isn't a VERR_FILE_TOO_BIG.
+ * @returns VERR_DISK_FULL or VERR_FILE_TOO_BIG.
+ */
+static int rtFileWinCheckIfDiskReallyFull(RTFILE hFile, uint64_t cbDesired)
+{
+ /*
+ * Windows doesn't appear to have a way to query the file size limit of a
+ * file system, so we have to deduce the limit from the file system driver name.
+ * This means it will only work for known file systems.
+ */
+ if (cbDesired >= _2G - 1)
+ {
+ uint64_t cbMaxFile = UINT64_MAX;
+ RTFSTYPE enmFsType;
+ int rc = rtNtQueryFsType((HANDLE)RTFileToNative(hFile), &enmFsType);
+ if (RT_SUCCESS(rc))
+ switch (enmFsType)
+ {
+ case RTFSTYPE_NTFS:
+ case RTFSTYPE_EXFAT:
+ case RTFSTYPE_UDF:
+ cbMaxFile = UINT64_C(0xffffffffffffffff); /* (May be limited by IFS.) */
+ break;
+
+ case RTFSTYPE_ISO9660:
+ cbMaxFile = 8 *_1T;
+ break;
+
+ case RTFSTYPE_FAT:
+ cbMaxFile = _4G;
+ break;
+
+ case RTFSTYPE_HPFS:
+ cbMaxFile = _2G;
+ break;
+
+ default:
+ break;
+ }
+ if (cbDesired >= cbMaxFile)
+ return VERR_FILE_TOO_BIG;
+ }
+ return VERR_DISK_FULL;
+}
+
+
+RTR3DECL(int) RTFileFromNative(PRTFILE pFile, RTHCINTPTR uNative)
+{
+ HANDLE h = (HANDLE)uNative;
+ AssertCompile(sizeof(h) == sizeof(uNative));
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ AssertMsgFailed(("%p\n", uNative));
+ *pFile = NIL_RTFILE;
+ return VERR_INVALID_HANDLE;
+ }
+ *pFile = (RTFILE)h;
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(RTHCINTPTR) RTFileToNative(RTFILE hFile)
+{
+ AssertReturn(hFile != NIL_RTFILE, (RTHCINTPTR)INVALID_HANDLE_VALUE);
+ return (RTHCINTPTR)hFile;
+}
+
+
+RTR3DECL(int) RTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen)
+{
+ return RTFileOpenEx(pszFilename, fOpen, pFile, NULL);
+}
+
+
+RTDECL(int) RTFileOpenEx(const char *pszFilename, uint64_t fOpen, PRTFILE phFile, PRTFILEACTION penmActionTaken)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(phFile, VERR_INVALID_PARAMETER);
+ *phFile = NIL_RTFILE;
+ if (penmActionTaken)
+ *penmActionTaken = RTFILEACTION_INVALID;
+ AssertReturn(pszFilename, VERR_INVALID_PARAMETER);
+
+ /*
+ * Merge forced open flags and validate them.
+ */
+ int rc = rtFileRecalcAndValidateFlags(&fOpen);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Determine disposition, access, share mode, creation flags, and security attributes
+ * for the CreateFile API call.
+ */
+ DWORD dwCreationDisposition;
+ switch (fOpen & RTFILE_O_ACTION_MASK)
+ {
+ case RTFILE_O_OPEN:
+ dwCreationDisposition = fOpen & RTFILE_O_TRUNCATE ? TRUNCATE_EXISTING : OPEN_EXISTING;
+ break;
+ case RTFILE_O_OPEN_CREATE:
+ dwCreationDisposition = OPEN_ALWAYS;
+ break;
+ case RTFILE_O_CREATE:
+ dwCreationDisposition = CREATE_NEW;
+ break;
+ case RTFILE_O_CREATE_REPLACE:
+ dwCreationDisposition = CREATE_ALWAYS;
+ break;
+ default:
+ AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
+ }
+
+ DWORD dwDesiredAccess;
+ switch (fOpen & RTFILE_O_ACCESS_MASK)
+ {
+ case RTFILE_O_READ:
+ dwDesiredAccess = FILE_GENERIC_READ; /* RTFILE_O_APPEND is ignored. */
+ break;
+ case RTFILE_O_WRITE:
+ dwDesiredAccess = fOpen & RTFILE_O_APPEND
+ ? FILE_GENERIC_WRITE & ~FILE_WRITE_DATA
+ : FILE_GENERIC_WRITE;
+ break;
+ case RTFILE_O_READWRITE:
+ dwDesiredAccess = fOpen & RTFILE_O_APPEND
+ ? FILE_GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA)
+ : FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+ break;
+ case RTFILE_O_ATTR_ONLY:
+ if (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
+ {
+ dwDesiredAccess = 0;
+ break;
+ }
+ RT_FALL_THRU();
+ default:
+ AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
+ }
+ if (dwCreationDisposition == TRUNCATE_EXISTING)
+ /* Required for truncating the file (see MSDN), it is *NOT* part of FILE_GENERIC_WRITE. */
+ dwDesiredAccess |= GENERIC_WRITE;
+
+ /* RTFileSetMode needs following rights as well. */
+ switch (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
+ {
+ case RTFILE_O_ACCESS_ATTR_READ: dwDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break;
+ case RTFILE_O_ACCESS_ATTR_WRITE: dwDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
+ case RTFILE_O_ACCESS_ATTR_READWRITE: dwDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
+ default:
+ /* Attributes access is the same as the file access. */
+ switch (fOpen & RTFILE_O_ACCESS_MASK)
+ {
+ case RTFILE_O_READ: dwDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break;
+ case RTFILE_O_WRITE: dwDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
+ case RTFILE_O_READWRITE: dwDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
+ default:
+ AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
+ }
+ }
+
+ DWORD dwShareMode;
+ switch (fOpen & RTFILE_O_DENY_MASK)
+ {
+ case RTFILE_O_DENY_NONE: dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
+ case RTFILE_O_DENY_READ: dwShareMode = FILE_SHARE_WRITE; break;
+ case RTFILE_O_DENY_WRITE: dwShareMode = FILE_SHARE_READ; break;
+ case RTFILE_O_DENY_READWRITE: dwShareMode = 0; break;
+
+ case RTFILE_O_DENY_NOT_DELETE: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; break;
+ case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READ: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_WRITE; break;
+ case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ; break;
+ case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READWRITE:dwShareMode = FILE_SHARE_DELETE; break;
+ default:
+ AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
+ }
+
+ SECURITY_ATTRIBUTES SecurityAttributes;
+ PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
+ if (fOpen & RTFILE_O_INHERIT)
+ {
+ SecurityAttributes.nLength = sizeof(SecurityAttributes);
+ SecurityAttributes.lpSecurityDescriptor = NULL;
+ SecurityAttributes.bInheritHandle = TRUE;
+ pSecurityAttributes = &SecurityAttributes;
+ }
+
+ DWORD dwFlagsAndAttributes;
+ dwFlagsAndAttributes = !(fOpen & RTFILE_O_TEMP_AUTO_DELETE) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_TEMPORARY;
+ if (fOpen & RTFILE_O_TEMP_AUTO_DELETE)
+ fOpen |= FILE_FLAG_DELETE_ON_CLOSE;
+ if (fOpen & RTFILE_O_WRITE_THROUGH)
+ dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
+ if (fOpen & RTFILE_O_ASYNC_IO)
+ dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED;
+ if (fOpen & RTFILE_O_NO_CACHE)
+ {
+ dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
+ dwDesiredAccess &= ~FILE_APPEND_DATA;
+ }
+
+ /*
+ * Open/Create the file.
+ */
+ PRTUTF16 pwszFilename;
+ rc = RTPathWinFromUtf8(&pwszFilename, pszFilename, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile = CreateFileW(pwszFilename,
+ dwDesiredAccess,
+ dwShareMode,
+ pSecurityAttributes,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL);
+ DWORD const dwErr = GetLastError();
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Calculate the action taken value.
+ */
+ RTFILEACTION enmActionTaken;
+ switch (dwCreationDisposition)
+ {
+ case CREATE_NEW:
+ enmActionTaken = RTFILEACTION_CREATED;
+ break;
+ case CREATE_ALWAYS:
+ AssertMsg(dwErr == ERROR_ALREADY_EXISTS || dwErr == NO_ERROR, ("%u\n", dwErr));
+ enmActionTaken = dwErr == ERROR_ALREADY_EXISTS ? RTFILEACTION_REPLACED : RTFILEACTION_CREATED;
+ break;
+ case OPEN_EXISTING:
+ enmActionTaken = RTFILEACTION_OPENED;
+ break;
+ case OPEN_ALWAYS:
+ AssertMsg(dwErr == ERROR_ALREADY_EXISTS || dwErr == NO_ERROR, ("%u\n", dwErr));
+ enmActionTaken = dwErr == ERROR_ALREADY_EXISTS ? RTFILEACTION_OPENED : RTFILEACTION_CREATED;
+ break;
+ case TRUNCATE_EXISTING:
+ enmActionTaken = RTFILEACTION_TRUNCATED;
+ break;
+ default:
+ AssertMsgFailed(("%d %#x\n", dwCreationDisposition, dwCreationDisposition));
+ enmActionTaken = RTFILEACTION_INVALID;
+ break;
+ }
+
+ /*
+ * Turn off indexing of directory through Windows Indexing Service if
+ * we created a new file or replaced an existing one.
+ */
+ if ( (fOpen & RTFILE_O_NOT_CONTENT_INDEXED)
+ && ( enmActionTaken == RTFILEACTION_CREATED
+ || enmActionTaken == RTFILEACTION_REPLACED) )
+ {
+ /** @todo there must be a way to do this via the handle! */
+ if (!SetFileAttributesW(pwszFilename, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ /*
+ * If RTFILEACTION_OPENED, we may need to truncate the file.
+ */
+ else if ( (fOpen & (RTFILE_O_TRUNCATE | RTFILE_O_ACTION_MASK)) == (RTFILE_O_TRUNCATE | RTFILE_O_OPEN_CREATE)
+ && enmActionTaken == RTFILEACTION_OPENED)
+ {
+ if (SetEndOfFile(hFile))
+ enmActionTaken = RTFILEACTION_TRUNCATED;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ if (penmActionTaken)
+ *penmActionTaken = enmActionTaken;
+ if (RT_SUCCESS(rc))
+ {
+ *phFile = (RTFILE)hFile;
+ Assert((HANDLE)*phFile == hFile);
+ RTPathWinFree(pwszFilename);
+ return VINF_SUCCESS;
+ }
+
+ CloseHandle(hFile);
+ }
+ else
+ {
+ if ( penmActionTaken
+ && dwCreationDisposition == CREATE_NEW
+ && dwErr == ERROR_FILE_EXISTS)
+ *penmActionTaken = RTFILEACTION_ALREADY_EXISTS;
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ RTPathWinFree(pwszFilename);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTFileOpenBitBucket(PRTFILE phFile, uint64_t fAccess)
+{
+ AssertReturn( fAccess == RTFILE_O_READ
+ || fAccess == RTFILE_O_WRITE
+ || fAccess == RTFILE_O_READWRITE,
+ VERR_INVALID_PARAMETER);
+ return RTFileOpen(phFile, "NUL", fAccess | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
+}
+
+
+RTDECL(int) RTFileDup(RTFILE hFileSrc, uint64_t fFlags, PRTFILE phFileNew)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phFileNew, VERR_INVALID_POINTER);
+ *phFileNew = NIL_RTFILE;
+ AssertPtrReturn(phFileNew, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~(uint64_t)RTFILE_O_INHERIT), VERR_INVALID_FLAGS);
+
+ /*
+ * Do the job.
+ */
+ HANDLE hNew = INVALID_HANDLE_VALUE;
+ if (DuplicateHandle(GetCurrentProcess(), (HANDLE)RTFileToNative(hFileSrc),
+ GetCurrentProcess(), &hNew, 0, RT_BOOL(fFlags & RTFILE_O_INHERIT), DUPLICATE_SAME_ACCESS))
+ {
+ *phFileNew = (RTFILE)hNew;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTR3DECL(int) RTFileClose(RTFILE hFile)
+{
+ if (hFile == NIL_RTFILE)
+ return VINF_SUCCESS;
+ if (CloseHandle((HANDLE)RTFileToNative(hFile)))
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTFILE rtFileGetStandard(RTHANDLESTD enmStdHandle)
+{
+ DWORD dwStdHandle;
+ switch (enmStdHandle)
+ {
+ case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break;
+ case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break;
+ case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break;
+ default:
+ AssertFailedReturn(NIL_RTFILE);
+ }
+
+ HANDLE hNative = GetStdHandle(dwStdHandle);
+ if (hNative == INVALID_HANDLE_VALUE)
+ return NIL_RTFILE;
+
+ RTFILE hFile = (RTFILE)(uintptr_t)hNative;
+ AssertReturn((HANDLE)(uintptr_t)hFile == hNative, NIL_RTFILE);
+ return hFile;
+}
+
+
+RTR3DECL(int) RTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
+{
+ static ULONG aulSeekRecode[] =
+ {
+ FILE_BEGIN,
+ FILE_CURRENT,
+ FILE_END,
+ };
+
+ /*
+ * Validate input.
+ */
+ if (uMethod > RTFILE_SEEK_END)
+ {
+ AssertMsgFailed(("Invalid uMethod=%d\n", uMethod));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Execute the seek.
+ */
+ if (MySetFilePointer(hFile, offSeek, poffActual, aulSeekRecode[uMethod]))
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTR3DECL(int) RTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ if (cbToRead <= 0)
+ {
+ if (pcbRead)
+ *pcbRead = 0;
+ return VINF_SUCCESS;
+ }
+ ULONG cbToReadAdj = (ULONG)cbToRead;
+ AssertReturn(cbToReadAdj == cbToRead, VERR_NUMBER_TOO_BIG);
+
+ ULONG cbRead = 0;
+ if (ReadFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToReadAdj, &cbRead, NULL))
+ {
+ if (pcbRead)
+ /* Caller can handle partial reads. */
+ *pcbRead = cbRead;
+ else
+ {
+ /* Caller expects everything to be read. */
+ while (cbToReadAdj > cbRead)
+ {
+ ULONG cbReadPart = 0;
+ if (!ReadFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbRead, cbToReadAdj - cbRead, &cbReadPart, NULL))
+ return RTErrConvertFromWin32(GetLastError());
+ if (cbReadPart == 0)
+ return VERR_EOF;
+ cbRead += cbReadPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * If it's a console, we might bump into out of memory conditions in the
+ * ReadConsole call.
+ */
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_NOT_ENOUGH_MEMORY)
+ {
+ ULONG cbChunk = cbToReadAdj / 2;
+ if (cbChunk > 16*_1K)
+ cbChunk = 16*_1K;
+ else
+ cbChunk = RT_ALIGN_32(cbChunk, 256);
+
+ cbRead = 0;
+ while (cbToReadAdj > cbRead)
+ {
+ ULONG cbToReadNow = RT_MIN(cbChunk, cbToReadAdj - cbRead);
+ ULONG cbReadPart = 0;
+ if (!ReadFile((HANDLE)RTFileToNative(hFile), (char *)pvBuf + cbRead, cbToReadNow, &cbReadPart, NULL))
+ {
+ /* If we failed because the buffer is too big, shrink it and
+ try again. */
+ dwErr = GetLastError();
+ if ( dwErr == ERROR_NOT_ENOUGH_MEMORY
+ && cbChunk > 8)
+ {
+ cbChunk /= 2;
+ continue;
+ }
+ return RTErrConvertFromWin32(dwErr);
+ }
+ cbRead += cbReadPart;
+
+ /* Return if the caller can handle partial reads, otherwise try
+ fill the buffer all the way up. */
+ if (pcbRead)
+ {
+ *pcbRead = cbRead;
+ break;
+ }
+ if (cbReadPart == 0)
+ return VERR_EOF;
+ }
+ return VINF_SUCCESS;
+ }
+
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+RTDECL(int) RTFileReadAt(RTFILE hFile, RTFOFF off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ ULONG cbToReadAdj = (ULONG)cbToRead;
+ AssertReturn(cbToReadAdj == cbToRead, VERR_NUMBER_TOO_BIG);
+
+ OVERLAPPED Overlapped;
+ Overlapped.Offset = (uint32_t)off;
+ Overlapped.OffsetHigh = (uint32_t)(off >> 32);
+ Overlapped.hEvent = NULL;
+ Overlapped.Internal = 0;
+ Overlapped.InternalHigh = 0;
+
+ ULONG cbRead = 0;
+ if (ReadFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToReadAdj, &cbRead, &Overlapped))
+ {
+ if (pcbRead)
+ /* Caller can handle partial reads. */
+ *pcbRead = cbRead;
+ else
+ {
+ /* Caller expects everything to be read. */
+ while (cbToReadAdj > cbRead)
+ {
+ Overlapped.Offset = (uint32_t)(off + cbRead);
+ Overlapped.OffsetHigh = (uint32_t)((off + cbRead) >> 32);
+ Overlapped.hEvent = NULL;
+ Overlapped.Internal = 0;
+ Overlapped.InternalHigh = 0;
+
+ ULONG cbReadPart = 0;
+ if (!ReadFile((HANDLE)RTFileToNative(hFile), (char *)pvBuf + cbRead, cbToReadAdj - cbRead,
+ &cbReadPart, &Overlapped))
+ return RTErrConvertFromWin32(GetLastError());
+ if (cbReadPart == 0)
+ return VERR_EOF;
+ cbRead += cbReadPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ /* We will get an EOF error when using overlapped I/O. So, make sure we don't
+ return it when pcbhRead is not NULL. */
+ DWORD dwErr = GetLastError();
+ if (pcbRead && dwErr == ERROR_HANDLE_EOF)
+ {
+ *pcbRead = 0;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+RTR3DECL(int) RTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ if (cbToWrite <= 0)
+ return VINF_SUCCESS;
+ ULONG const cbToWriteAdj = (ULONG)cbToWrite;
+ AssertReturn(cbToWriteAdj == cbToWrite, VERR_NUMBER_TOO_BIG);
+
+ ULONG cbWritten = 0;
+ if (WriteFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToWriteAdj, &cbWritten, NULL))
+ {
+ if (pcbWritten)
+ /* Caller can handle partial writes. */
+ *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */
+ else
+ {
+ /* Caller expects everything to be written. */
+ while (cbWritten < cbToWriteAdj)
+ {
+ ULONG cbWrittenPart = 0;
+ if (!WriteFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbWritten,
+ cbToWriteAdj - cbWritten, &cbWrittenPart, NULL))
+ {
+ int rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_DISK_FULL)
+ rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteAdj - cbWritten);
+ return rc;
+ }
+ if (cbWrittenPart == 0)
+ return VERR_WRITE_ERROR;
+ cbWritten += cbWrittenPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * If it's a console, we might bump into out of memory conditions in the
+ * WriteConsole call.
+ */
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_NOT_ENOUGH_MEMORY)
+ {
+ ULONG cbChunk = cbToWriteAdj / 2;
+ if (cbChunk > _32K)
+ cbChunk = _32K;
+ else
+ cbChunk = RT_ALIGN_32(cbChunk, 256);
+
+ cbWritten = 0;
+ while (cbWritten < cbToWriteAdj)
+ {
+ ULONG cbToWriteNow = RT_MIN(cbChunk, cbToWriteAdj - cbWritten);
+ ULONG cbWrittenPart = 0;
+ if (!WriteFile((HANDLE)RTFileToNative(hFile), (const char *)pvBuf + cbWritten, cbToWriteNow, &cbWrittenPart, NULL))
+ {
+ /* If we failed because the buffer is too big, shrink it and
+ try again. */
+ dwErr = GetLastError();
+ if ( dwErr == ERROR_NOT_ENOUGH_MEMORY
+ && cbChunk > 8)
+ {
+ cbChunk /= 2;
+ continue;
+ }
+ int rc = RTErrConvertFromWin32(dwErr);
+ if (rc == VERR_DISK_FULL)
+ rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteNow);
+ return rc;
+ }
+ cbWritten += cbWrittenPart;
+
+ /* Return if the caller can handle partial writes, otherwise try
+ write out everything. */
+ if (pcbWritten)
+ {
+ *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */
+ break;
+ }
+ if (cbWrittenPart == 0)
+ return VERR_WRITE_ERROR;
+ }
+ return VINF_SUCCESS;
+ }
+
+ int rc = RTErrConvertFromWin32(dwErr);
+ if (rc == VERR_DISK_FULL)
+ rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteAdj);
+ return rc;
+}
+
+
+RTDECL(int) RTFileWriteAt(RTFILE hFile, RTFOFF off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ ULONG const cbToWriteAdj = (ULONG)cbToWrite;
+ AssertReturn(cbToWriteAdj == cbToWrite, VERR_NUMBER_TOO_BIG);
+
+ OVERLAPPED Overlapped;
+ Overlapped.Offset = (uint32_t)off;
+ Overlapped.OffsetHigh = (uint32_t)(off >> 32);
+ Overlapped.hEvent = NULL;
+ Overlapped.Internal = 0;
+ Overlapped.InternalHigh = 0;
+
+ ULONG cbWritten = 0;
+ if (WriteFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToWriteAdj, &cbWritten, &Overlapped))
+ {
+ if (pcbWritten)
+ /* Caller can handle partial writes. */
+ *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */
+ else
+ {
+ /* Caller expects everything to be written. */
+ while (cbWritten < cbToWriteAdj)
+ {
+ Overlapped.Offset = (uint32_t)(off + cbWritten);
+ Overlapped.OffsetHigh = (uint32_t)((off + cbWritten) >> 32);
+ Overlapped.hEvent = NULL;
+ Overlapped.Internal = 0;
+ Overlapped.InternalHigh = 0;
+
+ ULONG cbWrittenPart = 0;
+ if (!WriteFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbWritten,
+ cbToWriteAdj - cbWritten, &cbWrittenPart, &Overlapped))
+ {
+ int rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_DISK_FULL)
+ rc = rtFileWinCheckIfDiskReallyFull(hFile, off + cbToWriteAdj);
+ return rc;
+ }
+ if (cbWrittenPart == 0)
+ return VERR_WRITE_ERROR;
+ cbWritten += cbWrittenPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ int rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_DISK_FULL)
+ rc = rtFileWinCheckIfDiskReallyFull(hFile, off + cbToWriteAdj);
+ return rc;
+}
+
+
+RTR3DECL(int) RTFileFlush(RTFILE hFile)
+{
+ if (!FlushFileBuffers((HANDLE)RTFileToNative(hFile)))
+ {
+ int rc = GetLastError();
+ Log(("FlushFileBuffers failed with %d\n", rc));
+ return RTErrConvertFromWin32(rc);
+ }
+ return VINF_SUCCESS;
+}
+
+#if 1
+
+/**
+ * Checks the the two handles refers to the same file.
+ *
+ * @returns true if the same file, false if different ones or invalid handles.
+ * @param hFile1 Handle \#1.
+ * @param hFile2 Handle \#2.
+ */
+static bool rtFileIsSame(HANDLE hFile1, HANDLE hFile2)
+{
+ /*
+ * We retry in case CreationTime or the Object ID is being modified and there
+ * aren't any IndexNumber (file ID) on this kind of file system.
+ */
+ for (uint32_t iTries = 0; iTries < 3; iTries++)
+ {
+ /*
+ * Fetch data to compare (being a little lazy here).
+ */
+ struct
+ {
+ HANDLE hFile;
+ NTSTATUS rcObjId;
+ FILE_OBJECTID_INFORMATION ObjId;
+ FILE_ALL_INFORMATION All;
+ FILE_FS_VOLUME_INFORMATION Vol;
+ } auData[2];
+ auData[0].hFile = hFile1;
+ auData[1].hFile = hFile2;
+
+ for (uintptr_t i = 0; i < RT_ELEMENTS(auData); i++)
+ {
+ RT_ZERO(auData[i].ObjId);
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ auData[i].rcObjId = NtQueryInformationFile(auData[i].hFile, &Ios, &auData[i].ObjId, sizeof(auData[i].ObjId),
+ FileObjectIdInformation);
+
+ RT_ZERO(auData[i].All);
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ NTSTATUS rcNt = NtQueryInformationFile(auData[i].hFile, &Ios, &auData[i].All, sizeof(auData[i].All),
+ FileAllInformation);
+ AssertReturn(rcNt == STATUS_BUFFER_OVERFLOW /* insufficient space for name info */ || NT_SUCCESS(rcNt), false);
+
+ union
+ {
+ FILE_FS_VOLUME_INFORMATION Info;
+ uint8_t abBuf[sizeof(FILE_FS_VOLUME_INFORMATION) + 4096];
+ } uVol;
+ RT_ZERO(uVol.Info);
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtQueryVolumeInformationFile(auData[i].hFile, &Ios, &uVol, sizeof(uVol), FileFsVolumeInformation);
+ if (NT_SUCCESS(rcNt))
+ auData[i].Vol = uVol.Info;
+ else
+ RT_ZERO(auData[i].Vol);
+ }
+
+ /*
+ * Compare it.
+ */
+ if ( auData[0].All.StandardInformation.Directory
+ == auData[1].All.StandardInformation.Directory)
+ { /* likely */ }
+ else
+ break;
+
+ if ( (auData[0].All.BasicInformation.FileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT))
+ == (auData[1].All.BasicInformation.FileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)))
+ { /* likely */ }
+ else
+ break;
+
+ if ( auData[0].Vol.VolumeSerialNumber
+ == auData[1].Vol.VolumeSerialNumber)
+ { /* likely */ }
+ else
+ break;
+
+ if ( auData[0].All.InternalInformation.IndexNumber.QuadPart
+ == auData[1].All.InternalInformation.IndexNumber.QuadPart)
+ { /* likely */ }
+ else
+ break;
+
+ if ( !NT_SUCCESS(auData[0].rcObjId)
+ || memcmp(&auData[0].ObjId, &auData[1].ObjId, RT_UOFFSETOF(FILE_OBJECTID_INFORMATION, ExtendedInfo)) == 0)
+ {
+ if ( auData[0].All.BasicInformation.CreationTime.QuadPart
+ == auData[1].All.BasicInformation.CreationTime.QuadPart)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * If @a hFile is opened in append mode, try return a handle with
+ * FILE_WRITE_DATA permissions.
+ *
+ * @returns Duplicate handle.
+ * @param hFile The NT handle to check & duplicate.
+ *
+ * @todo It would be much easier to implement this by not dropping the
+ * FILE_WRITE_DATA access and instead have the RTFileWrite APIs
+ * enforce the appending. That will require keeping additional
+ * information along side the handle (instance structure). However, on
+ * windows you can grant append permissions w/o giving people access to
+ * overwrite existing data, so the RTFileOpenEx code would have to deal
+ * with those kinds of STATUS_ACCESS_DENIED too then.
+ */
+static HANDLE rtFileReOpenAppendOnlyWithFullWriteAccess(HANDLE hFile)
+{
+ OBJECT_BASIC_INFORMATION BasicInfo = {0};
+ ULONG cbActual = 0;
+ NTSTATUS rcNt = NtQueryObject(hFile, ObjectBasicInformation, &BasicInfo, sizeof(BasicInfo), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ {
+ if ((BasicInfo.GrantedAccess & (FILE_APPEND_DATA | FILE_WRITE_DATA)) == FILE_APPEND_DATA)
+ {
+ /*
+ * We cannot use NtDuplicateObject here as it is not possible to
+ * upgrade the access on files, only making it more strict. So,
+ * query the path and re-open it (we could do by file/object/whatever
+ * id too, but that may not work with all file systems).
+ */
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ UNICODE_STRING NtName;
+ int rc = RTNtPathFromHandle(&NtName, hFile, 0);
+ AssertRCReturn(rc, INVALID_HANDLE_VALUE);
+
+ HANDLE hDupFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, BasicInfo.Attributes & ~OBJ_INHERIT, NULL, NULL);
+
+ rcNt = NtCreateFile(&hDupFile,
+ BasicInfo.GrantedAccess | FILE_WRITE_DATA,
+ &ObjAttr,
+ &Ios,
+ NULL /* AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT /*??*/,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ RTUtf16Free(NtName.Buffer);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Check that we've opened the same file.
+ */
+ if (rtFileIsSame(hFile, hDupFile))
+ return hDupFile;
+ NtClose(hDupFile);
+ }
+ }
+ AssertFailed();
+ }
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+#endif
+
+RTR3DECL(int) RTFileSetSize(RTFILE hFile, uint64_t cbSize)
+{
+#if 1
+ HANDLE hNtFile = (HANDLE)RTFileToNative(hFile);
+ HANDLE hDupFile = INVALID_HANDLE_VALUE;
+ union
+ {
+ FILE_END_OF_FILE_INFORMATION Eof;
+ FILE_ALLOCATION_INFORMATION Alloc;
+ } uInfo;
+
+ /*
+ * Change the EOF marker.
+ *
+ * HACK ALERT! If the file was opened in RTFILE_O_APPEND mode, we will have
+ * to re-open it with FILE_WRITE_DATA access to get the job done.
+ * This how ftruncate on a unixy system would work but not how
+ * it is done on Windows where appending is a separate permission
+ * rather than just a write modifier, making this hack totally wrong.
+ */
+ /** @todo The right way to fix this is either to add a RTFileSetSizeEx function
+ * for specifically requesting the unixy behaviour, or add an additional
+ * flag to RTFileOpen[Ex] to request the unixy append behaviour there.
+ * The latter would require saving the open flags in a instance data
+ * structure, which is a bit of a risky move, though something we should
+ * do in 6.2 (or later).
+ *
+ * Note! Because handle interitance, it is not realyan option to
+ * always use FILE_WRITE_DATA and implement the RTFILE_O_APPEND
+ * bits in RTFileWrite and friends. Besides, it's not like
+ * RTFILE_O_APPEND is so clearly defined anyway - see
+ * RTFileWriteAt.
+ */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ uInfo.Eof.EndOfFile.QuadPart = cbSize;
+ NTSTATUS rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Eof), FileEndOfFileInformation);
+ if (rcNt == STATUS_ACCESS_DENIED)
+ {
+ hDupFile = rtFileReOpenAppendOnlyWithFullWriteAccess(hNtFile);
+ if (hDupFile != INVALID_HANDLE_VALUE)
+ {
+ hNtFile = hDupFile;
+ uInfo.Eof.EndOfFile.QuadPart = cbSize;
+ rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Eof), FileEndOfFileInformation);
+ }
+ }
+
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Change the allocation.
+ */
+ uInfo.Alloc.AllocationSize.QuadPart = cbSize;
+ rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Alloc), FileAllocationInformation);
+ }
+
+ /*
+ * Close the temporary file handle:
+ */
+ if (hDupFile != INVALID_HANDLE_VALUE)
+ NtClose(hDupFile);
+
+ if (RT_SUCCESS(rcNt))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(rcNt);
+
+#else /* this version of the code will fail to truncate files when RTFILE_O_APPEND is in effect, which isn't what we want... */
+ /*
+ * Get current file pointer.
+ */
+ int rc;
+ uint64_t offCurrent;
+ if (MySetFilePointer(hFile, 0, &offCurrent, FILE_CURRENT))
+ {
+ /*
+ * Set new file pointer.
+ */
+ if (MySetFilePointer(hFile, cbSize, NULL, FILE_BEGIN))
+ {
+ /* set file pointer */
+ if (SetEndOfFile((HANDLE)RTFileToNative(hFile)))
+ {
+ /*
+ * Restore file pointer and return.
+ * If the old pointer was beyond the new file end, ignore failure.
+ */
+ if ( MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN)
+ || offCurrent > cbSize)
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Failed, try restoring the file pointer.
+ */
+ rc = GetLastError();
+ MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN);
+
+ if (rc == ERROR_DISK_FULL)
+ return rtFileWinCheckIfDiskReallyFull(hFile, cbSize);
+ }
+ else
+ rc = GetLastError();
+ }
+ else
+ rc = GetLastError();
+
+ return RTErrConvertFromWin32(rc);
+#endif
+}
+
+
+RTR3DECL(int) RTFileQuerySize(RTFILE hFile, uint64_t *pcbSize)
+{
+ /*
+ * GetFileSize works for most handles.
+ */
+ ULARGE_INTEGER Size;
+ Size.LowPart = GetFileSize((HANDLE)RTFileToNative(hFile), &Size.HighPart);
+ if (Size.LowPart != INVALID_FILE_SIZE)
+ {
+ *pcbSize = Size.QuadPart;
+ return VINF_SUCCESS;
+ }
+ int rc = RTErrConvertFromWin32(GetLastError());
+
+ /*
+ * Could it be a volume or a disk?
+ */
+ DISK_GEOMETRY DriveGeo;
+ DWORD cbDriveGeo;
+ if (DeviceIoControl((HANDLE)RTFileToNative(hFile),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
+ &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
+ {
+ if ( DriveGeo.MediaType == FixedMedia
+ || DriveGeo.MediaType == RemovableMedia)
+ {
+ *pcbSize = DriveGeo.Cylinders.QuadPart
+ * DriveGeo.TracksPerCylinder
+ * DriveGeo.SectorsPerTrack
+ * DriveGeo.BytesPerSector;
+
+ GET_LENGTH_INFORMATION DiskLenInfo;
+ DWORD Ignored;
+ if (DeviceIoControl((HANDLE)RTFileToNative(hFile),
+ IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
+ &DiskLenInfo, sizeof(DiskLenInfo), &Ignored, (LPOVERLAPPED)NULL))
+ {
+ /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */
+ *pcbSize = DiskLenInfo.Length.QuadPart;
+ }
+ return VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * Return the GetFileSize result if not a volume/disk.
+ */
+ return rc;
+}
+
+
+RTR3DECL(int) RTFileQueryMaxSizeEx(RTFILE hFile, PRTFOFF pcbMax)
+{
+ /** @todo r=bird:
+ * We might have to make this code OS version specific... In the worse
+ * case, we'll have to try GetVolumeInformationByHandle on vista and fall
+ * back on NtQueryVolumeInformationFile(,,,, FileFsAttributeInformation)
+ * else where, and check for known file system names. (For LAN shares we'll
+ * have to figure out the remote file system.) */
+ RT_NOREF_PV(hFile); RT_NOREF_PV(pcbMax);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTR3DECL(bool) RTFileIsValid(RTFILE hFile)
+{
+ if (hFile != NIL_RTFILE)
+ {
+ DWORD dwType = GetFileType((HANDLE)RTFileToNative(hFile));
+ switch (dwType)
+ {
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_DISK:
+ case FILE_TYPE_PIPE:
+ case FILE_TYPE_REMOTE:
+ return true;
+
+ case FILE_TYPE_UNKNOWN:
+ if (GetLastError() == NO_ERROR)
+ return true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+
+#define LOW_DWORD(u64) ((DWORD)u64)
+#define HIGH_DWORD(u64) (((DWORD *)&u64)[1])
+
+RTR3DECL(int) RTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /* Check arguments. */
+ if (fLock & ~RTFILE_LOCK_MASK)
+ {
+ AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Prepare flags. */
+ Assert(RTFILE_LOCK_WRITE);
+ DWORD dwFlags = (fLock & RTFILE_LOCK_WRITE) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
+ Assert(RTFILE_LOCK_WAIT);
+ if (!(fLock & RTFILE_LOCK_WAIT))
+ dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
+
+ /* Windows structure. */
+ OVERLAPPED Overlapped;
+ memset(&Overlapped, 0, sizeof(Overlapped));
+ Overlapped.Offset = LOW_DWORD(offLock);
+ Overlapped.OffsetHigh = HIGH_DWORD(offLock);
+
+ /* Note: according to Microsoft, LockFileEx API call is available starting from NT 3.5 */
+ if (LockFileEx((HANDLE)RTFileToNative(hFile), dwFlags, 0, LOW_DWORD(cbLock), HIGH_DWORD(cbLock), &Overlapped))
+ return VINF_SUCCESS;
+
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTR3DECL(int) RTFileChangeLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /* Check arguments. */
+ if (fLock & ~RTFILE_LOCK_MASK)
+ {
+ AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Remove old lock. */
+ int rc = RTFileUnlock(hFile, offLock, cbLock);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Set new lock. */
+ rc = RTFileLock(hFile, fLock, offLock, cbLock);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /* Try to restore old lock. */
+ unsigned fLockOld = (fLock & RTFILE_LOCK_WRITE) ? fLock & ~RTFILE_LOCK_WRITE : fLock | RTFILE_LOCK_WRITE;
+ rc = RTFileLock(hFile, fLockOld, offLock, cbLock);
+ if (RT_SUCCESS(rc))
+ return VERR_FILE_LOCK_VIOLATION;
+ else
+ return VERR_FILE_LOCK_LOST;
+}
+
+
+RTR3DECL(int) RTFileUnlock(RTFILE hFile, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ if (UnlockFile((HANDLE)RTFileToNative(hFile),
+ LOW_DWORD(offLock), HIGH_DWORD(offLock),
+ LOW_DWORD(cbLock), HIGH_DWORD(cbLock)))
+ return VINF_SUCCESS;
+
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+
+RTR3DECL(int) RTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ /*
+ * Validate input.
+ */
+ if (hFile == NIL_RTFILE)
+ {
+ AssertMsgFailed(("Invalid hFile=%RTfile\n", hFile));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!pObjInfo)
+ {
+ AssertMsgFailed(("Invalid pObjInfo=%p\n", pObjInfo));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING
+ || enmAdditionalAttribs > RTFSOBJATTRADD_LAST)
+ {
+ AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Query file info.
+ */
+ HANDLE hHandle = (HANDLE)RTFileToNative(hFile);
+#if 1
+ uint64_t auBuf[168 / sizeof(uint64_t)]; /* Missing FILE_ALL_INFORMATION here. */
+ int rc = rtPathNtQueryInfoFromHandle(hFile, auBuf, sizeof(auBuf), pObjInfo, enmAdditionalAttribs, NULL, 0);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /*
+ * Console I/O handles make trouble here. On older windows versions they
+ * end up with ERROR_INVALID_HANDLE when handed to the above API, while on
+ * more recent ones they cause different errors to appear.
+ *
+ * Thus, we must ignore the latter and doubly verify invalid handle claims.
+ * We use the undocumented VerifyConsoleIoHandle to do this, falling back on
+ * GetFileType should it not be there.
+ */
+ if ( rc == VERR_INVALID_HANDLE
+ || rc == VERR_ACCESS_DENIED
+ || rc == VERR_UNEXPECTED_FS_OBJ_TYPE)
+ {
+ static PFNVERIFYCONSOLEIOHANDLE s_pfnVerifyConsoleIoHandle = NULL;
+ static bool volatile s_fInitialized = false;
+ PFNVERIFYCONSOLEIOHANDLE pfnVerifyConsoleIoHandle;
+ if (s_fInitialized)
+ pfnVerifyConsoleIoHandle = s_pfnVerifyConsoleIoHandle;
+ else
+ {
+ pfnVerifyConsoleIoHandle = (PFNVERIFYCONSOLEIOHANDLE)RTLdrGetSystemSymbol("kernel32.dll", "VerifyConsoleIoHandle");
+ ASMAtomicWriteBool(&s_fInitialized, true);
+ }
+ if ( pfnVerifyConsoleIoHandle
+ ? !pfnVerifyConsoleIoHandle(hHandle)
+ : GetFileType(hHandle) == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR)
+ return VERR_INVALID_HANDLE;
+ }
+ /*
+ * On Windows 10 and (hopefully) 8.1 we get ERROR_INVALID_FUNCTION with console
+ * I/O handles and null device handles. We must ignore these just like the
+ * above invalid handle error.
+ */
+ else if (rc != VERR_INVALID_FUNCTION && rc != VERR_IO_BAD_COMMAND)
+ return rc;
+
+ RT_ZERO(*pObjInfo);
+ pObjInfo->Attr.enmAdditional = enmAdditionalAttribs;
+ pObjInfo->Attr.fMode = rtFsModeFromDos(RTFS_DOS_NT_DEVICE, "", 0, 0, 0);
+ return VINF_SUCCESS;
+#else
+
+ BY_HANDLE_FILE_INFORMATION Data;
+ if (!GetFileInformationByHandle(hHandle, &Data))
+ {
+ /*
+ * Console I/O handles make trouble here. On older windows versions they
+ * end up with ERROR_INVALID_HANDLE when handed to the above API, while on
+ * more recent ones they cause different errors to appear.
+ *
+ * Thus, we must ignore the latter and doubly verify invalid handle claims.
+ * We use the undocumented VerifyConsoleIoHandle to do this, falling back on
+ * GetFileType should it not be there.
+ */
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_INVALID_HANDLE)
+ {
+ static PFNVERIFYCONSOLEIOHANDLE s_pfnVerifyConsoleIoHandle = NULL;
+ static bool volatile s_fInitialized = false;
+ PFNVERIFYCONSOLEIOHANDLE pfnVerifyConsoleIoHandle;
+ if (s_fInitialized)
+ pfnVerifyConsoleIoHandle = s_pfnVerifyConsoleIoHandle;
+ else
+ {
+ pfnVerifyConsoleIoHandle = (PFNVERIFYCONSOLEIOHANDLE)RTLdrGetSystemSymbol("kernel32.dll", "VerifyConsoleIoHandle");
+ ASMAtomicWriteBool(&s_fInitialized, true);
+ }
+ if ( pfnVerifyConsoleIoHandle
+ ? !pfnVerifyConsoleIoHandle(hHandle)
+ : GetFileType(hHandle) == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR)
+ return VERR_INVALID_HANDLE;
+ }
+ /*
+ * On Windows 10 and (hopefully) 8.1 we get ERROR_INVALID_FUNCTION with console I/O
+ * handles. We must ignore these just like the above invalid handle error.
+ */
+ else if (dwErr != ERROR_INVALID_FUNCTION)
+ return RTErrConvertFromWin32(dwErr);
+
+ RT_ZERO(Data);
+ Data.dwFileAttributes = RTFS_DOS_NT_DEVICE;
+ }
+
+ /*
+ * Setup the returned data.
+ */
+ pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32)
+ | (uint64_t)Data.nFileSizeLow;
+ pObjInfo->cbAllocated = pObjInfo->cbObject;
+
+ Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime));
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime);
+ pObjInfo->ChangeTime = pObjInfo->ModificationTime;
+
+ pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, "", 0,
+ RTFSMODE_SYMLINK_REPARSE_TAG /* (symlink or not, doesn't usually matter here) */);
+
+ /*
+ * Requested attributes (we cannot provide anything actually).
+ */
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pObjInfo->Attr.u.Unix.uid = ~0U;
+ pObjInfo->Attr.u.Unix.gid = ~0U;
+ pObjInfo->Attr.u.Unix.cHardlinks = Data.nNumberOfLinks ? Data.nNumberOfLinks : 1;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = Data.dwVolumeSerialNumber;
+ pObjInfo->Attr.u.Unix.INodeId = RT_MAKE_U64(Data.nFileIndexLow, Data.nFileIndexHigh);
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pObjInfo->Attr.u.UnixOwner.uid = ~0U;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pObjInfo->Attr.u.UnixGroup.gid = ~0U;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+#endif
+}
+
+
+RTR3DECL(int) RTFileSetTimes(RTFILE hFile, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ RT_NOREF_PV(pChangeTime); /* Not exposed thru the windows API we're using. */
+
+ if (!pAccessTime && !pModificationTime && !pBirthTime)
+ return VINF_SUCCESS; /* NOP */
+
+ FILETIME CreationTimeFT;
+ PFILETIME pCreationTimeFT = NULL;
+ if (pBirthTime)
+ pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT);
+
+ FILETIME LastAccessTimeFT;
+ PFILETIME pLastAccessTimeFT = NULL;
+ if (pAccessTime)
+ pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT);
+
+ FILETIME LastWriteTimeFT;
+ PFILETIME pLastWriteTimeFT = NULL;
+ if (pModificationTime)
+ pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT);
+
+ int rc = VINF_SUCCESS;
+ if (!SetFileTime((HANDLE)RTFileToNative(hFile), pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT))
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTFileSetTimes(%RTfile, %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n",
+ hFile, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc));
+ }
+ return rc;
+}
+
+
+#if 0 /* RTFileSetMode is implemented by RTFileSetMode-r3-nt.cpp */
+/* This comes from a source file with a different set of system headers (DDK)
+ * so it can't be declared in a common header, like internal/file.h.
+ */
+extern int rtFileNativeSetAttributes(HANDLE FileHandle, ULONG FileAttributes);
+
+
+RTR3DECL(int) RTFileSetMode(RTFILE hFile, RTFMODE fMode)
+{
+ /*
+ * Normalize the mode and call the API.
+ */
+ fMode = rtFsModeNormalize(fMode, NULL, 0);
+ if (!rtFsModeIsValid(fMode))
+ return VERR_INVALID_PARAMETER;
+
+ ULONG FileAttributes = (fMode & RTFS_DOS_MASK) >> RTFS_DOS_SHIFT;
+ int Err = rtFileNativeSetAttributes((HANDLE)hFile, FileAttributes);
+ if (Err != ERROR_SUCCESS)
+ {
+ int rc = RTErrConvertFromWin32(Err);
+ Log(("RTFileSetMode(%RTfile, %RTfmode): rtFileNativeSetAttributes (0x%08X) failed with err %d (%Rrc)\n",
+ hFile, fMode, FileAttributes, Err, rc));
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+#endif
+
+
+/* RTFileQueryFsSizes is implemented by ../nt/RTFileQueryFsSizes-nt.cpp */
+
+
+RTR3DECL(int) RTFileDelete(const char *pszFilename)
+{
+ PRTUTF16 pwszFilename;
+ int rc = RTPathWinFromUtf8(&pwszFilename, pszFilename, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ if (!DeleteFileW(pwszFilename))
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTPathWinFree(pwszFilename);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFileRename(const char *pszSrc, const char *pszDst, unsigned fRename)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Hand it on to the worker.
+ */
+ int rc = rtPathWin32MoveRename(pszSrc, pszDst,
+ fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0,
+ RTFS_TYPE_FILE);
+
+ LogFlow(("RTFileMove(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
+ pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
+ return rc;
+
+}
+
+
+RTDECL(int) RTFileMove(const char *pszSrc, const char *pszDst, unsigned fMove)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(!(fMove & ~RTFILEMOVE_FLAGS_REPLACE), ("%#x\n", fMove), VERR_INVALID_PARAMETER);
+
+ /*
+ * Hand it on to the worker.
+ */
+ int rc = rtPathWin32MoveRename(pszSrc, pszDst,
+ fMove & RTFILEMOVE_FLAGS_REPLACE
+ ? MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING
+ : MOVEFILE_COPY_ALLOWED,
+ RTFS_TYPE_FILE);
+
+ LogFlow(("RTFileMove(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
+ pszSrc, pszSrc, pszDst, pszDst, fMove, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/fs-win.cpp b/src/VBox/Runtime/r3/win/fs-win.cpp
new file mode 100644
index 00000000..4210ffed
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/fs-win.cpp
@@ -0,0 +1,440 @@
+/* $Id: fs-win.cpp $ */
+/** @file
+ * IPRT - File System, Win32.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include <iprt/win/windows.h>
+
+#include <iprt/fs.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/param.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include "internal/fs.h"
+
+/* from ntdef.h */
+typedef LONG NTSTATUS;
+
+/* from ntddk.h */
+typedef struct _IO_STATUS_BLOCK {
+ union {
+ NTSTATUS Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef enum _FSINFOCLASS {
+ FileFsAttributeInformation = 5,
+} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
+
+/* from ntifs.h */
+
+typedef struct _FILE_FS_ATTRIBUTE_INFORMATION {
+ ULONG FileSystemAttributes;
+ LONG MaximumComponentNameLength;
+ ULONG FileSystemNameLength;
+ WCHAR FileSystemName[1];
+} FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION;
+
+extern "C" NTSTATUS NTAPI NtQueryVolumeInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS);
+
+/**
+ * Checks quickly if this is an correct root specification.
+ * Root specs ends with a slash of some kind.
+ *
+ * @returns indicator.
+ * @param pszFsPath Path to check.
+ */
+static bool rtFsIsRoot(const char *pszFsPath)
+{
+ /*
+ * UNC has exactly two slashes..
+ *
+ * Anything else starting with slashe(s) requires
+ * expansion and will have to take the long road.
+ */
+ if (RTPATH_IS_SLASH(pszFsPath[0]))
+ {
+ if ( !RTPATH_IS_SLASH(pszFsPath[1])
+ || RTPATH_IS_SLASH(pszFsPath[2]))
+ return false;
+
+ /* end of machine name */
+ const char *pszSlash = strpbrk(pszFsPath + 2, "\\/");
+ if (!pszSlash)
+ return false;
+
+ /* end of service name. */
+ pszSlash = strpbrk(pszSlash + 1, "\\/");
+ if (!pszSlash)
+ return false;
+
+ return pszSlash[1] == '\0';
+ }
+
+ /*
+ * Ok the other alternative is driver letter.
+ */
+ return pszFsPath[0] >= 'A' && pszFsPath[0] <= 'Z'
+ && pszFsPath[1] == ':'
+ && RTPATH_IS_SLASH(pszFsPath[2])
+ && !pszFsPath[3];
+}
+
+
+
+/**
+ * Finds the root of the specified volume.
+ *
+ * @returns iprt status code.
+ * @param pszFsPath Path within the filesystem. Verified as one byte or more.
+ * @param ppwszFsRoot Where to store the returned string. Free with rtFsFreeRoot(),
+ */
+static int rtFsGetRoot(const char *pszFsPath, PRTUTF16 *ppwszFsRoot)
+{
+ /*
+ * Do straight forward stuff first,
+ */
+ if (rtFsIsRoot(pszFsPath))
+ return RTStrToUtf16(pszFsPath, ppwszFsRoot);
+
+ /*
+ * Expand and add slash (if required).
+ */
+ char szFullPath[RTPATH_MAX];
+ int rc = RTPathAbs(pszFsPath, szFullPath, sizeof(szFullPath));
+ if (RT_FAILURE(rc))
+ return rc;
+ size_t cb = strlen(szFullPath);
+ if (!RTPATH_IS_SLASH(szFullPath[cb - 1]))
+ {
+ AssertReturn(cb + 1 < RTPATH_MAX, VERR_FILENAME_TOO_LONG);
+ szFullPath[cb] = '\\';
+ szFullPath[++cb] = '\0';
+ }
+
+ /*
+ * Convert the path.
+ */
+ rc = RTStrToUtf16(szFullPath, ppwszFsRoot);
+ if (RT_FAILURE(rc))
+ return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
+
+ /*
+ * Walk the path until our proper API is happy or there is no more path left.
+ */
+ PRTUTF16 pwszStart = *ppwszFsRoot;
+ if (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0))
+ {
+ PRTUTF16 pwszEnd = pwszStart + RTUtf16Len(pwszStart);
+ PRTUTF16 pwszMin = pwszStart + 2;
+ do
+ {
+ /* Strip off the last path component. */
+ while (pwszEnd-- > pwszMin)
+ if (RTPATH_IS_SLASH(*pwszEnd))
+ break;
+ AssertReturn(pwszEnd >= pwszMin, VERR_INTERNAL_ERROR); /* leaks, but that's irrelevant for an internal error. */
+ pwszEnd[1] = '\0';
+ } while (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0));
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Frees string returned by rtFsGetRoot().
+ */
+static void rtFsFreeRoot(PRTUTF16 pwszFsRoot)
+{
+ RTUtf16Free(pwszFsRoot);
+}
+
+
+RTR3DECL(int) RTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree,
+ uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ /*
+ * Validate & get valid root path.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
+ PRTUTF16 pwszFsRoot;
+ int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Free and total.
+ */
+ if (pcbTotal || pcbFree)
+ {
+ ULARGE_INTEGER cbTotal;
+ ULARGE_INTEGER cbFree;
+ if (GetDiskFreeSpaceExW(pwszFsRoot, &cbFree, &cbTotal, NULL))
+ {
+ if (pcbTotal)
+ *pcbTotal = cbTotal.QuadPart;
+ if (pcbFree)
+ *pcbFree = cbFree.QuadPart;
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n",
+ pszFsPath, Err, rc));
+ }
+ }
+
+ /*
+ * Block and sector size.
+ */
+ if ( RT_SUCCESS(rc)
+ && (pcbBlock || pcbSector))
+ {
+ DWORD dwDummy1, dwDummy2;
+ DWORD cbSector;
+ DWORD cSectorsPerCluster;
+ if (GetDiskFreeSpaceW(pwszFsRoot, &cSectorsPerCluster, &cbSector, &dwDummy1, &dwDummy2))
+ {
+ if (pcbBlock)
+ *pcbBlock = cbSector * cSectorsPerCluster;
+ if (pcbSector)
+ *pcbSector = cbSector;
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTFsQuerySizes(%s,): GetDiskFreeSpace failed with lasterr %d (%Rrc)\n",
+ pszFsPath, Err, rc));
+ }
+ }
+
+ rtFsFreeRoot(pwszFsRoot);
+ return rc;
+}
+
+
+/**
+ * Query the serial number of a filesystem.
+ *
+ * @returns iprt status code.
+ * @param pszFsPath Path within the mounted filesystem.
+ * @param pu32Serial Where to store the serial number.
+ */
+RTR3DECL(int) RTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial)
+{
+ /*
+ * Validate & get valid root path.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pu32Serial, VERR_INVALID_POINTER);
+ PRTUTF16 pwszFsRoot;
+ int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Do work.
+ */
+ DWORD dwMaxName;
+ DWORD dwFlags;
+ DWORD dwSerial;
+ if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0))
+ *pu32Serial = dwSerial;
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n",
+ pszFsPath, Err, rc));
+ }
+
+ rtFsFreeRoot(pwszFsRoot);
+ return rc;
+}
+
+
+/**
+ * Query the properties of a mounted filesystem.
+ *
+ * @returns iprt status code.
+ * @param pszFsPath Path within the mounted filesystem.
+ * @param pProperties Where to store the properties.
+ */
+RTR3DECL(int) RTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties)
+{
+ /*
+ * Validate & get valid root path.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pProperties, VERR_INVALID_POINTER);
+ PRTUTF16 pwszFsRoot;
+ int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Do work.
+ */
+ DWORD dwMaxName;
+ DWORD dwFlags;
+ DWORD dwSerial;
+ if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0))
+ {
+ memset(pProperties, 0, sizeof(*pProperties));
+ pProperties->cbMaxComponent = dwMaxName;
+ pProperties->fFileCompression = !!(dwFlags & FILE_FILE_COMPRESSION);
+ pProperties->fCompressed = !!(dwFlags & FILE_VOLUME_IS_COMPRESSED);
+ pProperties->fReadOnly = !!(dwFlags & FILE_READ_ONLY_VOLUME);
+ pProperties->fSupportsUnicode = !!(dwFlags & FILE_UNICODE_ON_DISK);
+ pProperties->fCaseSensitive = false; /* win32 is case preserving only */
+ /** @todo r=bird: What about FILE_CASE_SENSITIVE_SEARCH ? Is this set for NTFS
+ * as well perchance? If so, better mention it instead of just setting
+ * fCaseSensitive to false. */
+ pProperties->fRemote = false; /* no idea yet */
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTFsQuerySizes(%s,): GetVolumeInformation failed with lasterr %d (%Rrc)\n",
+ pszFsPath, Err, rc));
+ }
+
+ rtFsFreeRoot(pwszFsRoot);
+ return rc;
+}
+
+
+RTR3DECL(bool) RTFsIsCaseSensitive(const char *pszFsPath)
+{
+ return false;
+}
+
+
+/**
+ * Internal helper for comparing a WCHAR string with a char string.
+ *
+ * @returns @c true if equal, @c false if not.
+ * @param pwsz1 The first string.
+ * @param cch1 The length of the first string, in bytes.
+ * @param psz2 The second string.
+ * @param cch2 The length of the second string.
+ */
+static bool rtFsWinAreEqual(WCHAR const *pwsz1, size_t cch1, const char *psz2, size_t cch2)
+{
+ if (cch1 != cch2 * 2)
+ return false;
+ while (cch2-- > 0)
+ {
+ unsigned ch1 = *pwsz1++;
+ unsigned ch2 = (unsigned char)*psz2++;
+ if (ch1 != ch2)
+ return false;
+ }
+ return true;
+}
+
+
+RTR3DECL(int) RTFsQueryType(const char *pszFsPath, PRTFSTYPE penmType)
+{
+ *penmType = RTFSTYPE_UNKNOWN;
+
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath, VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the path and try open it.
+ */
+ PRTUTF16 pwszFsPath;
+ int rc = RTPathWinFromUtf8(&pwszFsPath, pszFsPath, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile = CreateFileW(pwszFsPath,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Use the NT api directly to get the file system name.
+ */
+ char abBuf[8192];
+ IO_STATUS_BLOCK Ios;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios,
+ abBuf, sizeof(abBuf),
+ FileFsAttributeInformation);
+ if (rcNt >= 0)
+ {
+ PFILE_FS_ATTRIBUTE_INFORMATION pFsAttrInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)abBuf;
+#define IS_FS(szName) \
+ rtFsWinAreEqual(pFsAttrInfo->FileSystemName, pFsAttrInfo->FileSystemNameLength, szName, sizeof(szName) - 1)
+ if (IS_FS("NTFS"))
+ *penmType = RTFSTYPE_NTFS;
+ else if (IS_FS("FAT"))
+ *penmType = RTFSTYPE_FAT;
+ else if (IS_FS("FAT32"))
+ *penmType = RTFSTYPE_FAT;
+ else if (IS_FS("EXFAT"))
+ *penmType = RTFSTYPE_EXFAT;
+ else if (IS_FS("VBoxSharedFolderFS"))
+ *penmType = RTFSTYPE_VBOXSHF;
+#undef IS_FS
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ CloseHandle(hFile);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTPathWinFree(pwszFsPath);
+ }
+ return rc;
+}
diff --git a/src/VBox/Runtime/r3/win/init-win.cpp b/src/VBox/Runtime/r3/win/init-win.cpp
new file mode 100644
index 00000000..92cd2665
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/init-win.cpp
@@ -0,0 +1,919 @@
+/* $Id: init-win.cpp $ */
+/** @file
+ * IPRT - Init Ring-3, Windows Specific Code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/nt/nt-and-windows.h>
+#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
+# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x200
+# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
+#endif
+
+#include "internal-r3-win.h"
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include "../init.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef VOID (WINAPI *PFNGETCURRENTTHREADSTACKLIMITS)(PULONG_PTR puLow, PULONG_PTR puHigh);
+typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI * PFNSETUNHANDLEDEXCEPTIONFILTER)(LPTOP_LEVEL_EXCEPTION_FILTER);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Windows DLL loader protection level. */
+DECL_HIDDEN_DATA(RTR3WINLDRPROT) g_enmWinLdrProt = RTR3WINLDRPROT_NONE;
+/** Our simplified windows version. */
+DECL_HIDDEN_DATA(RTWINOSTYPE) g_enmWinVer = kRTWinOSType_UNKNOWN;
+/** Extended windows version information. */
+DECL_HIDDEN_DATA(OSVERSIONINFOEXW) g_WinOsInfoEx;
+
+/** The native kernel32.dll handle. */
+DECL_HIDDEN_DATA(HMODULE) g_hModKernel32 = NULL;
+/** GetSystemWindowsDirectoryW or GetWindowsDirectoryW (NT4). */
+DECL_HIDDEN_DATA(PFNGETWINSYSDIR) g_pfnGetSystemWindowsDirectoryW = NULL;
+/** The GetCurrentThreadStackLimits API. */
+static PFNGETCURRENTTHREADSTACKLIMITS g_pfnGetCurrentThreadStackLimits = NULL;
+/** The previous unhandled exception filter. */
+static LPTOP_LEVEL_EXCEPTION_FILTER g_pfnUnhandledXcptFilter = NULL;
+/** SystemTimeToTzSpecificLocalTime. */
+DECL_HIDDEN_DATA(decltype(SystemTimeToTzSpecificLocalTime) *) g_pfnSystemTimeToTzSpecificLocalTime = NULL;
+/** CreateWaitableTimerEx . */
+DECL_HIDDEN_DATA(PFNCREATEWAITABLETIMEREX) g_pfnCreateWaitableTimerExW = NULL;
+DECL_HIDDEN_DATA(decltype(GetHandleInformation) *) g_pfnGetHandleInformation = NULL;
+DECL_HIDDEN_DATA(decltype(SetHandleInformation) *) g_pfnSetHandleInformation = NULL;
+DECL_HIDDEN_DATA(decltype(IsDebuggerPresent) *) g_pfnIsDebuggerPresent = NULL;
+DECL_HIDDEN_DATA(decltype(GetSystemTimeAsFileTime) *) g_pfnGetSystemTimeAsFileTime = NULL;
+DECL_HIDDEN_DATA(decltype(GetProcessAffinityMask) *) g_pfnGetProcessAffinityMask = NULL;
+DECL_HIDDEN_DATA(decltype(SetThreadAffinityMask) *) g_pfnSetThreadAffinityMask = NULL;
+DECL_HIDDEN_DATA(decltype(CreateIoCompletionPort) *) g_pfnCreateIoCompletionPort = NULL;
+DECL_HIDDEN_DATA(decltype(GetQueuedCompletionStatus) *) g_pfnGetQueuedCompletionStatus = NULL;
+DECL_HIDDEN_DATA(decltype(PostQueuedCompletionStatus) *) g_pfnPostQueuedCompletionStatus = NULL;
+DECL_HIDDEN_DATA(decltype(IsProcessorFeaturePresent) *) g_pfnIsProcessorFeaturePresent = NULL;
+DECL_HIDDEN_DATA(decltype(SetUnhandledExceptionFilter) *) g_pfnSetUnhandledExceptionFilter = NULL;
+DECL_HIDDEN_DATA(decltype(UnhandledExceptionFilter) *) g_pfnUnhandledExceptionFilter = NULL;
+
+/** The native ntdll.dll handle. */
+DECL_HIDDEN_DATA(HMODULE) g_hModNtDll = NULL;
+/** NtQueryFullAttributesFile */
+DECL_HIDDEN_DATA(PFNNTQUERYFULLATTRIBUTESFILE) g_pfnNtQueryFullAttributesFile = NULL;
+/** NtDuplicateToken (NT 3.51). */
+DECL_HIDDEN_DATA(PFNNTDUPLICATETOKEN) g_pfnNtDuplicateToken = NULL;
+/** NtAlertThread (NT 3.51). */
+DECL_HIDDEN_DATA(decltype(NtAlertThread) *) g_pfnNtAlertThread = NULL;
+
+/** Either ws2_32.dll (NT4+) or wsock32.dll (NT3.x). */
+DECL_HIDDEN_DATA(HMODULE) g_hModWinSock = NULL;
+/** Set if we're dealing with old winsock. */
+DECL_HIDDEN_DATA(bool) g_fOldWinSock = false;
+/** WSAStartup */
+DECL_HIDDEN_DATA(PFNWSASTARTUP) g_pfnWSAStartup = NULL;
+/** WSACleanup */
+DECL_HIDDEN_DATA(PFNWSACLEANUP) g_pfnWSACleanup = NULL;
+/** Pointner to WSAGetLastError (for RTErrVarsSave). */
+DECL_HIDDEN_DATA(PFNWSAGETLASTERROR) g_pfnWSAGetLastError = NULL;
+/** Pointner to WSASetLastError (for RTErrVarsRestore). */
+DECL_HIDDEN_DATA(PFNWSASETLASTERROR) g_pfnWSASetLastError = NULL;
+/** WSACreateEvent */
+DECL_HIDDEN_DATA(PFNWSACREATEEVENT) g_pfnWSACreateEvent = NULL;
+/** WSACloseEvent */
+DECL_HIDDEN_DATA(PFNWSACLOSEEVENT) g_pfnWSACloseEvent = NULL;
+/** WSASetEvent */
+DECL_HIDDEN_DATA(PFNWSASETEVENT) g_pfnWSASetEvent = NULL;
+/** WSAEventSelect */
+DECL_HIDDEN_DATA(PFNWSAEVENTSELECT) g_pfnWSAEventSelect = NULL;
+/** WSAEnumNetworkEvents */
+DECL_HIDDEN_DATA(PFNWSAENUMNETWORKEVENTS) g_pfnWSAEnumNetworkEvents = NULL;
+/** WSASocketW */
+DECL_HIDDEN_DATA(PFNWSASOCKETW) g_pfnWSASocketW = NULL;
+/** WSASend */
+DECL_HIDDEN_DATA(PFNWSASEND) g_pfnWSASend = NULL;
+/** socket */
+DECL_HIDDEN_DATA(PFNWINSOCKSOCKET) g_pfnsocket = NULL;
+/** closesocket */
+DECL_HIDDEN_DATA(PFNWINSOCKCLOSESOCKET) g_pfnclosesocket = NULL;
+/** recv */
+DECL_HIDDEN_DATA(PFNWINSOCKRECV) g_pfnrecv = NULL;
+/** send */
+DECL_HIDDEN_DATA(PFNWINSOCKSEND) g_pfnsend = NULL;
+/** recvfrom */
+DECL_HIDDEN_DATA(PFNWINSOCKRECVFROM) g_pfnrecvfrom = NULL;
+/** sendto */
+DECL_HIDDEN_DATA(PFNWINSOCKSENDTO) g_pfnsendto = NULL;
+/** bind */
+DECL_HIDDEN_DATA(PFNWINSOCKBIND) g_pfnbind = NULL;
+/** listen */
+DECL_HIDDEN_DATA(PFNWINSOCKLISTEN) g_pfnlisten = NULL;
+/** accept */
+DECL_HIDDEN_DATA(PFNWINSOCKACCEPT) g_pfnaccept = NULL;
+/** connect */
+DECL_HIDDEN_DATA(PFNWINSOCKCONNECT) g_pfnconnect = NULL;
+/** shutdown */
+DECL_HIDDEN_DATA(PFNWINSOCKSHUTDOWN) g_pfnshutdown = NULL;
+/** getsockopt */
+DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKOPT) g_pfngetsockopt = NULL;
+/** setsockopt */
+DECL_HIDDEN_DATA(PFNWINSOCKSETSOCKOPT) g_pfnsetsockopt = NULL;
+/** ioctlsocket */
+DECL_HIDDEN_DATA(PFNWINSOCKIOCTLSOCKET) g_pfnioctlsocket = NULL;
+/** getpeername */
+DECL_HIDDEN_DATA(PFNWINSOCKGETPEERNAME) g_pfngetpeername = NULL;
+/** getsockname */
+DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKNAME) g_pfngetsockname = NULL;
+/** __WSAFDIsSet */
+DECL_HIDDEN_DATA(PFNWINSOCK__WSAFDISSET) g_pfn__WSAFDIsSet = NULL;
+/** select */
+DECL_HIDDEN_DATA(PFNWINSOCKSELECT) g_pfnselect = NULL;
+/** gethostbyname */
+DECL_HIDDEN_DATA(PFNWINSOCKGETHOSTBYNAME) g_pfngethostbyname = NULL;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static LONG CALLBACK rtR3WinUnhandledXcptFilter(PEXCEPTION_POINTERS);
+
+
+/**
+ * Translates OSVERSIONINOFEX into a Windows OS type.
+ *
+ * @returns The Windows OS type.
+ * @param pOSInfoEx The OS info returned by Windows.
+ *
+ * @remarks This table has been assembled from Usenet postings, personal
+ * observations, and reading other people's code. Please feel
+ * free to add to it or correct it.
+ * <pre>
+ dwPlatFormID dwMajorVersion dwMinorVersion dwBuildNumber
+95 1 4 0 950
+95 SP1 1 4 0 >950 && <=1080
+95 OSR2 1 4 <10 >1080
+98 1 4 10 1998
+98 SP1 1 4 10 >1998 && <2183
+98 SE 1 4 10 >=2183
+ME 1 4 90 3000
+
+NT 3.51 2 3 51 1057
+NT 4 2 4 0 1381
+2000 2 5 0 2195
+XP 2 5 1 2600
+2003 2 5 2 3790
+Vista 2 6 0
+
+CE 1.0 3 1 0
+CE 2.0 3 2 0
+CE 2.1 3 2 1
+CE 3.0 3 3 0
+</pre>
+ */
+static RTWINOSTYPE rtR3InitWinSimplifiedVersion(OSVERSIONINFOEXW const *pOSInfoEx)
+{
+ RTWINOSTYPE enmVer = kRTWinOSType_UNKNOWN;
+ BYTE const bProductType = pOSInfoEx->wProductType;
+ DWORD const dwPlatformId = pOSInfoEx->dwPlatformId;
+ DWORD const dwMinorVersion = pOSInfoEx->dwMinorVersion;
+ DWORD const dwMajorVersion = pOSInfoEx->dwMajorVersion;
+ DWORD const dwBuildNumber = pOSInfoEx->dwBuildNumber & 0xFFFF; /* Win 9x needs this. */
+
+ if ( dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
+ && dwMajorVersion == 4)
+ {
+ if ( dwMinorVersion < 10
+ && dwBuildNumber == 950)
+ enmVer = kRTWinOSType_95;
+ else if ( dwMinorVersion < 10
+ && dwBuildNumber > 950
+ && dwBuildNumber <= 1080)
+ enmVer = kRTWinOSType_95SP1;
+ else if ( dwMinorVersion < 10
+ && dwBuildNumber > 1080)
+ enmVer = kRTWinOSType_95OSR2;
+ else if ( dwMinorVersion == 10
+ && dwBuildNumber == 1998)
+ enmVer = kRTWinOSType_98;
+ else if ( dwMinorVersion == 10
+ && dwBuildNumber > 1998
+ && dwBuildNumber < 2183)
+ enmVer = kRTWinOSType_98SP1;
+ else if ( dwMinorVersion == 10
+ && dwBuildNumber >= 2183)
+ enmVer = kRTWinOSType_98SE;
+ else if (dwMinorVersion == 90)
+ enmVer = kRTWinOSType_ME;
+ }
+ else if (dwPlatformId == VER_PLATFORM_WIN32_NT)
+ {
+ if (dwMajorVersion == 3)
+ {
+ if ( dwMinorVersion < 50)
+ enmVer = kRTWinOSType_NT310;
+ else if (dwMinorVersion == 50)
+ enmVer = kRTWinOSType_NT350;
+ else
+ enmVer = kRTWinOSType_NT351;
+ }
+ else if (dwMajorVersion == 4)
+ enmVer = kRTWinOSType_NT4;
+ else if (dwMajorVersion == 5)
+ {
+ if (dwMinorVersion == 0)
+ enmVer = kRTWinOSType_2K;
+ else if (dwMinorVersion == 1)
+ enmVer = kRTWinOSType_XP;
+ else
+ enmVer = kRTWinOSType_2003;
+ }
+ else if (dwMajorVersion == 6)
+ {
+ if (dwMinorVersion == 0)
+ enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2008 : kRTWinOSType_VISTA;
+ else if (dwMinorVersion == 1)
+ enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2008R2 : kRTWinOSType_7;
+ else if (dwMinorVersion == 2)
+ enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2012 : kRTWinOSType_8;
+ else if (dwMinorVersion == 3)
+ enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2012R2 : kRTWinOSType_81;
+ else if (dwMinorVersion == 4)
+ enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2016 : kRTWinOSType_10;
+ else
+ enmVer = kRTWinOSType_NT_UNKNOWN;
+ }
+ else if (dwMajorVersion == 10)
+ {
+ if (dwMinorVersion == 0)
+ {
+ /* The version detection for server 2019, server 2022 and windows 11
+ are by build number. Stupid, stupid, Microsoft. */
+ if (bProductType == VER_NT_WORKSTATION)
+ enmVer = dwBuildNumber >= 22000 ? kRTWinOSType_11 : kRTWinOSType_10;
+ else
+ enmVer = dwBuildNumber >= 20348 ? kRTWinOSType_2022
+ : dwBuildNumber >= 17763 ? kRTWinOSType_2019 : kRTWinOSType_2016;
+ }
+ else
+ enmVer = kRTWinOSType_NT_UNKNOWN;
+ }
+ else
+ enmVer = kRTWinOSType_NT_UNKNOWN;
+ }
+
+ return enmVer;
+}
+
+
+/**
+ * Initializes the global variables related to windows version.
+ */
+static void rtR3InitWindowsVersion(void)
+{
+ Assert(g_hModNtDll != NULL);
+
+ /*
+ * ASSUMES OSVERSIONINFOEX starts with the exact same layout as OSVERSIONINFO (safe).
+ */
+ AssertCompileMembersSameSizeAndOffset(OSVERSIONINFOEX, szCSDVersion, OSVERSIONINFO, szCSDVersion);
+ AssertCompileMemberOffset(OSVERSIONINFOEX, wServicePackMajor, sizeof(OSVERSIONINFO));
+
+ /*
+ * Use the NT version of RtlGetVersion (since w2k) so we don't get fooled
+ * by the standard compatibility shims. (Sandboxes may still fool us.)
+ *
+ * Note! This API was added in windows 2000 together with the extended
+ * version info structure (OSVERSIONINFOEXW), so there is no need
+ * to retry with the smaller version (OSVERSIONINFOW).
+ */
+ RT_ZERO(g_WinOsInfoEx);
+ g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+
+ LONG (__stdcall *pfnRtlGetVersion)(OSVERSIONINFOEXW *);
+ *(FARPROC *)&pfnRtlGetVersion = GetProcAddress(g_hModNtDll, "RtlGetVersion");
+ LONG rcNt = -1;
+ if (pfnRtlGetVersion)
+ rcNt = pfnRtlGetVersion(&g_WinOsInfoEx);
+ if (rcNt != 0)
+ {
+ /*
+ * Couldn't find it or it failed, try the windows version of the API.
+ * The GetVersionExW API was added in NT 3.51, however only the small
+ * structure version existed till windows 2000. We'll try the larger
+ * structure version first, anyway, just in case.
+ */
+ RT_ZERO(g_WinOsInfoEx);
+ g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+
+ BOOL (__stdcall *pfnGetVersionExW)(OSVERSIONINFOW *);
+ *(FARPROC *)&pfnGetVersionExW = GetProcAddress(g_hModKernel32, "GetVersionExW");
+
+ if (!pfnGetVersionExW || !pfnGetVersionExW((POSVERSIONINFOW)&g_WinOsInfoEx))
+ {
+ /*
+ * If that didn't work either, just get the basic version bits.
+ */
+ RT_ZERO(g_WinOsInfoEx);
+ g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
+ if (pfnGetVersionExW && pfnGetVersionExW((POSVERSIONINFOW)&g_WinOsInfoEx))
+ Assert(g_WinOsInfoEx.dwPlatformId != VER_PLATFORM_WIN32_NT || g_WinOsInfoEx.dwMajorVersion < 5);
+ else
+ {
+ /*
+ * Okay, nothing worked, so use GetVersion.
+ *
+ * This should only happen if we're on NT 3.1 or NT 3.50.
+ * It should never happen for 64-bit builds.
+ */
+#ifdef RT_ARCH_X86
+ RT_ZERO(g_WinOsInfoEx);
+ DWORD const dwVersion = GetVersion();
+
+ /* Common fields: */
+ g_WinOsInfoEx.dwMajorVersion = dwVersion & 0xff;
+ g_WinOsInfoEx.dwMinorVersion = (dwVersion >> 8) & 0xff;
+ if (!(dwVersion & RT_BIT_32(31)))
+ g_WinOsInfoEx.dwBuildNumber = dwVersion >> 16;
+ else
+ g_WinOsInfoEx.dwBuildNumber = 511;
+ g_WinOsInfoEx.dwPlatformId = VER_PLATFORM_WIN32_NT;
+ /** @todo get CSD from registry. */
+#else
+ AssertBreakpoint();
+ RT_ZERO(g_WinOsInfoEx);
+#endif
+ }
+
+#ifdef RT_ARCH_X86
+ /*
+ * Fill in some of the extended info too.
+ */
+ g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); /* Pretend. */
+ g_WinOsInfoEx.wProductType = VER_NT_WORKSTATION;
+ NT_PRODUCT_TYPE enmProdType = NtProductWinNt;
+ if (RtlGetNtProductType(&enmProdType))
+ g_WinOsInfoEx.wProductType = (BYTE)enmProdType;
+ /** @todo parse the CSD string to figure that version. */
+#endif
+ }
+ }
+
+ if (g_WinOsInfoEx.dwOSVersionInfoSize)
+ g_enmWinVer = rtR3InitWinSimplifiedVersion(&g_WinOsInfoEx);
+}
+
+
+/**
+ * Resolves the winsock error APIs.
+ */
+static void rtR3InitWinSockApis(void)
+{
+ /*
+ * Try get ws2_32.dll, then try load it, then finally fall back to the old
+ * wsock32.dll. We use RTLdrLoadSystem to the loading as it has all the fancy
+ * logic for safely doing that.
+ */
+ g_hModWinSock = GetModuleHandleW(L"ws2_32.dll");
+ if (g_hModWinSock == NULL)
+ {
+ RTLDRMOD hLdrMod;
+ int rc = RTLdrLoadSystem("ws2_32.dll", true /*fNoUnload*/, &hLdrMod);
+ if (RT_FAILURE(rc))
+ {
+ rc = RTLdrLoadSystem("wsock32.dll", true /*fNoUnload*/, &hLdrMod);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("rc=%Rrc\n", rc));
+ return;
+ }
+ g_fOldWinSock = true;
+ }
+ g_hModWinSock = (HMODULE)RTLdrGetNativeHandle(hLdrMod);
+ RTLdrClose(hLdrMod);
+ }
+
+ g_pfnWSAStartup = (decltype(g_pfnWSAStartup)) GetProcAddress(g_hModWinSock, "WSAStartup");
+ g_pfnWSACleanup = (decltype(g_pfnWSACleanup)) GetProcAddress(g_hModWinSock, "WSACleanup");
+ g_pfnWSAGetLastError = (decltype(g_pfnWSAGetLastError)) GetProcAddress(g_hModWinSock, "WSAGetLastError");
+ g_pfnWSASetLastError = (decltype(g_pfnWSASetLastError)) GetProcAddress(g_hModWinSock, "WSASetLastError");
+ g_pfnWSACreateEvent = (decltype(g_pfnWSACreateEvent)) GetProcAddress(g_hModWinSock, "WSACreateEvent");
+ g_pfnWSACloseEvent = (decltype(g_pfnWSACloseEvent)) GetProcAddress(g_hModWinSock, "WSACloseEvent");
+ g_pfnWSASetEvent = (decltype(g_pfnWSASetEvent)) GetProcAddress(g_hModWinSock, "WSASetEvent");
+ g_pfnWSAEventSelect = (decltype(g_pfnWSAEventSelect)) GetProcAddress(g_hModWinSock, "WSAEventSelect");
+ g_pfnWSAEnumNetworkEvents = (decltype(g_pfnWSAEnumNetworkEvents))GetProcAddress(g_hModWinSock,"WSAEnumNetworkEvents");
+ g_pfnWSASocketW = (decltype(g_pfnWSASocketW)) GetProcAddress(g_hModWinSock, "WSASocketW");
+ g_pfnWSASend = (decltype(g_pfnWSASend)) GetProcAddress(g_hModWinSock, "WSASend");
+ g_pfnsocket = (decltype(g_pfnsocket)) GetProcAddress(g_hModWinSock, "socket");
+ g_pfnclosesocket = (decltype(g_pfnclosesocket)) GetProcAddress(g_hModWinSock, "closesocket");
+ g_pfnrecv = (decltype(g_pfnrecv)) GetProcAddress(g_hModWinSock, "recv");
+ g_pfnsend = (decltype(g_pfnsend)) GetProcAddress(g_hModWinSock, "send");
+ g_pfnrecvfrom = (decltype(g_pfnrecvfrom)) GetProcAddress(g_hModWinSock, "recvfrom");
+ g_pfnsendto = (decltype(g_pfnsendto)) GetProcAddress(g_hModWinSock, "sendto");
+ g_pfnbind = (decltype(g_pfnbind)) GetProcAddress(g_hModWinSock, "bind");
+ g_pfnlisten = (decltype(g_pfnlisten)) GetProcAddress(g_hModWinSock, "listen");
+ g_pfnaccept = (decltype(g_pfnaccept)) GetProcAddress(g_hModWinSock, "accept");
+ g_pfnconnect = (decltype(g_pfnconnect)) GetProcAddress(g_hModWinSock, "connect");
+ g_pfnshutdown = (decltype(g_pfnshutdown)) GetProcAddress(g_hModWinSock, "shutdown");
+ g_pfngetsockopt = (decltype(g_pfngetsockopt)) GetProcAddress(g_hModWinSock, "getsockopt");
+ g_pfnsetsockopt = (decltype(g_pfnsetsockopt)) GetProcAddress(g_hModWinSock, "setsockopt");
+ g_pfnioctlsocket = (decltype(g_pfnioctlsocket)) GetProcAddress(g_hModWinSock, "ioctlsocket");
+ g_pfngetpeername = (decltype(g_pfngetpeername)) GetProcAddress(g_hModWinSock, "getpeername");
+ g_pfngetsockname = (decltype(g_pfngetsockname)) GetProcAddress(g_hModWinSock, "getsockname");
+ g_pfn__WSAFDIsSet = (decltype(g_pfn__WSAFDIsSet)) GetProcAddress(g_hModWinSock, "__WSAFDIsSet");
+ g_pfnselect = (decltype(g_pfnselect)) GetProcAddress(g_hModWinSock, "select");
+ g_pfngethostbyname = (decltype(g_pfngethostbyname)) GetProcAddress(g_hModWinSock, "gethostbyname");
+
+ Assert(g_pfnWSAStartup);
+ Assert(g_pfnWSACleanup);
+ Assert(g_pfnWSAGetLastError);
+ Assert(g_pfnWSASetLastError);
+ Assert(g_pfnWSACreateEvent || g_fOldWinSock);
+ Assert(g_pfnWSACloseEvent || g_fOldWinSock);
+ Assert(g_pfnWSASetEvent || g_fOldWinSock);
+ Assert(g_pfnWSAEventSelect || g_fOldWinSock);
+ Assert(g_pfnWSAEnumNetworkEvents || g_fOldWinSock);
+ Assert(g_pfnWSASocketW || g_fOldWinSock);
+ Assert(g_pfnWSASend || g_fOldWinSock);
+ Assert(g_pfnsocket);
+ Assert(g_pfnclosesocket);
+ Assert(g_pfnrecv);
+ Assert(g_pfnsend);
+ Assert(g_pfnrecvfrom);
+ Assert(g_pfnsendto);
+ Assert(g_pfnbind);
+ Assert(g_pfnlisten);
+ Assert(g_pfnaccept);
+ Assert(g_pfnconnect);
+ Assert(g_pfnshutdown);
+ Assert(g_pfngetsockopt);
+ Assert(g_pfnsetsockopt);
+ Assert(g_pfnioctlsocket);
+ Assert(g_pfngetpeername);
+ Assert(g_pfngetsockname);
+ Assert(g_pfn__WSAFDIsSet);
+ Assert(g_pfnselect);
+ Assert(g_pfngethostbyname);
+}
+
+
+static int rtR3InitNativeObtrusiveWorker(uint32_t fFlags)
+{
+ /*
+ * Disable error popups.
+ */
+ UINT fOldErrMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
+ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX | fOldErrMode);
+
+ /*
+ * Restrict DLL searching for the process on windows versions which allow
+ * us to do so.
+ * - The first trick works on XP SP1+ and disables the searching of the
+ * current directory.
+ * - The second trick is W7 w/ KB2533623 and W8+, it restrict the DLL
+ * searching to the application directory (except when
+ * RTR3INIT_FLAGS_STANDALONE_APP is given) and the System32 directory.
+ */
+ int rc = VINF_SUCCESS;
+
+ typedef BOOL (WINAPI *PFNSETDLLDIRECTORY)(LPCWSTR);
+ PFNSETDLLDIRECTORY pfnSetDllDir = (PFNSETDLLDIRECTORY)GetProcAddress(g_hModKernel32, "SetDllDirectoryW");
+ if (pfnSetDllDir)
+ {
+ if (pfnSetDllDir(L""))
+ g_enmWinLdrProt = RTR3WINLDRPROT_NO_CWD;
+ else
+ rc = VERR_INTERNAL_ERROR_3;
+ }
+
+ /** @bugref{6861} Observed GUI issues on Vista (32-bit and 64-bit) when using
+ * SetDefaultDllDirectories.
+ * @bugref{8194} Try use SetDefaultDllDirectories on Vista for standalone apps
+ * despite potential GUI issues. */
+ if ( g_enmWinVer > kRTWinOSType_VISTA
+ || (fFlags & RTR3INIT_FLAGS_STANDALONE_APP))
+ {
+ typedef BOOL(WINAPI *PFNSETDEFAULTDLLDIRECTORIES)(DWORD);
+ PFNSETDEFAULTDLLDIRECTORIES pfnSetDefDllDirs;
+ pfnSetDefDllDirs = (PFNSETDEFAULTDLLDIRECTORIES)GetProcAddress(g_hModKernel32, "SetDefaultDllDirectories");
+ if (pfnSetDefDllDirs)
+ {
+ DWORD fDllDirs = LOAD_LIBRARY_SEARCH_SYSTEM32;
+ if (!(fFlags & RTR3INIT_FLAGS_STANDALONE_APP))
+ fDllDirs |= LOAD_LIBRARY_SEARCH_APPLICATION_DIR;
+ if (pfnSetDefDllDirs(fDllDirs))
+ g_enmWinLdrProt = fDllDirs & LOAD_LIBRARY_SEARCH_APPLICATION_DIR ? RTR3WINLDRPROT_SAFE : RTR3WINLDRPROT_SAFER;
+ else if (RT_SUCCESS(rc))
+ rc = VERR_INTERNAL_ERROR_4;
+ }
+ }
+
+ /*
+ * Register an unhandled exception callback if we can.
+ */
+ g_pfnGetCurrentThreadStackLimits = (PFNGETCURRENTTHREADSTACKLIMITS)GetProcAddress(g_hModKernel32, "GetCurrentThreadStackLimits");
+ g_pfnSetUnhandledExceptionFilter = (decltype(SetUnhandledExceptionFilter) *)GetProcAddress(g_hModKernel32, "SetUnhandledExceptionFilter");
+ g_pfnUnhandledExceptionFilter = (decltype(UnhandledExceptionFilter) *) GetProcAddress(g_hModKernel32, "UnhandledExceptionFilter");
+ if (g_pfnSetUnhandledExceptionFilter && !g_pfnUnhandledXcptFilter)
+ {
+ g_pfnUnhandledXcptFilter = g_pfnSetUnhandledExceptionFilter(rtR3WinUnhandledXcptFilter);
+ AssertStmt(g_pfnUnhandledXcptFilter != rtR3WinUnhandledXcptFilter, g_pfnUnhandledXcptFilter = NULL);
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR3InitNativeFirst(uint32_t fFlags)
+{
+ /*
+ * Make sure we've got the handles of the two main Windows NT dlls.
+ */
+ g_hModKernel32 = GetModuleHandleW(L"kernel32.dll");
+ if (g_hModKernel32 == NULL)
+ return VERR_INTERNAL_ERROR_2;
+ g_hModNtDll = GetModuleHandleW(L"ntdll.dll");
+ if (g_hModNtDll == NULL)
+ return VERR_INTERNAL_ERROR_2;
+
+ rtR3InitWindowsVersion();
+
+ int rc = VINF_SUCCESS;
+ if (!(fFlags & RTR3INIT_FLAGS_UNOBTRUSIVE))
+ rc = rtR3InitNativeObtrusiveWorker(fFlags);
+
+ /*
+ * Resolve some kernel32.dll APIs we may need but aren't necessarily
+ * present in older windows versions.
+ */
+ g_pfnGetSystemWindowsDirectoryW = (PFNGETWINSYSDIR)GetProcAddress(g_hModKernel32, "GetSystemWindowsDirectoryW");
+ if (g_pfnGetSystemWindowsDirectoryW)
+ g_pfnGetSystemWindowsDirectoryW = (PFNGETWINSYSDIR)GetProcAddress(g_hModKernel32, "GetWindowsDirectoryW");
+ g_pfnSystemTimeToTzSpecificLocalTime = (decltype(SystemTimeToTzSpecificLocalTime) *)GetProcAddress(g_hModKernel32, "SystemTimeToTzSpecificLocalTime");
+ g_pfnCreateWaitableTimerExW = (PFNCREATEWAITABLETIMEREX) GetProcAddress(g_hModKernel32, "CreateWaitableTimerExW");
+ g_pfnGetHandleInformation = (decltype(GetHandleInformation) *) GetProcAddress(g_hModKernel32, "GetHandleInformation");
+ g_pfnSetHandleInformation = (decltype(SetHandleInformation) *) GetProcAddress(g_hModKernel32, "SetHandleInformation");
+ g_pfnIsDebuggerPresent = (decltype(IsDebuggerPresent) *) GetProcAddress(g_hModKernel32, "IsDebuggerPresent");
+ g_pfnGetSystemTimeAsFileTime = (decltype(GetSystemTimeAsFileTime) *) GetProcAddress(g_hModKernel32, "GetSystemTimeAsFileTime");
+ g_pfnGetProcessAffinityMask = (decltype(GetProcessAffinityMask) *) GetProcAddress(g_hModKernel32, "GetProcessAffinityMask");
+ g_pfnSetThreadAffinityMask = (decltype(SetThreadAffinityMask) *) GetProcAddress(g_hModKernel32, "SetThreadAffinityMask");
+ g_pfnCreateIoCompletionPort = (decltype(CreateIoCompletionPort) *) GetProcAddress(g_hModKernel32, "CreateIoCompletionPort");
+ g_pfnGetQueuedCompletionStatus = (decltype(GetQueuedCompletionStatus) *) GetProcAddress(g_hModKernel32, "GetQueuedCompletionStatus");
+ g_pfnPostQueuedCompletionStatus = (decltype(PostQueuedCompletionStatus) *)GetProcAddress(g_hModKernel32, "PostQueuedCompletionStatus");
+ g_pfnIsProcessorFeaturePresent = (decltype(IsProcessorFeaturePresent) *) GetProcAddress(g_hModKernel32, "IsProcessorFeaturePresent");
+
+ Assert(g_pfnGetHandleInformation || g_enmWinVer < kRTWinOSType_NT351);
+ Assert(g_pfnSetHandleInformation || g_enmWinVer < kRTWinOSType_NT351);
+ Assert(g_pfnIsDebuggerPresent || g_enmWinVer < kRTWinOSType_NT4);
+ Assert(g_pfnGetSystemTimeAsFileTime || g_enmWinVer < kRTWinOSType_NT4);
+ Assert(g_pfnGetProcessAffinityMask || g_enmWinVer < kRTWinOSType_NT350);
+ Assert(g_pfnSetThreadAffinityMask || g_enmWinVer < kRTWinOSType_NT350);
+ Assert(g_pfnCreateIoCompletionPort || g_enmWinVer < kRTWinOSType_NT350);
+ Assert(g_pfnGetQueuedCompletionStatus || g_enmWinVer < kRTWinOSType_NT350);
+ Assert(g_pfnPostQueuedCompletionStatus || g_enmWinVer < kRTWinOSType_NT350);
+ Assert(g_pfnIsProcessorFeaturePresent || g_enmWinVer < kRTWinOSType_NT4);
+
+ /*
+ * Resolve some ntdll.dll APIs that weren't there in early NT versions.
+ */
+ g_pfnNtQueryFullAttributesFile = (PFNNTQUERYFULLATTRIBUTESFILE)GetProcAddress(g_hModNtDll, "NtQueryFullAttributesFile");
+ g_pfnNtDuplicateToken = (PFNNTDUPLICATETOKEN)GetProcAddress( g_hModNtDll, "NtDuplicateToken");
+ g_pfnNtAlertThread = (decltype(NtAlertThread) *)GetProcAddress( g_hModNtDll, "NtAlertThread");
+
+ /*
+ * Resolve the winsock error getter and setter so assertions can save those too.
+ */
+ rtR3InitWinSockApis();
+
+ return rc;
+}
+
+
+DECLHIDDEN(void) rtR3InitNativeObtrusive(uint32_t fFlags)
+{
+ rtR3InitNativeObtrusiveWorker(fFlags);
+}
+
+
+DECLHIDDEN(int) rtR3InitNativeFinal(uint32_t fFlags)
+{
+ /* Nothing to do here. */
+ RT_NOREF_PV(fFlags);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Unhandled exception filter callback.
+ *
+ * Will try log stuff.
+ */
+static LONG CALLBACK rtR3WinUnhandledXcptFilter(PEXCEPTION_POINTERS pPtrs)
+{
+ /*
+ * Try get the logger and log exception details.
+ *
+ * Note! We'll be using RTLogLoggerWeak for now, though we should probably add
+ * a less deadlock prone API here and gives up pretty fast if it
+ * cannot get the lock...
+ */
+ PRTLOGGER pLogger = RTLogRelGetDefaultInstanceWeak();
+ if (!pLogger)
+ pLogger = RTLogGetDefaultInstanceWeak();
+ if (pLogger)
+ {
+ RTLogLoggerWeak(pLogger, NULL, "\n!!! rtR3WinUnhandledXcptFilter caught an exception on thread %p in %u !!!\n",
+ RTThreadNativeSelf(), RTProcSelf());
+
+ /*
+ * Dump the exception record.
+ */
+ uintptr_t uXcptPC = 0;
+ PEXCEPTION_RECORD pXcptRec = RT_VALID_PTR(pPtrs) && RT_VALID_PTR(pPtrs->ExceptionRecord) ? pPtrs->ExceptionRecord : NULL;
+ if (pXcptRec)
+ {
+ RTLogLoggerWeak(pLogger, NULL, "\nExceptionCode=%#010x ExceptionFlags=%#010x ExceptionAddress=%p\n",
+ pXcptRec->ExceptionCode, pXcptRec->ExceptionFlags, pXcptRec->ExceptionAddress);
+ for (uint32_t i = 0; i < RT_MIN(pXcptRec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS); i++)
+ RTLogLoggerWeak(pLogger, NULL, "ExceptionInformation[%d]=%p\n", i, pXcptRec->ExceptionInformation[i]);
+ uXcptPC = (uintptr_t)pXcptRec->ExceptionAddress;
+
+ /* Nested? Display one level only. */
+ PEXCEPTION_RECORD pNestedRec = pXcptRec->ExceptionRecord;
+ if (RT_VALID_PTR(pNestedRec))
+ {
+ RTLogLoggerWeak(pLogger, NULL, "Nested: ExceptionCode=%#010x ExceptionFlags=%#010x ExceptionAddress=%p (nested %p)\n",
+ pNestedRec->ExceptionCode, pNestedRec->ExceptionFlags, pNestedRec->ExceptionAddress,
+ pNestedRec->ExceptionRecord);
+ for (uint32_t i = 0; i < RT_MIN(pNestedRec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS); i++)
+ RTLogLoggerWeak(pLogger, NULL, "Nested: ExceptionInformation[%d]=%p\n", i, pNestedRec->ExceptionInformation[i]);
+ uXcptPC = (uintptr_t)pNestedRec->ExceptionAddress;
+ }
+ }
+
+ /*
+ * Dump the context record.
+ */
+ volatile char szMarker[] = "stackmarker";
+ uintptr_t uXcptSP = (uintptr_t)&szMarker[0];
+ PCONTEXT pXcptCtx = RT_VALID_PTR(pPtrs) && RT_VALID_PTR(pPtrs->ContextRecord) ? pPtrs->ContextRecord : NULL;
+ if (pXcptCtx)
+ {
+#ifdef RT_ARCH_AMD64
+ RTLogLoggerWeak(pLogger, NULL, "\ncs:rip=%04x:%016RX64\n", pXcptCtx->SegCs, pXcptCtx->Rip);
+ RTLogLoggerWeak(pLogger, NULL, "ss:rsp=%04x:%016RX64 rbp=%016RX64\n", pXcptCtx->SegSs, pXcptCtx->Rsp, pXcptCtx->Rbp);
+ RTLogLoggerWeak(pLogger, NULL, "rax=%016RX64 rcx=%016RX64 rdx=%016RX64 rbx=%016RX64\n",
+ pXcptCtx->Rax, pXcptCtx->Rcx, pXcptCtx->Rdx, pXcptCtx->Rbx);
+ RTLogLoggerWeak(pLogger, NULL, "rsi=%016RX64 rdi=%016RX64 rsp=%016RX64 rbp=%016RX64\n",
+ pXcptCtx->Rsi, pXcptCtx->Rdi, pXcptCtx->Rsp, pXcptCtx->Rbp);
+ RTLogLoggerWeak(pLogger, NULL, "r8 =%016RX64 r9 =%016RX64 r10=%016RX64 r11=%016RX64\n",
+ pXcptCtx->R8, pXcptCtx->R9, pXcptCtx->R10, pXcptCtx->R11);
+ RTLogLoggerWeak(pLogger, NULL, "r12=%016RX64 r13=%016RX64 r14=%016RX64 r15=%016RX64\n",
+ pXcptCtx->R12, pXcptCtx->R13, pXcptCtx->R14, pXcptCtx->R15);
+ RTLogLoggerWeak(pLogger, NULL, "ds=%04x es=%04x fs=%04x gs=%04x eflags=%08x\n",
+ pXcptCtx->SegDs, pXcptCtx->SegEs, pXcptCtx->SegFs, pXcptCtx->SegGs, pXcptCtx->EFlags);
+ RTLogLoggerWeak(pLogger, NULL, "p1home=%016RX64 p2home=%016RX64 pe3home=%016RX64\n",
+ pXcptCtx->P1Home, pXcptCtx->P2Home, pXcptCtx->P3Home);
+ RTLogLoggerWeak(pLogger, NULL, "p4home=%016RX64 p5home=%016RX64 pe6home=%016RX64\n",
+ pXcptCtx->P4Home, pXcptCtx->P5Home, pXcptCtx->P6Home);
+ RTLogLoggerWeak(pLogger, NULL, " LastBranchToRip=%016RX64 LastBranchFromRip=%016RX64\n",
+ pXcptCtx->LastBranchToRip, pXcptCtx->LastBranchFromRip);
+ RTLogLoggerWeak(pLogger, NULL, "LastExceptionToRip=%016RX64 LastExceptionFromRip=%016RX64\n",
+ pXcptCtx->LastExceptionToRip, pXcptCtx->LastExceptionFromRip);
+ uXcptSP = pXcptCtx->Rsp;
+ uXcptPC = pXcptCtx->Rip;
+
+#elif defined(RT_ARCH_X86)
+ RTLogLoggerWeak(pLogger, NULL, "\ncs:eip=%04x:%08RX32\n", pXcptCtx->SegCs, pXcptCtx->Eip);
+ RTLogLoggerWeak(pLogger, NULL, "ss:esp=%04x:%08RX32 ebp=%08RX32\n", pXcptCtx->SegSs, pXcptCtx->Esp, pXcptCtx->Ebp);
+ RTLogLoggerWeak(pLogger, NULL, "eax=%08RX32 ecx=%08RX32 edx=%08RX32 ebx=%08RX32\n",
+ pXcptCtx->Eax, pXcptCtx->Ecx, pXcptCtx->Edx, pXcptCtx->Ebx);
+ RTLogLoggerWeak(pLogger, NULL, "esi=%08RX32 edi=%08RX32 esp=%08RX32 ebp=%08RX32\n",
+ pXcptCtx->Esi, pXcptCtx->Edi, pXcptCtx->Esp, pXcptCtx->Ebp);
+ RTLogLoggerWeak(pLogger, NULL, "ds=%04x es=%04x fs=%04x gs=%04x eflags=%08x\n",
+ pXcptCtx->SegDs, pXcptCtx->SegEs, pXcptCtx->SegFs, pXcptCtx->SegGs, pXcptCtx->EFlags);
+ uXcptSP = pXcptCtx->Esp;
+ uXcptPC = pXcptCtx->Eip;
+#endif
+ }
+
+ /*
+ * Dump stack.
+ */
+ uintptr_t uStack = (uintptr_t)(void *)&szMarker[0];
+ uStack -= uStack & 15;
+
+ size_t cbToDump = PAGE_SIZE - (uStack & PAGE_OFFSET_MASK);
+ if (cbToDump < 512)
+ cbToDump += PAGE_SIZE;
+ size_t cbToXcpt = uXcptSP - uStack;
+ while (cbToXcpt > cbToDump && cbToXcpt <= _16K)
+ cbToDump += PAGE_SIZE;
+ ULONG_PTR uLow = (uintptr_t)&szMarker[0];
+ ULONG_PTR uHigh = (uintptr_t)&szMarker[0];
+ if (g_pfnGetCurrentThreadStackLimits)
+ {
+ g_pfnGetCurrentThreadStackLimits(&uLow, &uHigh);
+ size_t cbToTop = RT_MAX(uLow, uHigh) - uStack;
+ if (cbToTop < _1M)
+ cbToDump = cbToTop;
+ }
+
+ RTLogLoggerWeak(pLogger, NULL, "\nStack %p, dumping %#x bytes (low=%p, high=%p)\n", uStack, cbToDump, uLow, uHigh);
+ RTLogLoggerWeak(pLogger, NULL, "%.*RhxD\n", cbToDump, uStack);
+
+ /*
+ * Try figure the thread name.
+ *
+ * Note! This involves the thread db lock, so it may deadlock, which
+ * is why it's at the end.
+ */
+ RTLogLoggerWeak(pLogger, NULL, "Thread ID: %p\n", RTThreadNativeSelf());
+ RTLogLoggerWeak(pLogger, NULL, "Thread name: %s\n", RTThreadSelfName());
+ RTLogLoggerWeak(pLogger, NULL, "Thread IPRT: %p\n", RTThreadSelf());
+
+ /*
+ * Try dump the load information.
+ */
+ PPEB pPeb = RTNtCurrentPeb();
+ if (RT_VALID_PTR(pPeb))
+ {
+ PPEB_LDR_DATA pLdrData = pPeb->Ldr;
+ if (RT_VALID_PTR(pLdrData))
+ {
+ PLDR_DATA_TABLE_ENTRY pFound = NULL;
+ LIST_ENTRY * const pList = &pLdrData->InMemoryOrderModuleList;
+ LIST_ENTRY *pListEntry = pList->Flink;
+ uint32_t cLoops = 0;
+ RTLogLoggerWeak(pLogger, NULL,
+ "\nLoaded Modules:\n"
+ "%-*s[*] Timestamp Path\n", sizeof(void *) * 4 + 2 - 1, "Address range"
+ );
+ while (pListEntry != pList && RT_VALID_PTR(pListEntry) && cLoops < 1024)
+ {
+ PLDR_DATA_TABLE_ENTRY pLdrEntry = RT_FROM_MEMBER(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
+ uint32_t const cbLength = (uint32_t)(uintptr_t)pLdrEntry->Reserved3[1];
+ char chInd = ' ';
+ if (uXcptPC - (uintptr_t)pLdrEntry->DllBase < cbLength)
+ {
+ chInd = '*';
+ pFound = pLdrEntry;
+ }
+
+ if ( RT_VALID_PTR(pLdrEntry->FullDllName.Buffer)
+ && pLdrEntry->FullDllName.Length > 0
+ && pLdrEntry->FullDllName.Length < _8K
+ && (pLdrEntry->FullDllName.Length & 1) == 0
+ && pLdrEntry->FullDllName.Length <= pLdrEntry->FullDllName.MaximumLength)
+ RTLogLoggerWeak(pLogger, NULL, "%p..%p%c %08RX32 %.*ls\n",
+ pLdrEntry->DllBase, (uintptr_t)pLdrEntry->DllBase + cbLength - 1, chInd,
+ pLdrEntry->TimeDateStamp, pLdrEntry->FullDllName.Length / sizeof(RTUTF16),
+ pLdrEntry->FullDllName.Buffer);
+ else
+ RTLogLoggerWeak(pLogger, NULL, "%p..%p%c %08RX32 <bad or missing: %p LB %#x max %#x\n",
+ pLdrEntry->DllBase, (uintptr_t)pLdrEntry->DllBase + cbLength - 1, chInd,
+ pLdrEntry->TimeDateStamp, pLdrEntry->FullDllName.Buffer, pLdrEntry->FullDllName.Length,
+ pLdrEntry->FullDllName.MaximumLength);
+
+ /* advance */
+ pListEntry = pListEntry->Flink;
+ cLoops++;
+ }
+
+ /*
+ * Use the above to pick out code addresses on the stack.
+ */
+ if ( cLoops < 1024
+ && uXcptSP - uStack < cbToDump)
+ {
+ RTLogLoggerWeak(pLogger, NULL, "\nPotential code addresses on the stack:\n");
+ if (pFound)
+ {
+ if ( RT_VALID_PTR(pFound->FullDllName.Buffer)
+ && pFound->FullDllName.Length > 0
+ && pFound->FullDllName.Length < _8K
+ && (pFound->FullDllName.Length & 1) == 0
+ && pFound->FullDllName.Length <= pFound->FullDllName.MaximumLength)
+ RTLogLoggerWeak(pLogger, NULL, "%-*s: %p - %#010RX32 bytes into %.*ls\n",
+ sizeof(void *) * 2, "Xcpt PC", uXcptPC, (uint32_t)(uXcptPC - (uintptr_t)pFound->DllBase),
+ pFound->FullDllName.Length / sizeof(RTUTF16), pFound->FullDllName.Buffer);
+ else
+ RTLogLoggerWeak(pLogger, NULL, "%-*s: %p - %08RX32 into module at %p\n",
+ sizeof(void *) * 2, "Xcpt PC", uXcptPC, (uint32_t)(uXcptPC - (uintptr_t)pFound->DllBase),
+ pFound->DllBase);
+ }
+
+ uintptr_t const *puStack = (uintptr_t const *)uXcptSP;
+ uintptr_t cLeft = (cbToDump - (uXcptSP - uStack)) / sizeof(uintptr_t);
+ while (cLeft-- > 0)
+ {
+ uintptr_t uPtr = *puStack;
+ if (RT_VALID_PTR(uPtr))
+ {
+ /* Search the module table. */
+ pFound = NULL;
+ cLoops = 0;
+ pListEntry = pList->Flink;
+ while (pListEntry != pList && RT_VALID_PTR(pListEntry) && cLoops < 1024)
+ {
+ PLDR_DATA_TABLE_ENTRY pLdrEntry = RT_FROM_MEMBER(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
+ uint32_t const cbLength = (uint32_t)(uintptr_t)pLdrEntry->Reserved3[1];
+ if (uPtr - (uintptr_t)pLdrEntry->DllBase < cbLength)
+ {
+ pFound = pLdrEntry;
+ break;
+ }
+
+ /* advance */
+ pListEntry = pListEntry->Flink;
+ cLoops++;
+ }
+
+ if (pFound)
+ {
+ if ( RT_VALID_PTR(pFound->FullDllName.Buffer)
+ && pFound->FullDllName.Length > 0
+ && pFound->FullDllName.Length < _8K
+ && (pFound->FullDllName.Length & 1) == 0
+ && pFound->FullDllName.Length <= pFound->FullDllName.MaximumLength)
+ RTLogLoggerWeak(pLogger, NULL, "%p: %p - %#010RX32 bytes into %.*ls\n",
+ puStack, uPtr, (uint32_t)(uPtr - (uintptr_t)pFound->DllBase),
+ pFound->FullDllName.Length / sizeof(RTUTF16), pFound->FullDllName.Buffer);
+ else
+ RTLogLoggerWeak(pLogger, NULL, "%p: %p - %08RX32 into module at %p\n",
+ puStack, uPtr, (uint32_t)(uPtr - (uintptr_t)pFound->DllBase), pFound->DllBase);
+ }
+ }
+
+ puStack++;
+ }
+ }
+ }
+
+ /*
+ * Dump the command line if we have one. We do this last in case it crashes.
+ */
+ PRTL_USER_PROCESS_PARAMETERS pProcParams = pPeb->ProcessParameters;
+ if (RT_VALID_PTR(pProcParams))
+ {
+ if (RT_VALID_PTR(pProcParams->CommandLine.Buffer)
+ && pProcParams->CommandLine.Length > 0
+ && pProcParams->CommandLine.Length <= pProcParams->CommandLine.MaximumLength
+ && !(pProcParams->CommandLine.Length & 1)
+ && !(pProcParams->CommandLine.MaximumLength & 1))
+ RTLogLoggerWeak(pLogger, NULL, "PEB/CommandLine: %.*ls\n",
+ pProcParams->CommandLine.Length / sizeof(RTUTF16), pProcParams->CommandLine.Buffer);
+ }
+ }
+ }
+
+ /*
+ * Do the default stuff, never mind us.
+ */
+ if (g_pfnUnhandledXcptFilter)
+ return g_pfnUnhandledXcptFilter(pPtrs);
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
diff --git a/src/VBox/Runtime/r3/win/internal-r3-win.h b/src/VBox/Runtime/r3/win/internal-r3-win.h
new file mode 100644
index 00000000..562ac0ed
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/internal-r3-win.h
@@ -0,0 +1,244 @@
+/* $Id: internal-r3-win.h $ */
+/** @file
+ * IPRT - some Windows OS type constants.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_win_internal_r3_win_h
+#define IPRT_INCLUDED_SRC_r3_win_internal_r3_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "internal/iprt.h"
+#include <iprt/types.h>
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Windows OS type as determined by rtSystemWinOSType().
+ *
+ * @note ASSUMPTIONS are made regarding ordering. Win 9x should come first, then
+ * NT. The Win9x and NT versions should internally be ordered in ascending
+ * version/code-base order.
+ */
+typedef enum RTWINOSTYPE
+{
+ kRTWinOSType_UNKNOWN = 0,
+ kRTWinOSType_9XFIRST = 1,
+ kRTWinOSType_95 = kRTWinOSType_9XFIRST,
+ kRTWinOSType_95SP1,
+ kRTWinOSType_95OSR2,
+ kRTWinOSType_98,
+ kRTWinOSType_98SP1,
+ kRTWinOSType_98SE,
+ kRTWinOSType_ME,
+ kRTWinOSType_9XLAST = 99,
+ kRTWinOSType_NTFIRST = 100,
+ kRTWinOSType_NT310 = kRTWinOSType_NTFIRST,
+ kRTWinOSType_NT350,
+ kRTWinOSType_NT351,
+ kRTWinOSType_NT4,
+ kRTWinOSType_2K, /* 5.0 */
+ kRTWinOSType_XP, /* 5.1 */
+ kRTWinOSType_XP64, /* 5.2, workstation */
+ kRTWinOSType_2003, /* 5.2 */
+ kRTWinOSType_VISTA, /* 6.0, workstation */
+ kRTWinOSType_2008, /* 6.0, server */
+ kRTWinOSType_7, /* 6.1, workstation */
+ kRTWinOSType_2008R2, /* 6.1, server */
+ kRTWinOSType_8, /* 6.2, workstation */
+ kRTWinOSType_2012, /* 6.2, server */
+ kRTWinOSType_81, /* 6.3, workstation */
+ kRTWinOSType_2012R2, /* 6.3, server */
+ kRTWinOSType_10, /* 10.0, workstation */
+ kRTWinOSType_2016, /* 10.0 1607, server */
+ kRTWinOSType_2019, /* 10.0 1809, server */
+ kRTWinOSType_2022, /* 10.0 21H2, server */
+ kRTWinOSType_11, /* 11.0, workstation */
+ kRTWinOSType_NT_UNKNOWN = 199,
+ kRTWinOSType_NT_LAST = kRTWinOSType_UNKNOWN
+} RTWINOSTYPE;
+
+/**
+ * Windows loader protection level.
+ */
+typedef enum RTR3WINLDRPROT
+{
+ RTR3WINLDRPROT_INVALID = 0,
+ RTR3WINLDRPROT_NONE,
+ RTR3WINLDRPROT_NO_CWD,
+ RTR3WINLDRPROT_SAFE,
+ RTR3WINLDRPROT_SAFER
+} RTR3WINLDRPROT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern DECL_HIDDEN_DATA(RTR3WINLDRPROT) g_enmWinLdrProt;
+extern DECL_HIDDEN_DATA(RTWINOSTYPE) g_enmWinVer;
+#ifdef _WINDEF_
+extern DECL_HIDDEN_DATA(OSVERSIONINFOEXW) g_WinOsInfoEx;
+
+extern DECL_HIDDEN_DATA(HMODULE) g_hModKernel32;
+typedef UINT (WINAPI *PFNGETWINSYSDIR)(LPWSTR,UINT);
+extern DECL_HIDDEN_DATA(PFNGETWINSYSDIR) g_pfnGetSystemWindowsDirectoryW;
+typedef HANDLE (WINAPI *PFNCREATEWAITABLETIMEREX)(LPSECURITY_ATTRIBUTES, LPCWSTR, DWORD, DWORD);
+extern DECL_HIDDEN_DATA(PFNCREATEWAITABLETIMEREX) g_pfnCreateWaitableTimerExW;
+extern DECL_HIDDEN_DATA(decltype(SystemTimeToTzSpecificLocalTime) *) g_pfnSystemTimeToTzSpecificLocalTime;
+extern DECL_HIDDEN_DATA(decltype(GetHandleInformation) *) g_pfnGetHandleInformation;
+extern DECL_HIDDEN_DATA(decltype(SetHandleInformation) *) g_pfnSetHandleInformation;
+extern DECL_HIDDEN_DATA(decltype(IsDebuggerPresent) *) g_pfnIsDebuggerPresent;
+extern DECL_HIDDEN_DATA(decltype(GetSystemTimeAsFileTime) *) g_pfnGetSystemTimeAsFileTime;
+extern DECL_HIDDEN_DATA(decltype(GetProcessAffinityMask) *) g_pfnGetProcessAffinityMask;
+extern DECL_HIDDEN_DATA(decltype(SetThreadAffinityMask) *) g_pfnSetThreadAffinityMask;
+extern DECL_HIDDEN_DATA(decltype(CreateIoCompletionPort) *) g_pfnCreateIoCompletionPort;
+extern DECL_HIDDEN_DATA(decltype(GetQueuedCompletionStatus) *) g_pfnGetQueuedCompletionStatus;
+extern DECL_HIDDEN_DATA(decltype(PostQueuedCompletionStatus) *) g_pfnPostQueuedCompletionStatus;
+extern DECL_HIDDEN_DATA(decltype(SetUnhandledExceptionFilter) *) g_pfnSetUnhandledExceptionFilter;
+extern DECL_HIDDEN_DATA(decltype(UnhandledExceptionFilter) *) g_pfnUnhandledExceptionFilter;
+extern DECL_HIDDEN_DATA(decltype(IsProcessorFeaturePresent) *) g_pfnIsProcessorFeaturePresent;
+
+
+extern DECL_HIDDEN_DATA(HMODULE) g_hModNtDll;
+typedef NTSTATUS (NTAPI *PFNNTQUERYFULLATTRIBUTESFILE)(struct _OBJECT_ATTRIBUTES *, struct _FILE_NETWORK_OPEN_INFORMATION *);
+extern DECL_HIDDEN_DATA(PFNNTQUERYFULLATTRIBUTESFILE) g_pfnNtQueryFullAttributesFile;
+typedef NTSTATUS (NTAPI *PFNNTDUPLICATETOKEN)(HANDLE, ACCESS_MASK, struct _OBJECT_ATTRIBUTES *, BOOLEAN, TOKEN_TYPE, PHANDLE);
+extern DECL_HIDDEN_DATA(PFNNTDUPLICATETOKEN) g_pfnNtDuplicateToken;
+#ifdef IPRT_INCLUDED_nt_nt_h
+extern DECL_HIDDEN_DATA(decltype(NtAlertThread) *) g_pfnNtAlertThread;
+#endif
+
+extern DECL_HIDDEN_DATA(HMODULE) g_hModWinSock;
+
+/** WSAStartup */
+typedef int (WINAPI *PFNWSASTARTUP)(WORD, struct WSAData *);
+/** WSACleanup */
+typedef int (WINAPI *PFNWSACLEANUP)(void);
+/** WSAGetLastError */
+typedef int (WINAPI *PFNWSAGETLASTERROR)(void);
+/** WSASetLastError */
+typedef int (WINAPI *PFNWSASETLASTERROR)(int);
+/** WSACreateEvent */
+typedef HANDLE (WINAPI *PFNWSACREATEEVENT)(void);
+/** WSASetEvent */
+typedef BOOL (WINAPI *PFNWSASETEVENT)(HANDLE);
+/** WSACloseEvent */
+typedef BOOL (WINAPI *PFNWSACLOSEEVENT)(HANDLE);
+/** WSAEventSelect */
+typedef BOOL (WINAPI *PFNWSAEVENTSELECT)(UINT_PTR, HANDLE, LONG);
+/** WSAEnumNetworkEvents */
+typedef int (WINAPI *PFNWSAENUMNETWORKEVENTS)(UINT_PTR, HANDLE, struct _WSANETWORKEVENTS *);
+/** WSASocketW */
+typedef UINT_PTR (WINAPI *PFNWSASOCKETW)(int, int, int, struct _WSAPROTOCOL_INFOW *, unsigned, DWORD);
+/** WSASend */
+typedef int (WINAPI *PFNWSASEND)(UINT_PTR, struct _WSABUF *, DWORD, LPDWORD, DWORD dwFlags,
+ struct _OVERLAPPED *, uintptr_t /*LPWSAOVERLAPPED_COMPLETION_ROUTINE*/);
+
+/** socket */
+typedef UINT_PTR (WINAPI *PFNWINSOCKSOCKET)(int, int, int);
+/** closesocket */
+typedef int (WINAPI *PFNWINSOCKCLOSESOCKET)(UINT_PTR);
+/** recv */
+typedef int (WINAPI *PFNWINSOCKRECV)(UINT_PTR, char *, int, int);
+/** send */
+typedef int (WINAPI *PFNWINSOCKSEND)(UINT_PTR, const char *, int, int);
+/** recvfrom */
+typedef int (WINAPI *PFNWINSOCKRECVFROM)(UINT_PTR, char *, int, int, struct sockaddr *, int *);
+/** sendto */
+typedef int (WINAPI *PFNWINSOCKSENDTO)(UINT_PTR, const char *, int, int, const struct sockaddr *, int);
+/** bind */
+typedef int (WINAPI *PFNWINSOCKBIND)(UINT_PTR, const struct sockaddr *, int);
+/** listen */
+typedef int (WINAPI *PFNWINSOCKLISTEN)(UINT_PTR, int);
+/** accept */
+typedef UINT_PTR (WINAPI *PFNWINSOCKACCEPT)(UINT_PTR, struct sockaddr *, int *);
+/** connect */
+typedef int (WINAPI *PFNWINSOCKCONNECT)(UINT_PTR, const struct sockaddr *, int);
+/** shutdown */
+typedef int (WINAPI *PFNWINSOCKSHUTDOWN)(UINT_PTR, int);
+/** getsockopt */
+typedef int (WINAPI *PFNWINSOCKGETSOCKOPT)(UINT_PTR, int, int, char *, int *);
+/** setsockopt */
+typedef int (WINAPI *PFNWINSOCKSETSOCKOPT)(UINT_PTR, int, int, const char *, int);
+/** ioctlsocket */
+typedef int (WINAPI *PFNWINSOCKIOCTLSOCKET)(UINT_PTR, long, unsigned long *);
+/** getpeername */
+typedef int (WINAPI *PFNWINSOCKGETPEERNAME)(UINT_PTR, struct sockaddr *, int *);
+/** getsockname */
+typedef int (WINAPI *PFNWINSOCKGETSOCKNAME)(UINT_PTR, struct sockaddr *, int *);
+/** __WSAFDIsSet */
+typedef int (WINAPI *PFNWINSOCK__WSAFDISSET)(UINT_PTR, struct fd_set *);
+/** select */
+typedef int (WINAPI *PFNWINSOCKSELECT)(int, struct fd_set *, struct fd_set *, struct fd_set *, const struct timeval *);
+/** gethostbyname */
+typedef struct hostent *(WINAPI *PFNWINSOCKGETHOSTBYNAME)(const char *);
+
+extern DECL_HIDDEN_DATA(PFNWSASTARTUP) g_pfnWSAStartup;
+extern DECL_HIDDEN_DATA(PFNWSACLEANUP) g_pfnWSACleanup;
+extern DECL_HIDDEN_DATA(PFNWSAGETLASTERROR) g_pfnWSAGetLastError;
+extern DECL_HIDDEN_DATA(PFNWSASETLASTERROR) g_pfnWSASetLastError;
+extern DECL_HIDDEN_DATA(PFNWSACREATEEVENT) g_pfnWSACreateEvent;
+extern DECL_HIDDEN_DATA(PFNWSACLOSEEVENT) g_pfnWSACloseEvent;
+extern DECL_HIDDEN_DATA(PFNWSASETEVENT) g_pfnWSASetEvent;
+extern DECL_HIDDEN_DATA(PFNWSAEVENTSELECT) g_pfnWSAEventSelect;
+extern DECL_HIDDEN_DATA(PFNWSAENUMNETWORKEVENTS) g_pfnWSAEnumNetworkEvents;
+extern DECL_HIDDEN_DATA(PFNWSASOCKETW) g_pfnWSASocketW;
+extern DECL_HIDDEN_DATA(PFNWSASEND) g_pfnWSASend;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSOCKET) g_pfnsocket;
+extern DECL_HIDDEN_DATA(PFNWINSOCKCLOSESOCKET) g_pfnclosesocket;
+extern DECL_HIDDEN_DATA(PFNWINSOCKRECV) g_pfnrecv;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSEND) g_pfnsend;
+extern DECL_HIDDEN_DATA(PFNWINSOCKRECVFROM) g_pfnrecvfrom;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSENDTO) g_pfnsendto;
+extern DECL_HIDDEN_DATA(PFNWINSOCKBIND) g_pfnbind;
+extern DECL_HIDDEN_DATA(PFNWINSOCKLISTEN) g_pfnlisten;
+extern DECL_HIDDEN_DATA(PFNWINSOCKACCEPT) g_pfnaccept;
+extern DECL_HIDDEN_DATA(PFNWINSOCKCONNECT) g_pfnconnect;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSHUTDOWN) g_pfnshutdown;
+extern DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKOPT) g_pfngetsockopt;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSETSOCKOPT) g_pfnsetsockopt;
+extern DECL_HIDDEN_DATA(PFNWINSOCKIOCTLSOCKET) g_pfnioctlsocket;
+extern DECL_HIDDEN_DATA(PFNWINSOCKGETPEERNAME) g_pfngetpeername;
+extern DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKNAME) g_pfngetsockname;
+extern DECL_HIDDEN_DATA(PFNWINSOCK__WSAFDISSET) g_pfn__WSAFDIsSet;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSELECT) g_pfnselect;
+extern DECL_HIDDEN_DATA(PFNWINSOCKGETHOSTBYNAME) g_pfngethostbyname;
+#endif
+
+
+#endif /* !IPRT_INCLUDED_SRC_r3_win_internal_r3_win_h */
+
diff --git a/src/VBox/Runtime/r3/win/krnlmod-win.cpp b/src/VBox/Runtime/r3/win/krnlmod-win.cpp
new file mode 100644
index 00000000..255cc2cf
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/krnlmod-win.cpp
@@ -0,0 +1,331 @@
+/* $Id: krnlmod-win.cpp $ */
+/** @file
+ * IPRT - Kernel module, Windows.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYSTEM
+#include <iprt/nt/nt.h>
+
+#include <iprt/krnlmod.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/dir.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/types.h>
+
+
+/**
+ * Internal kernel information record state.
+ */
+typedef struct RTKRNLMODINFOINT
+{
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** Reference count for the kernel module. */
+ uint32_t cRefKrnlMod;
+ /** Load address of the kernel module. */
+ RTR0UINTPTR uLoadAddr;
+ /** Size of the kernel module. */
+ size_t cbKrnlMod;
+ /** Pointer to the driver name. */
+ const char *pszName;
+ /** Size of the name in characters including the zero terminator. */
+ size_t cchFilePath;
+ /** Module name - variable in size. */
+ char achFilePath[1];
+} RTKRNLMODINFOINT;
+/** Pointer to the internal kernel module information record. */
+typedef RTKRNLMODINFOINT *PRTKRNLMODINFOINT;
+/** Pointer to a const internal kernel module information record. */
+typedef const RTKRNLMODINFOINT *PCRTKRNLMODINFOINT;
+
+
+/**
+ * Destroy the given kernel module information record.
+ *
+ * @param pThis The record to destroy.
+ */
+static void rtKrnlModInfoDestroy(PRTKRNLMODINFOINT pThis)
+{
+ RTMemFree(pThis);
+}
+
+
+/**
+ * Queries the complete kernel modules structure and returns a pointer to it.
+ *
+ * @returns IPRT status code.
+ * @param ppKrnlMods Where to store the pointer to the kernel module list on success.
+ * Free with RTMemFree().
+ */
+static int rtKrnlModWinQueryKrnlMods(PRTL_PROCESS_MODULES *ppKrnlMods)
+{
+ int rc = VINF_SUCCESS;
+ RTL_PROCESS_MODULES KrnlModsSize;
+
+ NTSTATUS rcNt = NtQuerySystemInformation(SystemModuleInformation, &KrnlModsSize, sizeof(KrnlModsSize), NULL);
+ if (NT_SUCCESS(rcNt) || rcNt == STATUS_INFO_LENGTH_MISMATCH)
+ {
+ ULONG cbKrnlMods = RT_UOFFSETOF_DYN(RTL_PROCESS_MODULES, Modules[KrnlModsSize.NumberOfModules]);
+ PRTL_PROCESS_MODULES pKrnlMods = (PRTL_PROCESS_MODULES)RTMemAllocZ(cbKrnlMods);
+ if (RT_LIKELY(pKrnlMods))
+ {
+ rcNt = NtQuerySystemInformation(SystemModuleInformation, pKrnlMods, cbKrnlMods, NULL);
+ if (NT_SUCCESS(rcNt))
+ *ppKrnlMods = pKrnlMods;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ return rc;
+}
+
+/**
+ * Creates a new kernel module information record for the given module.
+ *
+ * @returns IPRT status code.
+ * @param pModInfo The kernel module information.
+ * @param phKrnlModInfo Where to store the handle to the kernel module information record
+ * on success.
+ */
+static int rtKrnlModWinInfoCreate(PRTL_PROCESS_MODULE_INFORMATION pModInfo, PRTKRNLMODINFO phKrnlModInfo)
+{
+ int rc = VINF_SUCCESS;
+ RT_NOREF2(pModInfo, phKrnlModInfo);
+ size_t cchFilePath = strlen((const char *)&pModInfo->FullPathName[0]) + 1;
+ PRTKRNLMODINFOINT pThis = (PRTKRNLMODINFOINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTKRNLMODINFOINT, achFilePath[cchFilePath]));
+ if (RT_LIKELY(pThis))
+ {
+ memcpy(&pThis->achFilePath[0], &pModInfo->FullPathName[0], cchFilePath);
+ pThis->cchFilePath = cchFilePath;
+ pThis->cRefs = 1;
+ pThis->cbKrnlMod = pModInfo->ImageSize;
+ pThis->uLoadAddr = (RTR0UINTPTR)pModInfo->ImageBase;
+ pThis->pszName = pModInfo->OffsetToFileName >= cchFilePath
+ ? NULL
+ : pThis->achFilePath + pModInfo->OffsetToFileName;
+
+ *phKrnlModInfo = pThis;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTKrnlModQueryLoaded(const char *pszName, bool *pfLoaded)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfLoaded, VERR_INVALID_POINTER);
+
+ int rc = VERR_NOT_IMPLEMENTED;
+
+ return rc;
+}
+
+
+RTDECL(int) RTKrnlModLoadedQueryInfo(const char *pszName, PRTKRNLMODINFO phKrnlModInfo)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(phKrnlModInfo, VERR_INVALID_POINTER);
+
+ int rc = VERR_NOT_IMPLEMENTED;
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTKrnlModLoadedGetCount(void)
+{
+ uint32_t cKrnlMods = 0;
+ RTL_PROCESS_MODULES ProcMods;
+
+ NTSTATUS rcNt = NtQuerySystemInformation(SystemModuleInformation, &ProcMods, sizeof(ProcMods), NULL);
+ if (NT_SUCCESS(rcNt) || rcNt == STATUS_INFO_LENGTH_MISMATCH)
+ cKrnlMods = ProcMods.NumberOfModules;
+
+ return cKrnlMods;
+}
+
+
+RTDECL(int) RTKrnlModLoadedQueryInfoAll(PRTKRNLMODINFO pahKrnlModInfo, uint32_t cEntriesMax,
+ uint32_t *pcEntries)
+{
+ if (cEntriesMax > 0)
+ AssertPtrReturn(pahKrnlModInfo, VERR_INVALID_POINTER);
+
+ PRTL_PROCESS_MODULES pKrnlMods = NULL;
+ int rc = rtKrnlModWinQueryKrnlMods(&pKrnlMods);
+ if (RT_SUCCESS(rc))
+ {
+ if (pKrnlMods->NumberOfModules <= cEntriesMax)
+ {
+ for (unsigned i = 0; i < pKrnlMods->NumberOfModules; i++)
+ {
+ pKrnlMods->Modules[i].FullPathName[255] = '\0'; /* Paranoia */
+ rc = rtKrnlModWinInfoCreate(&pKrnlMods->Modules[i], &pahKrnlModInfo[i]);
+ if (RT_FAILURE(rc))
+ {
+ while (i-- > 0)
+ RTKrnlModInfoRelease(pahKrnlModInfo[i]);
+ break;
+ }
+ }
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ if (pcEntries)
+ *pcEntries = pKrnlMods->NumberOfModules;
+
+ RTMemFree(pKrnlMods);
+ }
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoRetain(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoRelease(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ if (!pThis)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0)
+ rtKrnlModInfoDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoGetRefCnt(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->cRefKrnlMod;
+}
+
+
+RTDECL(const char *) RTKrnlModInfoGetName(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, NULL);
+
+ return pThis->pszName;
+}
+
+
+RTDECL(const char *) RTKrnlModInfoGetFilePath(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, NULL);
+
+ return &pThis->achFilePath[0];
+}
+
+
+RTDECL(size_t) RTKrnlModInfoGetSize(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->cbKrnlMod;
+}
+
+
+RTDECL(RTR0UINTPTR) RTKrnlModInfoGetLoadAddr(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->uLoadAddr;
+}
+
+
+RTDECL(int) RTKrnlModInfoQueryRefModInfo(RTKRNLMODINFO hKrnlModInfo, uint32_t idx,
+ PRTKRNLMODINFO phKrnlModInfoRef)
+{
+ RT_NOREF3(hKrnlModInfo, idx, phKrnlModInfoRef);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModLoadByName(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModLoadByPath(const char *pszPath)
+{
+ AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModUnloadByName(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
diff --git a/src/VBox/Runtime/r3/win/ldrNative-win.cpp b/src/VBox/Runtime/r3/win/ldrNative-win.cpp
new file mode 100644
index 00000000..2ff98a4c
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/ldrNative-win.cpp
@@ -0,0 +1,326 @@
+/* $Id: ldrNative-win.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader, Win32 native.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/once.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "internal/ldr.h"
+#include "internal-r3-win.h"
+
+#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
+# define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR UINT32_C(0x100)
+# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR UINT32_C(0x200)
+# define LOAD_LIBRARY_SEARCH_SYSTEM32 UINT32_C(0x800)
+#endif
+
+
+DECLHIDDEN(int) rtldrNativeLoad(const char *pszFilename, uintptr_t *phHandle, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ Assert(sizeof(*phHandle) >= sizeof(HMODULE));
+ AssertReturn(!(fFlags & RTLDRLOAD_FLAGS_GLOBAL), VERR_INVALID_FLAGS);
+ AssertLogRelMsgReturn(RTPathStartsWithRoot(pszFilename), /* Relative names will still be applied to the search path. */
+ ("pszFilename='%s'\n", pszFilename),
+ VERR_INTERNAL_ERROR_2);
+ AssertReleaseMsg(g_hModKernel32,
+ ("rtldrNativeLoad(%s,,) is called before IPRT has configured the windows loader!\n", pszFilename));
+
+ /*
+ * Convert to UTF-16 and make sure it got a .DLL suffix.
+ */
+ /** @todo Implement long path support for native DLL loading on windows. @bugref{9248} */
+ int rc;
+ RTUTF16 *pwszNative = NULL;
+ if (RTPathHasSuffix(pszFilename) || (fFlags & RTLDRLOAD_FLAGS_NO_SUFFIX))
+ rc = RTStrToUtf16(pszFilename, &pwszNative);
+ else
+ {
+ size_t cwcAlloc;
+ rc = RTStrCalcUtf16LenEx(pszFilename, RTSTR_MAX, &cwcAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ cwcAlloc += sizeof(".DLL");
+ pwszNative = RTUtf16Alloc(cwcAlloc * sizeof(RTUTF16));
+ if (pwszNative)
+ {
+ size_t cwcNative;
+ rc = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwszNative, cwcAlloc, &cwcNative);
+ if (RT_SUCCESS(rc))
+ rc = RTUtf16CopyAscii(&pwszNative[cwcNative], cwcAlloc - cwcNative, ".DLL");
+ }
+ else
+ rc = VERR_NO_UTF16_MEMORY;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /* Convert slashes just to be on the safe side. */
+ for (size_t off = 0;; off++)
+ {
+ RTUTF16 wc = pwszNative[off];
+ if (wc == '/')
+ pwszNative[off] = '\\';
+ else if (!wc)
+ break;
+ }
+
+ /*
+ * Attempt load.
+ */
+ HMODULE hmod;
+ static int s_iSearchDllLoadDirSupported = 0;
+ if ( !(fFlags & RTLDRLOAD_FLAGS_NT_SEARCH_DLL_LOAD_DIR)
+ || s_iSearchDllLoadDirSupported < 0)
+ hmod = LoadLibraryExW(pwszNative, NULL, 0);
+ else
+ {
+ hmod = LoadLibraryExW(pwszNative, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32
+ | LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
+ if (s_iSearchDllLoadDirSupported == 0)
+ {
+ if (hmod != NULL || GetLastError() != ERROR_INVALID_PARAMETER)
+ s_iSearchDllLoadDirSupported = 1;
+ else
+ {
+ s_iSearchDllLoadDirSupported = -1;
+ hmod = LoadLibraryExW(pwszNative, NULL, 0);
+ }
+ }
+ }
+ if (hmod)
+ {
+ *phHandle = (uintptr_t)hmod;
+ RTUtf16Free(pwszNative);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Try figure why it failed to load.
+ */
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ rc = RTErrInfoSetF(pErrInfo, rc, "GetLastError=%u", dwErr);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "Error converting UTF-8 to UTF-16 string.");
+ RTUtf16Free(pwszNative);
+ return rc;
+}
+
+
+DECLCALLBACK(int) rtldrNativeGetSymbol(PRTLDRMODINTERNAL pMod, const char *pszSymbol, void **ppvValue)
+{
+ PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod;
+ FARPROC pfn = GetProcAddress((HMODULE)pModNative->hNative, pszSymbol);
+ if (pfn)
+ {
+ *ppvValue = (void *)pfn;
+ return VINF_SUCCESS;
+ }
+ *ppvValue = NULL;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+DECLCALLBACK(int) rtldrNativeClose(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod;
+ if ( (pModNative->fFlags & RTLDRLOAD_FLAGS_NO_UNLOAD)
+ || FreeLibrary((HMODULE)pModNative->hNative))
+ {
+ pModNative->hNative = (uintptr_t)INVALID_HANDLE_VALUE;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+DECLHIDDEN(int) rtldrNativeLoadSystem(const char *pszFilename, const char *pszExt, uint32_t fFlags, PRTLDRMOD phLdrMod)
+{
+ AssertReleaseMsg(g_hModKernel32,
+ ("rtldrNativeLoadSystem(%s,,) is called before IPRT has configured the windows loader!\n", pszFilename));
+
+ /*
+ * Resolve side-by-side resolver API.
+ */
+ static bool volatile s_fInitialized = false;
+ static decltype(RtlDosApplyFileIsolationRedirection_Ustr) *s_pfnApplyRedir = NULL;
+ if (!s_fInitialized)
+ {
+ s_pfnApplyRedir = (decltype(s_pfnApplyRedir))GetProcAddress(g_hModNtDll,
+ "RtlDosApplyFileIsolationRedirection_Ustr");
+ ASMCompilerBarrier();
+ s_fInitialized = true;
+ }
+
+ /*
+ * We try WinSxS via undocumented NTDLL API and flal back on the System32
+ * directory. No other locations are supported.
+ */
+ int rc = VERR_TRY_AGAIN;
+ char szPath[RTPATH_MAX];
+ char *pszPath = szPath;
+
+ /* Get the windows system32 directory so we can sanity check the WinSxS result. */
+ WCHAR wszSysDir[MAX_PATH];
+ UINT cwcSysDir = GetSystemDirectoryW(wszSysDir, MAX_PATH);
+ if (cwcSysDir >= MAX_PATH)
+ return VERR_FILENAME_TOO_LONG;
+
+ /* Try side-by-side first (see COMCTL32.DLL). */
+ if (s_pfnApplyRedir)
+ {
+ size_t cwcName = 0;
+ RTUTF16 wszName[MAX_PATH];
+ PRTUTF16 pwszName = wszName;
+ int rc2 = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwszName, RT_ELEMENTS(wszName), &cwcName);
+ if (RT_SUCCESS(rc2))
+ {
+ static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll");
+ WCHAR wszPath[MAX_PATH];
+ UNICODE_STRING UniStrStatic = { 0, (USHORT)sizeof(wszPath) - sizeof(WCHAR), wszPath };
+ UNICODE_STRING UniStrDynamic = { 0, 0, NULL };
+ PUNICODE_STRING pUniStrResult = NULL;
+ UNICODE_STRING UniStrName =
+ { (USHORT)(cwcName * sizeof(RTUTF16)), (USHORT)((cwcName + 1) * sizeof(RTUTF16)), wszName };
+
+ NTSTATUS rcNt = s_pfnApplyRedir(1 /*fFlags*/,
+ &UniStrName,
+ (PUNICODE_STRING)&s_DefaultSuffix,
+ &UniStrStatic,
+ &UniStrDynamic,
+ &pUniStrResult,
+ NULL /*pNewFlags*/,
+ NULL /*pcbFilename*/,
+ NULL /*pcbNeeded*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Check that the resolved path has similarities to the
+ * system directory.
+ *
+ * ASSUMES the windows directory is a root directory and
+ * that both System32 and are on the same level. So, we'll
+ * have 2 matching components (or more if the resolver
+ * returns a system32 path for some reason).
+ */
+ unsigned cMatchingComponents = 0;
+ size_t off = 0;
+ while (off < pUniStrResult->Length)
+ {
+ RTUTF16 wc1 = wszSysDir[off];
+ RTUTF16 wc2 = pUniStrResult->Buffer[off];
+ if (!RTPATH_IS_SLASH(wc1))
+ {
+ if (wc1 == wc2)
+ off++;
+ else if ( wc1 < 127
+ && wc2 < 127
+ && RT_C_TO_LOWER(wc1) == RT_C_TO_LOWER(wc2) )
+ off++;
+ else
+ break;
+ }
+ else if (RTPATH_IS_SLASH(wc2))
+ {
+ cMatchingComponents += off > 0;
+ do
+ off++;
+ while ( off < pUniStrResult->Length
+ && RTPATH_IS_SLASH(wszSysDir[off])
+ && RTPATH_IS_SLASH(pUniStrResult->Buffer[off]));
+ }
+ else
+ break;
+ }
+ if (cMatchingComponents >= 2)
+ {
+ pszPath = szPath;
+ rc2 = RTUtf16ToUtf8Ex(pUniStrResult->Buffer, pUniStrResult->Length / sizeof(RTUTF16),
+ &pszPath, sizeof(szPath), NULL);
+ if (RT_SUCCESS(rc2))
+ rc = VINF_SUCCESS;
+ }
+ else
+ AssertMsgFailed(("%s -> '%*.ls'\n", pszFilename, pUniStrResult->Length, pUniStrResult->Buffer));
+ RtlFreeUnicodeString(&UniStrDynamic);
+ }
+ }
+ else
+ AssertMsgFailed(("%Rrc\n", rc));
+ }
+
+ /* If the above didn't succeed, create a system32 path. */
+ if (RT_FAILURE(rc))
+ {
+ rc = RTUtf16ToUtf8Ex(wszSysDir, RTSTR_MAX, &pszPath, sizeof(szPath), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPathAppend(szPath, sizeof(szPath), pszFilename);
+ if (pszExt && RT_SUCCESS(rc))
+ rc = RTStrCat(szPath, sizeof(szPath), pszExt);
+ }
+ }
+
+ /* Do the actual loading, if we were successful constructing a name. */
+ if (RT_SUCCESS(rc))
+ {
+ if (RTFileExists(szPath))
+ rc = RTLdrLoadEx(szPath, phLdrMod, fFlags, NULL);
+ else
+ rc = VERR_MODULE_NOT_FOUND;
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/localipc-win.cpp b/src/VBox/Runtime/r3/win/localipc-win.cpp
new file mode 100644
index 00000000..6d56ab91
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/localipc-win.cpp
@@ -0,0 +1,1778 @@
+/* $Id: localipc-win.cpp $ */
+/** @file
+ * IPRT - Local IPC, Windows Implementation Using Named Pipes.
+ *
+ * @note This code only works on W2K because of the dependency on
+ * ConvertStringSecurityDescriptorToSecurityDescriptor.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LOCALIPC
+#include <iprt/nt/nt-and-windows.h> /* Need NtCancelIoFile and a few Rtl functions. */
+#include <sddl.h>
+#include <aclapi.h>
+
+#include "internal/iprt.h"
+#include <iprt/localipc.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/utf16.h>
+
+#include "internal/magics.h"
+#include "internal-r3-win.h"
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Pipe prefix string. */
+#define RTLOCALIPC_WIN_PREFIX L"\\\\.\\pipe\\IPRT-"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Local IPC service instance, Windows.
+ */
+typedef struct RTLOCALIPCSERVERINT
+{
+ /** The magic (RTLOCALIPCSERVER_MAGIC). */
+ uint32_t u32Magic;
+ /** The creation flags. */
+ uint32_t fFlags;
+ /** Critical section protecting the structure. */
+ RTCRITSECT CritSect;
+ /** The number of references to the instance.
+ * @remarks The reference counting isn't race proof. */
+ uint32_t volatile cRefs;
+ /** Indicates that there is a pending cancel request. */
+ bool volatile fCancelled;
+ /** The named pipe handle. */
+ HANDLE hNmPipe;
+ /** The handle to the event object we're using for overlapped I/O. */
+ HANDLE hEvent;
+ /** The overlapped I/O structure. */
+ OVERLAPPED OverlappedIO;
+ /** The full pipe name (variable length). */
+ RTUTF16 wszName[1];
+} RTLOCALIPCSERVERINT;
+/** Pointer to a local IPC server instance (Windows). */
+typedef RTLOCALIPCSERVERINT *PRTLOCALIPCSERVERINT;
+
+
+/**
+ * Local IPC session instance, Windows.
+ *
+ * This is a named pipe and we should probably merge the pipe code with this to
+ * save work and code duplication.
+ */
+typedef struct RTLOCALIPCSESSIONINT
+{
+ /** The magic (RTLOCALIPCSESSION_MAGIC). */
+ uint32_t u32Magic;
+ /** Critical section protecting the structure. */
+ RTCRITSECT CritSect;
+ /** The number of references to the instance.
+ * @remarks The reference counting isn't race proof. */
+ uint32_t volatile cRefs;
+ /** Set if the zero byte read that the poll code using is pending. */
+ bool fZeroByteRead;
+ /** Indicates that there is a pending cancel request. */
+ bool volatile fCancelled;
+ /** Set if this is the server side, clear if the client. */
+ bool fServerSide;
+ /** The named pipe handle. */
+ HANDLE hNmPipe;
+ struct
+ {
+ RTTHREAD hActiveThread;
+ /** The handle to the event object we're using for overlapped I/O. */
+ HANDLE hEvent;
+ /** The overlapped I/O structure. */
+ OVERLAPPED OverlappedIO;
+ }
+ /** Overlapped reads. */
+ Read,
+ /** Overlapped writes. */
+ Write;
+#if 0 /* Non-blocking writes are not yet supported. */
+ /** Bounce buffer for writes. */
+ uint8_t *pbBounceBuf;
+ /** Amount of used buffer space. */
+ size_t cbBounceBufUsed;
+ /** Amount of allocated buffer space. */
+ size_t cbBounceBufAlloc;
+#endif
+ /** Buffer for the zero byte read.
+ * Used in RTLocalIpcSessionWaitForData(). */
+ uint8_t abBuf[8];
+} RTLOCALIPCSESSIONINT;
+/** Pointer to a local IPC session instance (Windows). */
+typedef RTLOCALIPCSESSIONINT *PRTLOCALIPCSESSIONINT;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession);
+
+
+/**
+ * DACL for block all network access and local users other than the creator/owner.
+ *
+ * ACE format: (ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid)
+ *
+ * Note! FILE_GENERIC_WRITE (SDDL_FILE_WRITE) is evil here because it includes
+ * the FILE_CREATE_PIPE_INSTANCE(=FILE_APPEND_DATA) flag. Thus the hardcoded
+ * value 0x0012019b in the client ACE. The server-side still needs
+ * setting FILE_CREATE_PIPE_INSTANCE although.
+ * It expands to:
+ * 0x00000001 - FILE_READ_DATA
+ * 0x00000008 - FILE_READ_EA
+ * 0x00000080 - FILE_READ_ATTRIBUTES
+ * 0x00020000 - READ_CONTROL
+ * 0x00100000 - SYNCHRONIZE
+ * 0x00000002 - FILE_WRITE_DATA
+ * 0x00000010 - FILE_WRITE_EA
+ * 0x00000100 - FILE_WRITE_ATTRIBUTES
+ * = 0x0012019b (client)
+ * + (only for server):
+ * 0x00000004 - FILE_CREATE_PIPE_INSTANCE
+ * = 0x0012019f
+ *
+ * @todo Triple check this!
+ * @todo EVERYONE -> AUTHENTICATED USERS or something more appropriate?
+ * @todo Have trouble allowing the owner FILE_CREATE_PIPE_INSTANCE access, so for now I'm hacking
+ * it just to get progress - the service runs as local system.
+ * The CREATOR OWNER and PERSONAL SELF works (the former is only involved in inheriting
+ * it seems, which is why it won't work. The latter I've no idea about. Perhaps the solution
+ * is to go the annoying route of OpenProcessToken, QueryTokenInformation,
+ * ConvertSidToStringSid and then use the result... Suggestions are very welcome
+ */
+#define RTLOCALIPC_WIN_SDDL_BASE \
+ SDDL_DACL SDDL_DELIMINATOR \
+ SDDL_ACE_BEGIN SDDL_ACCESS_DENIED L";;" SDDL_GENERIC_ALL L";;;" SDDL_NETWORK SDDL_ACE_END \
+ SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" SDDL_FILE_ALL L";;;" SDDL_LOCAL_SYSTEM SDDL_ACE_END
+#define RTLOCALIPC_WIN_SDDL_SERVER \
+ RTLOCALIPC_WIN_SDDL_BASE \
+ SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019f" L";;;" SDDL_EVERYONE SDDL_ACE_END
+#define RTLOCALIPC_WIN_SDDL_CLIENT \
+ RTLOCALIPC_WIN_SDDL_BASE \
+ SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019b" L";;;" SDDL_EVERYONE SDDL_ACE_END
+static NTSTATUS rtLocalIpcBuildDacl(PACL pDacl, bool fServer)
+{
+ static SID_IDENTIFIER_AUTHORITY s_NtAuth = SECURITY_NT_AUTHORITY;
+ static SID_IDENTIFIER_AUTHORITY s_WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
+ union
+ {
+ SID Sid;
+ uint8_t abPadding[SECURITY_MAX_SID_SIZE];
+ } Network, LocalSystem, Everyone;
+
+
+ /* 1. SDDL_ACCESS_DENIED L";;" SDDL_GENERIC_ALL L";;;" SDDL_NETWORK */
+ NTSTATUS rcNt = RtlInitializeSid(&Network.Sid, &s_NtAuth, 1);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+ *RtlSubAuthoritySid(&Network.Sid, 0) = SECURITY_NETWORK_RID;
+
+ rcNt = RtlAddAccessDeniedAce(pDacl, ACL_REVISION, GENERIC_ALL, &Network.Sid);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+
+ /* 2. SDDL_ACCESS_ALLOWED L";;" SDDL_FILE_ALL L";;;" SDDL_LOCAL_SYSTEM */
+ rcNt = RtlInitializeSid(&LocalSystem.Sid, &s_NtAuth, 1);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+ *RtlSubAuthoritySid(&LocalSystem.Sid, 0) = SECURITY_LOCAL_SYSTEM_RID;
+
+ rcNt = RtlAddAccessAllowedAce(pDacl, ACL_REVISION, FILE_ALL_ACCESS, &Network.Sid);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+
+
+ /* 3. server: SDDL_ACCESS_ALLOWED L";;" L"0x0012019f" L";;;" SDDL_EVERYONE
+ client: SDDL_ACCESS_ALLOWED L";;" L"0x0012019b" L";;;" SDDL_EVERYONE */
+ rcNt = RtlInitializeSid(&Everyone.Sid, &s_NtAuth, 1);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+ *RtlSubAuthoritySid(&Everyone.Sid, 0) = SECURITY_WORLD_RID;
+
+ DWORD const fAccess = FILE_READ_DATA /* 0x00000001 */
+ | FILE_WRITE_DATA /* 0x00000002 */
+ | FILE_CREATE_PIPE_INSTANCE * fServer /* 0x00000004 */
+ | FILE_READ_EA /* 0x00000008 */
+ | FILE_WRITE_EA /* 0x00000010 */
+ | FILE_READ_ATTRIBUTES /* 0x00000080 */
+ | FILE_WRITE_ATTRIBUTES /* 0x00000100 */
+ | READ_CONTROL /* 0x00020000 */
+ | SYNCHRONIZE; /* 0x00100000*/
+ Assert(fAccess == (fServer ? 0x0012019fU : 0x0012019bU));
+
+ rcNt = RtlAddAccessAllowedAce(pDacl, ACL_REVISION, fAccess, &Network.Sid);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+
+ return true;
+}
+
+
+/**
+ * Builds and allocates the security descriptor required for securing the local pipe.
+ *
+ * @return IPRT status code.
+ * @param ppDesc Where to store the allocated security descriptor on success.
+ * Must be free'd using LocalFree().
+ * @param fServer Whether it's for a server or client instance.
+ */
+static int rtLocalIpcServerWinAllocSecurityDescriptor(PSECURITY_DESCRIPTOR *ppDesc, bool fServer)
+{
+ int rc;
+ PSECURITY_DESCRIPTOR pSecDesc = NULL;
+
+#if 0
+ /*
+ * Resolve the API the first time around.
+ */
+ static bool volatile s_fResolvedApis = false;
+ /** advapi32.dll API ConvertStringSecurityDescriptorToSecurityDescriptorW. */
+ static decltype(ConvertStringSecurityDescriptorToSecurityDescriptorW) *s_pfnSSDLToSecDescW = NULL;
+
+ if (!s_fResolvedApis)
+ {
+ s_pfnSSDLToSecDescW
+ = (decltype(s_pfnSSDLToSecDescW))RTLdrGetSystemSymbol("advapi32.dll",
+ "ConvertStringSecurityDescriptorToSecurityDescriptorW");
+ ASMCompilerBarrier();
+ s_fResolvedApis = true;
+ }
+ if (s_pfnSSDLToSecDescW)
+ {
+ /*
+ * We'll create a security descriptor from a SDDL that denies
+ * access to network clients (this is local IPC after all), it
+ * makes some further restrictions to prevent non-authenticated
+ * users from screwing around.
+ */
+ PCRTUTF16 pwszSDDL = fServer ? RTLOCALIPC_WIN_SDDL_SERVER : RTLOCALIPC_WIN_SDDL_CLIENT;
+ ULONG cbSecDesc = 0;
+ SetLastError(0);
+ if (s_pfnSSDLToSecDescW(pwszSDDL, SDDL_REVISION_1, &pSecDesc, &cbSecDesc))
+ {
+ DWORD dwErr = GetLastError(); RT_NOREF(dwErr);
+ AssertPtr(pSecDesc);
+ *ppDesc = pSecDesc;
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+#endif
+ {
+ /*
+ * Manually construct the descriptor.
+ *
+ * This is a bit crude. The 8KB is probably 50+ times more than what we need.
+ */
+ uint32_t const cbAlloc = SECURITY_DESCRIPTOR_MIN_LENGTH * 2 + _8K;
+ pSecDesc = LocalAlloc(LMEM_FIXED, cbAlloc);
+ if (!pSecDesc)
+ return VERR_NO_MEMORY;
+ RT_BZERO(pSecDesc, cbAlloc);
+
+ uint32_t const cbDacl = cbAlloc - SECURITY_DESCRIPTOR_MIN_LENGTH * 2;
+ PACL const pDacl = (PACL)((uint8_t *)pSecDesc + SECURITY_DESCRIPTOR_MIN_LENGTH * 2);
+
+ if ( InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION)
+ && InitializeAcl(pDacl, cbDacl, ACL_REVISION))
+ {
+ if (rtLocalIpcBuildDacl(pDacl, fServer))
+ {
+ *ppDesc = pSecDesc;
+ return VINF_SUCCESS;
+ }
+ rc = VERR_GENERAL_FAILURE;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ LocalFree(pSecDesc);
+ }
+ return rc;
+}
+
+
+/**
+ * Creates a named pipe instance.
+ *
+ * This is used by both RTLocalIpcServerCreate and RTLocalIpcServerListen.
+ *
+ * @return IPRT status code.
+ * @param phNmPipe Where to store the named pipe handle on success.
+ * This will be set to INVALID_HANDLE_VALUE on failure.
+ * @param pwszPipeName The named pipe name, full, UTF-16 encoded.
+ * @param fFirst Set on the first call (from RTLocalIpcServerCreate),
+ * otherwise clear. Governs the
+ * FILE_FLAG_FIRST_PIPE_INSTANCE flag.
+ */
+static int rtLocalIpcServerWinCreatePipeInstance(PHANDLE phNmPipe, PCRTUTF16 pwszPipeName, bool fFirst)
+{
+ *phNmPipe = INVALID_HANDLE_VALUE;
+
+ /*
+ * Create a security descriptor blocking access to the pipe via network.
+ */
+ PSECURITY_DESCRIPTOR pSecDesc;
+ int rc = rtLocalIpcServerWinAllocSecurityDescriptor(&pSecDesc, fFirst /* Server? */);
+ if (RT_SUCCESS(rc))
+ {
+#if 0
+ { /* Just for checking the security descriptor out in the debugger (!sd <addr> doesn't work): */
+ DWORD dwRet = LookupSecurityDescriptorPartsW(NULL, NULL, NULL, NULL, NULL, NULL, pSecDesc);
+ __debugbreak(); RT_NOREF(dwRet);
+
+ PTRUSTEE_W pOwner = NULL;
+ PTRUSTEE_W pGroup = NULL;
+ ULONG cAces = 0;
+ PEXPLICIT_ACCESS_W paAces = NULL;
+ ULONG cAuditEntries = 0;
+ PEXPLICIT_ACCESS_W paAuditEntries = NULL;
+ dwRet = LookupSecurityDescriptorPartsW(&pOwner, NULL, NULL, NULL, NULL, NULL, pSecDesc);
+ dwRet = LookupSecurityDescriptorPartsW(NULL, &pGroup, NULL, NULL, NULL, NULL, pSecDesc);
+ dwRet = LookupSecurityDescriptorPartsW(NULL, NULL, &cAces, &paAces, NULL, NULL, pSecDesc);
+ dwRet = LookupSecurityDescriptorPartsW(NULL, NULL, NULL, NULL, &cAuditEntries, &paAuditEntries, pSecDesc);
+ __debugbreak(); RT_NOREF(dwRet);
+ }
+#endif
+
+ /*
+ * Now, create the pipe.
+ */
+ SECURITY_ATTRIBUTES SecAttrs;
+ SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+ SecAttrs.lpSecurityDescriptor = pSecDesc;
+ SecAttrs.bInheritHandle = FALSE;
+
+ DWORD fOpenMode = PIPE_ACCESS_DUPLEX
+ | PIPE_WAIT
+ | FILE_FLAG_OVERLAPPED;
+ if ( fFirst
+ && ( g_enmWinVer >= kRTWinOSType_XP
+ || ( g_enmWinVer == kRTWinOSType_2K
+ && g_WinOsInfoEx.wServicePackMajor >= 2) ) )
+ fOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; /* Introduced with W2K SP2 */
+
+ HANDLE hNmPipe = CreateNamedPipeW(pwszPipeName, /* lpName */
+ fOpenMode, /* dwOpenMode */
+ PIPE_TYPE_BYTE, /* dwPipeMode */
+ PIPE_UNLIMITED_INSTANCES, /* nMaxInstances */
+ PAGE_SIZE, /* nOutBufferSize (advisory) */
+ PAGE_SIZE, /* nInBufferSize (ditto) */
+ 30*1000, /* nDefaultTimeOut = 30 sec */
+ &SecAttrs); /* lpSecurityAttributes */
+ if (hNmPipe != INVALID_HANDLE_VALUE)
+ {
+#if 0 /* For checking access control stuff in windbg (doesn't work): */
+ PSECURITY_DESCRIPTOR pSecDesc2 = NULL;
+ PACL pDacl = NULL;
+ DWORD dwRet;
+ dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, NULL, &pSecDesc2);
+ PACL pSacl = NULL;
+ dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, SACL_SECURITY_INFORMATION, NULL, NULL, NULL, &pSacl, &pSecDesc2);
+ dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, &pSacl, &pSecDesc2);
+ PSID pSidOwner = NULL;
+ dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pSidOwner, NULL, NULL, NULL, &pSecDesc2);
+ PSID pSidGroup = NULL;
+ dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, NULL, &pSidGroup, NULL, NULL, &pSecDesc2);
+ __debugbreak();
+ RT_NOREF(dwRet);
+#endif
+ *phNmPipe = hNmPipe;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ LocalFree(pSecDesc);
+ }
+ return rc;
+}
+
+
+/**
+ * Validates the user specified name.
+ *
+ * @returns IPRT status code.
+ * @param pszName The name to validate.
+ * @param pcwcFullName Where to return the UTF-16 length of the full name.
+ * @param fNative Whether it's a native name or a portable name.
+ */
+static int rtLocalIpcWinValidateName(const char *pszName, size_t *pcwcFullName, bool fNative)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(*pszName, VERR_INVALID_NAME);
+
+ if (!fNative)
+ {
+ size_t cwcName = RT_ELEMENTS(RTLOCALIPC_WIN_PREFIX) - 1;
+ for (;;)
+ {
+ char ch = *pszName++;
+ if (!ch)
+ break;
+ AssertReturn(!RT_C_IS_CNTRL(ch), VERR_INVALID_NAME);
+ AssertReturn((unsigned)ch < 0x80, VERR_INVALID_NAME);
+ AssertReturn(ch != '\\', VERR_INVALID_NAME);
+ AssertReturn(ch != '/', VERR_INVALID_NAME);
+ cwcName++;
+ }
+ *pcwcFullName = cwcName;
+ }
+ else
+ {
+ int rc = RTStrCalcUtf16LenEx(pszName, RTSTR_MAX, pcwcFullName);
+ AssertRCReturn(rc, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Constructs the full pipe name as UTF-16.
+ *
+ * @returns IPRT status code.
+ * @param pszName The user supplied name. ASSUMES reasonable length
+ * for now, so no long path prefixing needed.
+ * @param pwszFullName The output buffer.
+ * @param cwcFullName The output buffer size excluding the terminator.
+ * @param fNative Whether the user supplied name is a native or
+ * portable one.
+ */
+static int rtLocalIpcWinConstructName(const char *pszName, PRTUTF16 pwszFullName, size_t cwcFullName, bool fNative)
+{
+ if (!fNative)
+ {
+ static RTUTF16 const s_wszPrefix[] = RTLOCALIPC_WIN_PREFIX;
+ Assert(cwcFullName * sizeof(RTUTF16) > sizeof(s_wszPrefix));
+ memcpy(pwszFullName, s_wszPrefix, sizeof(s_wszPrefix));
+ cwcFullName -= RT_ELEMENTS(s_wszPrefix) - 1;
+ pwszFullName += RT_ELEMENTS(s_wszPrefix) - 1;
+ }
+ return RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszFullName, cwcFullName + 1, NULL);
+}
+
+
+RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszName, uint32_t fFlags)
+{
+ /*
+ * Validate parameters.
+ */
+ AssertPtrReturn(phServer, VERR_INVALID_POINTER);
+ *phServer = NIL_RTLOCALIPCSERVER;
+ AssertReturn(!(fFlags & ~RTLOCALIPC_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
+ size_t cwcFullName;
+ int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate and initialize the instance data.
+ */
+ size_t cbThis = RT_UOFFSETOF_DYN(RTLOCALIPCSERVERINT, wszName[cwcFullName + 1]);
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)RTMemAllocVar(cbThis);
+ AssertReturn(pThis, VERR_NO_MEMORY);
+
+ pThis->u32Magic = RTLOCALIPCSERVER_MAGIC;
+ pThis->cRefs = 1; /* the one we return */
+ pThis->fCancelled = false;
+
+ rc = rtLocalIpcWinConstructName(pszName, pThis->wszName, cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
+ FALSE /*bInitialState*/, NULL /*lpName*/);
+ if (pThis->hEvent != NULL)
+ {
+ RT_ZERO(pThis->OverlappedIO);
+ pThis->OverlappedIO.Internal = STATUS_PENDING;
+ pThis->OverlappedIO.hEvent = pThis->hEvent;
+
+ rc = rtLocalIpcServerWinCreatePipeInstance(&pThis->hNmPipe, pThis->wszName, true /* fFirst */);
+ if (RT_SUCCESS(rc))
+ {
+ *phServer = pThis;
+ return VINF_SUCCESS;
+ }
+
+ BOOL fRc = CloseHandle(pThis->hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ int rc2 = RTCritSectDelete(&pThis->CritSect);
+ AssertRC(rc2);
+ }
+ }
+ RTMemFree(pThis);
+ }
+ return rc;
+}
+
+
+/**
+ * Retains a reference to the server instance.
+ *
+ * @param pThis The server instance.
+ */
+DECLINLINE(void) rtLocalIpcServerRetain(PRTLOCALIPCSERVERINT pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs);
+}
+
+
+/**
+ * Call when the reference count reaches 0.
+ *
+ * Caller owns the critsect.
+ *
+ * @returns VINF_OBJECT_DESTROYED
+ * @param pThis The instance to destroy.
+ */
+DECL_NO_INLINE(static, int) rtLocalIpcServerWinDestroy(PRTLOCALIPCSERVERINT pThis)
+{
+ Assert(pThis->u32Magic == ~RTLOCALIPCSERVER_MAGIC);
+ pThis->u32Magic = ~RTLOCALIPCSERVER_MAGIC;
+
+ BOOL fRc = CloseHandle(pThis->hNmPipe);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ pThis->hNmPipe = INVALID_HANDLE_VALUE;
+
+ fRc = CloseHandle(pThis->hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ pThis->hEvent = NULL;
+
+ RTCritSectLeave(&pThis->CritSect);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+ return VINF_OBJECT_DESTROYED;
+}
+
+
+/**
+ * Server instance destructor.
+ *
+ * @returns VINF_OBJECT_DESTROYED
+ * @param pThis The server instance.
+ */
+DECL_NO_INLINE(static, int) rtLocalIpcServerDtor(PRTLOCALIPCSERVERINT pThis)
+{
+ RTCritSectEnter(&pThis->CritSect);
+ return rtLocalIpcServerWinDestroy(pThis);
+}
+
+
+/**
+ * Releases a reference to the server instance.
+ *
+ * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
+ * @param pThis The server instance.
+ */
+DECLINLINE(int) rtLocalIpcServerRelease(PRTLOCALIPCSERVERINT pThis)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (!cRefs)
+ return rtLocalIpcServerDtor(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Releases a reference to the server instance and leaves the critsect.
+ *
+ * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
+ * @param pThis The server instance.
+ */
+DECLINLINE(int) rtLocalIpcServerReleaseAndUnlock(PRTLOCALIPCSERVERINT pThis)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (!cRefs)
+ return rtLocalIpcServerWinDestroy(pThis);
+ return RTCritSectLeave(&pThis->CritSect);
+}
+
+
+
+RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer)
+{
+ /*
+ * Validate input.
+ */
+ if (hServer == NIL_RTLOCALIPCSERVER)
+ return VINF_SUCCESS;
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Cancel any thread currently busy using the server,
+ * leaving the cleanup to it.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC, RTLOCALIPCSERVER_MAGIC), VERR_WRONG_ORDER);
+
+ RTCritSectEnter(&pThis->CritSect);
+
+ /* Cancel everything. */
+ ASMAtomicUoWriteBool(&pThis->fCancelled, true);
+ if (pThis->cRefs > 1)
+ {
+ BOOL fRc = SetEvent(pThis->hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ }
+
+ return rtLocalIpcServerReleaseAndUnlock(pThis);
+}
+
+
+RTDECL(int) RTLocalIpcServerGrantGroupAccess(RTLOCALIPCSERVER hServer, RTGID gid)
+{
+ RT_NOREF_PV(hServer); RT_NOREF(gid);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(phClientSession, VERR_INVALID_POINTER);
+
+ /*
+ * Enter the critsect before inspecting the object further.
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, rc);
+
+ rtLocalIpcServerRetain(pThis);
+ if (!pThis->fCancelled)
+ {
+ ResetEvent(pThis->hEvent);
+
+ RTCritSectLeave(&pThis->CritSect);
+
+ /*
+ * Try connect a client. We need to use overlapped I/O here because
+ * of the cancellation done by RTLocalIpcServerCancel and RTLocalIpcServerDestroy.
+ */
+ SetLastError(NO_ERROR);
+ BOOL fRc = ConnectNamedPipe(pThis->hNmPipe, &pThis->OverlappedIO);
+ DWORD dwErr = fRc ? NO_ERROR : GetLastError();
+ if ( !fRc
+ && dwErr == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(pThis->hEvent, INFINITE);
+ DWORD dwIgnored;
+ fRc = GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &dwIgnored, FALSE /* bWait*/);
+ dwErr = fRc ? NO_ERROR : GetLastError();
+ }
+
+ RTCritSectEnter(&pThis->CritSect);
+ if ( !pThis->fCancelled /* Event signalled but not cancelled? */
+ && pThis->u32Magic == RTLOCALIPCSERVER_MAGIC)
+ {
+ /*
+ * Still alive, some error or an actual client.
+ *
+ * If it's the latter we'll have to create a new pipe instance that
+ * replaces the current one for the server. The current pipe instance
+ * will be assigned to the client session.
+ */
+ if ( fRc
+ || dwErr == ERROR_PIPE_CONNECTED)
+ {
+ HANDLE hNmPipe;
+ rc = rtLocalIpcServerWinCreatePipeInstance(&hNmPipe, pThis->wszName, false /* fFirst */);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hNmPipeSession = pThis->hNmPipe; /* consumed */
+ pThis->hNmPipe = hNmPipe;
+ rc = rtLocalIpcWinCreateSession(phClientSession, hNmPipeSession);
+ }
+ else
+ {
+ /*
+ * We failed to create a new instance for the server, disconnect
+ * the client and fail. Don't try service the client here.
+ */
+ fRc = DisconnectNamedPipe(pThis->hNmPipe);
+ AssertMsg(fRc, ("%d\n", GetLastError()));
+ }
+ }
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ else
+ {
+ /*
+ * Cancelled.
+ *
+ * Cancel the overlapped io if it didn't complete (must be done
+ * in the this thread) or disconnect the client.
+ */
+ Assert(pThis->fCancelled);
+ if ( fRc
+ || dwErr == ERROR_PIPE_CONNECTED)
+ fRc = DisconnectNamedPipe(pThis->hNmPipe);
+ else if (dwErr == ERROR_IO_PENDING)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtCancelIoFile(pThis->hNmPipe, &Ios);
+ fRc = NT_SUCCESS(rcNt);
+ }
+ else
+ fRc = TRUE;
+ AssertMsg(fRc, ("%d\n", GetLastError()));
+ rc = VERR_CANCELLED;
+ }
+ }
+ else
+ {
+ /*pThis->fCancelled = false; - Terrible interface idea. Add API to clear fCancelled if ever required. */
+ rc = VERR_CANCELLED;
+ }
+ rtLocalIpcServerReleaseAndUnlock(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Enter the critical section, then set the cancellation flag
+ * and signal the event (to wake up anyone in/at WaitForSingleObject).
+ */
+ rtLocalIpcServerRetain(pThis);
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicUoWriteBool(&pThis->fCancelled, true);
+
+ BOOL fRc = SetEvent(pThis->hEvent);
+ if (fRc)
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("dwErr=%u\n", dwErr));
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+
+ rtLocalIpcServerReleaseAndUnlock(pThis);
+ }
+ else
+ rtLocalIpcServerRelease(pThis);
+ return rc;
+}
+
+
+/**
+ * Create a session instance for a new server client or a client connect.
+ *
+ * @returns IPRT status code.
+ *
+ * @param ppSession Where to store the session handle on success.
+ * @param hNmPipeSession The named pipe handle if server calling,
+ * INVALID_HANDLE_VALUE if client connect. This will
+ * be consumed by this session, meaning on failure to
+ * create the session it will be closed.
+ */
+static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession)
+{
+ AssertPtr(ppSession);
+
+ /*
+ * Allocate and initialize the session instance data.
+ */
+ int rc;
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
+ pThis->cRefs = 1; /* our ref */
+ pThis->fCancelled = false;
+ pThis->fZeroByteRead = false;
+ pThis->fServerSide = hNmPipeSession != INVALID_HANDLE_VALUE;
+ pThis->hNmPipe = hNmPipeSession;
+#if 0 /* Non-blocking writes are not yet supported. */
+ pThis->pbBounceBuf = NULL;
+ pThis->cbBounceBufAlloc = 0;
+ pThis->cbBounceBufUsed = 0;
+#endif
+ rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Read.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
+ FALSE /*bInitialState*/, NULL /*lpName*/);
+ if (pThis->Read.hEvent != NULL)
+ {
+ pThis->Read.OverlappedIO.Internal = STATUS_PENDING;
+ pThis->Read.OverlappedIO.hEvent = pThis->Read.hEvent;
+ pThis->Read.hActiveThread = NIL_RTTHREAD;
+
+ pThis->Write.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
+ FALSE /*bInitialState*/, NULL /*lpName*/);
+ if (pThis->Write.hEvent != NULL)
+ {
+ pThis->Write.OverlappedIO.Internal = STATUS_PENDING;
+ pThis->Write.OverlappedIO.hEvent = pThis->Write.hEvent;
+ pThis->Write.hActiveThread = NIL_RTTHREAD;
+
+ *ppSession = pThis;
+ return VINF_SUCCESS;
+ }
+
+ CloseHandle(pThis->Read.hEvent);
+ }
+
+ /* bail out */
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTCritSectDelete(&pThis->CritSect);
+ }
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (hNmPipeSession != INVALID_HANDLE_VALUE)
+ {
+ BOOL fRc = CloseHandle(hNmPipeSession);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phSession, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTLOCALIPC_C_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
+
+ size_t cwcFullName;
+ int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a session (shared with server client session creation).
+ */
+ PRTLOCALIPCSESSIONINT pThis;
+ rc = rtLocalIpcWinCreateSession(&pThis, INVALID_HANDLE_VALUE);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try open the pipe.
+ */
+ PSECURITY_DESCRIPTOR pSecDesc;
+ rc = rtLocalIpcServerWinAllocSecurityDescriptor(&pSecDesc, false /*fServer*/);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszFullName = RTUtf16Alloc((cwcFullName + 1) * sizeof(RTUTF16));
+ if (pwszFullName)
+ rc = rtLocalIpcWinConstructName(pszName, pwszFullName, cwcFullName,
+ RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
+ else
+ rc = VERR_NO_UTF16_MEMORY;
+ if (RT_SUCCESS(rc))
+ {
+ SECURITY_ATTRIBUTES SecAttrs;
+ SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+ SecAttrs.lpSecurityDescriptor = pSecDesc;
+ SecAttrs.bInheritHandle = FALSE;
+
+ /* The SECURITY_XXX flags are needed in order to prevent the server from impersonating with
+ this thread's security context (supported at least back to NT 3.51). See @bugref{9773}. */
+ HANDLE hPipe = CreateFileW(pwszFullName,
+ GENERIC_READ | GENERIC_WRITE,
+ 0 /*no sharing*/,
+ &SecAttrs,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS,
+ NULL /*no template handle*/);
+ if (hPipe != INVALID_HANDLE_VALUE)
+ {
+ pThis->hNmPipe = hPipe;
+
+ LocalFree(pSecDesc);
+ RTUtf16Free(pwszFullName);
+
+ /*
+ * We're done!
+ */
+ *phSession = pThis;
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+
+ RTUtf16Free(pwszFullName);
+ LocalFree(pSecDesc);
+ }
+
+ /* destroy the session handle. */
+ CloseHandle(pThis->Read.hEvent);
+ CloseHandle(pThis->Write.hEvent);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Cancells all pending I/O operations, forcing the methods to return with
+ * VERR_CANCELLED (unless they've got actual data to return).
+ *
+ * Used by RTLocalIpcSessionCancel and RTLocalIpcSessionClose.
+ *
+ * @returns IPRT status code.
+ * @param pThis The client session instance.
+ */
+static int rtLocalIpcWinCancel(PRTLOCALIPCSESSIONINT pThis)
+{
+ ASMAtomicUoWriteBool(&pThis->fCancelled, true);
+
+ /*
+ * Call NtCancelIoFile since this call cancels both read and write
+ * oriented operations.
+ */
+ if ( pThis->fZeroByteRead
+ || pThis->Read.hActiveThread != NIL_RTTHREAD
+ || pThis->Write.hActiveThread != NIL_RTTHREAD)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NtCancelIoFile(pThis->hNmPipe, &Ios);
+ }
+
+ /*
+ * Set both event semaphores.
+ */
+ BOOL fRc = SetEvent(pThis->Read.hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ fRc = SetEvent(pThis->Write.hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Retains a reference to the session instance.
+ *
+ * @param pThis The client session instance.
+ */
+DECLINLINE(void) rtLocalIpcSessionRetain(PRTLOCALIPCSESSIONINT pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs);
+}
+
+
+RTDECL(uint32_t) RTLocalIpcSessionRetain(RTLOCALIPCSESSION hSession)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2 && cRefs);
+ return cRefs;
+}
+
+
+/**
+ * Call when the reference count reaches 0.
+ *
+ * Caller owns the critsect.
+ *
+ * @returns VINF_OBJECT_DESTROYED
+ * @param pThis The instance to destroy.
+ */
+DECL_NO_INLINE(static, int) rtLocalIpcSessionWinDestroy(PRTLOCALIPCSESSIONINT pThis)
+{
+ BOOL fRc = CloseHandle(pThis->hNmPipe);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ pThis->hNmPipe = INVALID_HANDLE_VALUE;
+
+ fRc = CloseHandle(pThis->Write.hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError()));
+ pThis->Write.hEvent = NULL;
+
+ fRc = CloseHandle(pThis->Read.hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError()));
+ pThis->Read.hEvent = NULL;
+
+ int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+ return VINF_OBJECT_DESTROYED;
+}
+
+
+/**
+ * Releases a reference to the session instance and unlock it.
+ *
+ * @returns VINF_SUCCESS or VINF_OBJECT_DESTROYED as appropriate.
+ * @param pThis The session instance.
+ */
+DECLINLINE(int) rtLocalIpcSessionReleaseAndUnlock(PRTLOCALIPCSESSIONINT pThis)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (!cRefs)
+ return rtLocalIpcSessionWinDestroy(pThis);
+
+ int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2);
+ Log(("rtLocalIpcSessionReleaseAndUnlock: %u refs left\n", cRefs));
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTLocalIpcSessionRelease(RTLOCALIPCSESSION hSession)
+{
+ if (hSession == NIL_RTLOCALIPCSESSION)
+ return 0;
+
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (cRefs)
+ Log(("RTLocalIpcSessionRelease: %u refs left\n", cRefs));
+ else
+ {
+ RTCritSectEnter(&pThis->CritSect);
+ rtLocalIpcSessionWinDestroy(pThis);
+ }
+ return cRefs;
+}
+
+
+RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
+{
+ /*
+ * Validate input.
+ */
+ if (hSession == NIL_RTLOCALIPCSESSION)
+ return VINF_SUCCESS;
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the instance, cancel all outstanding I/O and drop our reference.
+ */
+ RTCritSectEnter(&pThis->CritSect);
+ rtLocalIpcWinCancel(pThis);
+ return rtLocalIpcSessionReleaseAndUnlock(pThis);
+}
+
+
+/**
+ * Handles WaitForSingleObject return value when waiting for a zero byte read.
+ *
+ * The zero byte read is started by the RTLocalIpcSessionWaitForData method and
+ * left pending when the function times out. This saves us the problem of
+ * NtCancelIoFile messing with all active I/O operations and the trouble of
+ * restarting the zero byte read the next time the method is called. However
+ * should RTLocalIpcSessionRead be called after a failed
+ * RTLocalIpcSessionWaitForData call, the zero byte read will still be pending
+ * and it must wait for it to complete before the OVERLAPPEDIO structure can be
+ * reused.
+ *
+ * Thus, both functions will do WaitForSingleObject and share this routine to
+ * handle the outcome.
+ *
+ * @returns IPRT status code.
+ * @param pThis The session instance.
+ * @param rcWait The WaitForSingleObject return code.
+ */
+static int rtLocalIpcWinGetZeroReadResult(PRTLOCALIPCSESSIONINT pThis, DWORD rcWait)
+{
+ int rc;
+ DWORD cbRead = 42;
+ if (rcWait == WAIT_OBJECT_0)
+ {
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, !pThis->fCancelled /*fWait*/))
+ {
+ Assert(cbRead == 0);
+ rc = VINF_SUCCESS;
+ pThis->fZeroByteRead = false;
+ }
+ else if (pThis->fCancelled)
+ rc = VERR_CANCELLED;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ /* We try get the result here too, just in case we're lucky, but no waiting. */
+ DWORD dwErr = GetLastError();
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, FALSE /*fWait*/))
+ {
+ Assert(cbRead == 0);
+ rc = VINF_SUCCESS;
+ pThis->fZeroByteRead = false;
+ }
+ else if (rcWait == WAIT_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else if (rcWait == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ /* pcbRead is optional. */
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rtLocalIpcSessionRetain(pThis);
+ if (pThis->Read.hActiveThread == NIL_RTTHREAD)
+ {
+ pThis->Read.hActiveThread = RTThreadSelf();
+
+ size_t cbTotalRead = 0;
+ while (cbToRead > 0)
+ {
+ DWORD cbRead = 0;
+ if (!pThis->fCancelled)
+ {
+ /*
+ * Wait for pending zero byte read, if necessary.
+ * Note! It cannot easily be cancelled due to concurrent current writes.
+ */
+ if (!pThis->fZeroByteRead)
+ { /* likely */ }
+ else
+ {
+ RTCritSectLeave(&pThis->CritSect);
+ DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, RT_MS_1MIN);
+ RTCritSectEnter(&pThis->CritSect);
+
+ rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
+ if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
+ continue;
+ break;
+ }
+
+ /*
+ * Kick of a an overlapped read. It should return immediately if
+ * there is bytes in the buffer. If not, we'll cancel it and see
+ * what we get back.
+ */
+ rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE);
+ RTCritSectLeave(&pThis->CritSect);
+
+ if (ReadFile(pThis->hNmPipe, pvBuf,
+ cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
+ &cbRead, &pThis->Read.OverlappedIO))
+ {
+ RTCritSectEnter(&pThis->CritSect);
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE);
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/))
+ rc = VINF_SUCCESS;
+ else
+ {
+ if (pThis->fCancelled)
+ rc = VERR_CANCELLED;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailedBreak(("%Rrc\n", rc));
+ }
+ }
+ else
+ {
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ /* Advance. */
+ cbToRead -= cbRead;
+ cbTotalRead += cbRead;
+ pvBuf = (uint8_t *)pvBuf + cbRead;
+ }
+
+ if (pcbRead)
+ {
+ *pcbRead = cbTotalRead;
+ if ( RT_FAILURE(rc)
+ && cbTotalRead
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+
+ pThis->Read.hActiveThread = NIL_RTTHREAD;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ rtLocalIpcSessionReleaseAndUnlock(pThis);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionReadNB(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
+ *pcbRead = 0;
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rtLocalIpcSessionRetain(pThis);
+ if (pThis->Read.hActiveThread == NIL_RTTHREAD)
+ {
+ pThis->Read.hActiveThread = RTThreadSelf();
+
+ for (;;)
+ {
+ DWORD cbRead = 0;
+ if (!pThis->fCancelled)
+ {
+ /*
+ * Wait for pending zero byte read, if necessary.
+ * Note! It cannot easily be cancelled due to concurrent current writes.
+ */
+ if (!pThis->fZeroByteRead)
+ { /* likely */ }
+ else
+ {
+ RTCritSectLeave(&pThis->CritSect);
+ DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0);
+ RTCritSectEnter(&pThis->CritSect);
+
+ rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
+ if (RT_SUCCESS(rc))
+ continue;
+
+ if (rc == VERR_TIMEOUT)
+ rc = VINF_TRY_AGAIN;
+ break;
+ }
+
+ /*
+ * Figure out how much we can read (cannot try and cancel here
+ * like in the anonymous pipe code).
+ */
+ DWORD cbAvailable;
+ if (PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL))
+ {
+ if (cbAvailable == 0 || cbToRead == 0)
+ {
+ *pcbRead = 0;
+ rc = VINF_TRY_AGAIN;
+ break;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+ }
+ if (cbAvailable > cbToRead)
+ cbAvailable = (DWORD)cbToRead;
+
+ /*
+ * Kick of a an overlapped read. It should return immediately, so we
+ * don't really need to leave the critsect here.
+ */
+ rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE);
+ if (ReadFile(pThis->hNmPipe, pvBuf, cbAvailable, &cbRead, &pThis->Read.OverlappedIO))
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0);
+ if (rcWait == WAIT_TIMEOUT)
+ {
+ RTCritSectLeave(&pThis->CritSect);
+ rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE);
+ RTCritSectEnter(&pThis->CritSect);
+ }
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/))
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ if (pThis->fCancelled)
+ rc = VERR_CANCELLED;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailedBreak(("%Rrc\n", rc));
+ }
+ }
+ else
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ pThis->Read.hActiveThread = NIL_RTTHREAD;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ rtLocalIpcSessionReleaseAndUnlock(pThis);
+ }
+
+ return rc;
+}
+
+
+#if 0 /* Non-blocking writes are not yet supported. */
+/**
+ * Common worker for handling I/O completion.
+ *
+ * This is used by RTLocalIpcSessionClose and RTLocalIpcSessionWrite.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ */
+static int rtLocalIpcSessionWriteCheckCompletion(PRTLOCALIPCSESSIONINT pThis)
+{
+ int rc;
+ DWORD rcWait = WaitForSingleObject(pThis->OverlappedIO.hEvent, 0);
+ if (rcWait == WAIT_OBJECT_0)
+ {
+ DWORD cbWritten = 0;
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE))
+ {
+ for (;;)
+ {
+ if (cbWritten >= pThis->cbBounceBufUsed)
+ {
+ pThis->fIOPending = false;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* resubmit the remainder of the buffer - can this actually happen? */
+ memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
+ rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
+ if (!WriteFile(pThis->hNmPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
+ &cbWritten, &pThis->OverlappedIO))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_IO_PENDING)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fIOPending = false;
+ if (dwErr == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ break;
+ }
+ Assert(cbWritten > 0);
+ }
+ }
+ else
+ {
+ pThis->fIOPending = false;
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else if (rcWait == WAIT_TIMEOUT)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fIOPending = false;
+ if (rcWait == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ return rc;
+}
+#endif
+
+
+RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuf, size_t cbToWrite)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite, VERR_INVALID_PARAMETER);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rtLocalIpcSessionRetain(pThis);
+ if (pThis->Write.hActiveThread == NIL_RTTHREAD)
+ {
+ pThis->Write.hActiveThread = RTThreadSelf();
+
+ /*
+ * Try write everything. No bounce buffering necessary.
+ */
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ DWORD cbWritten = 0;
+ if (!pThis->fCancelled)
+ {
+ BOOL fRc = ResetEvent(pThis->Write.OverlappedIO.hEvent); Assert(fRc == TRUE);
+ RTCritSectLeave(&pThis->CritSect);
+
+ DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
+ fRc = WriteFile(pThis->hNmPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Write.OverlappedIO);
+ if (fRc)
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_IO_PENDING)
+ {
+ DWORD rcWait = WaitForSingleObject(pThis->Write.OverlappedIO.hEvent, INFINITE);
+ if (rcWait == WAIT_OBJECT_0)
+ {
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->Write.OverlappedIO, &cbWritten, TRUE /*fWait*/))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (rcWait == WAIT_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else if (rcWait == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (dwErr == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+
+ if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
+ cbWritten = cbToWriteInThisIteration;
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else
+ {
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ /* Advance. */
+ pvBuf = (char const *)pvBuf + cbWritten;
+ cbTotalWritten += cbWritten;
+ cbToWrite -= cbWritten;
+ }
+
+ pThis->Write.hActiveThread = NIL_RTTHREAD;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ rtLocalIpcSessionReleaseAndUnlock(pThis);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->Write.hActiveThread == NIL_RTTHREAD)
+ {
+ /* No flushing on Windows needed since RTLocalIpcSessionWrite will block until
+ * all data was written (or an error occurred). */
+ /** @todo r=bird: above comment is misinformed.
+ * Implement this as soon as we want an explicit asynchronous version of
+ * RTLocalIpcSessionWrite on Windows. */
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ uint64_t const msStart = RTTimeMilliTS();
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rtLocalIpcSessionRetain(pThis);
+ if (pThis->Read.hActiveThread == NIL_RTTHREAD)
+ {
+ pThis->Read.hActiveThread = RTThreadSelf();
+
+ /*
+ * Wait loop.
+ */
+ for (unsigned iLoop = 0;; iLoop++)
+ {
+ /*
+ * Check for cancellation before we continue.
+ */
+ if (!pThis->fCancelled)
+ { /* likely */ }
+ else
+ {
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ /*
+ * Prep something we can wait on.
+ */
+ HANDLE hWait = INVALID_HANDLE_VALUE;
+ if (pThis->fZeroByteRead)
+ hWait = pThis->Read.OverlappedIO.hEvent;
+ else
+ {
+ /* Peek at the pipe buffer and see how many bytes it contains. */
+ DWORD cbAvailable;
+ if ( PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL)
+ && cbAvailable)
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* Start a zero byte read operation that we can wait on. */
+ if (cMillies == 0)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ BOOL fRc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(fRc == TRUE); NOREF(fRc);
+ DWORD cbRead = 0;
+ if (ReadFile(pThis->hNmPipe, pThis->abBuf, 0 /*cbToRead*/, &cbRead, &pThis->Read.OverlappedIO))
+ {
+ rc = VINF_SUCCESS;
+ if (iLoop > 10)
+ RTThreadYield();
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->fZeroByteRead = true;
+ hWait = pThis->Read.OverlappedIO.hEvent;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ /*
+ * Check for timeout.
+ */
+ DWORD cMsMaxWait = INFINITE; /* (MSC maybe used uninitialized) */
+ if (cMillies == RT_INDEFINITE_WAIT)
+ cMsMaxWait = INFINITE;
+ else if ( hWait != INVALID_HANDLE_VALUE
+ || iLoop > 10)
+ {
+ uint64_t cMsElapsed = RTTimeMilliTS() - msStart;
+ if (cMsElapsed <= cMillies)
+ cMsMaxWait = cMillies - (uint32_t)cMsElapsed;
+ else if (iLoop == 0)
+ cMsMaxWait = cMillies ? 1 : 0;
+ else
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ }
+
+ /*
+ * Wait and collect the result.
+ */
+ if (hWait != INVALID_HANDLE_VALUE)
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ DWORD rcWait = WaitForSingleObject(hWait, cMsMaxWait);
+
+ int rc2 = RTCritSectEnter(&pThis->CritSect);
+ AssertRC(rc2);
+
+ rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
+ break;
+ }
+ }
+
+ pThis->Read.hActiveThread = NIL_RTTHREAD;
+ }
+
+ rtLocalIpcSessionReleaseAndUnlock(pThis);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Enter the critical section, then set the cancellation flag
+ * and signal the event (to wake up anyone in/at WaitForSingleObject).
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rtLocalIpcSessionRetain(pThis);
+ rc = rtLocalIpcWinCancel(pThis);
+ rtLocalIpcSessionReleaseAndUnlock(pThis);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
+{
+ RT_NOREF_PV(hSession); RT_NOREF_PV(pProcess);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
+{
+ RT_NOREF_PV(hSession); RT_NOREF_PV(pUid);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTGID pGid)
+{
+ RT_NOREF_PV(hSession); RT_NOREF_PV(pGid);
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/Runtime/r3/win/mp-win.cpp b/src/VBox/Runtime/r3/win/mp-win.cpp
new file mode 100644
index 00000000..b76ab254
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/mp-win.cpp
@@ -0,0 +1,822 @@
+/* $Id: mp-win.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYSTEM
+#include <iprt/win/windows.h>
+
+#include <iprt/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/cpuset.h>
+#include <iprt/ldr.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/param.h>
+#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#if defined(VBOX) && !defined(IN_GUEST) && !defined(IN_RT_STATIC)
+# include <VBox/sup.h>
+# define IPRT_WITH_GIP_MP_INFO
+#else
+# undef IPRT_WITH_GIP_MP_INFO
+#endif
+
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def RTMPWIN_UPDATE_GIP_GLOBALS
+ * Does lazy (re-)initialization using information provieded by GIP. */
+#ifdef IPRT_WITH_GIP_MP_INFO
+# define RTMPWIN_UPDATE_GIP_GLOBALS() \
+ do { RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP(); } while (0)
+#else
+# define RTMPWIN_UPDATE_GIP_GLOBALS() do { } while (0)
+#endif
+
+/** @def RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP
+ * Does lazy (re-)initialization using information provieded by GIP and
+ * declare and initalize a pGip local variable. */
+#ifdef IPRT_WITH_GIP_MP_INFO
+#define RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP() \
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage; \
+ if (pGip) \
+ { \
+ if ( pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC \
+ && RTOnce(&g_MpInitOnceGip, rtMpWinInitOnceGip, NULL) == VINF_SUCCESS) \
+ { \
+ if (g_cRtMpWinActiveCpus >= pGip->cOnlineCpus) \
+ { /* likely */ } \
+ else \
+ rtMpWinRefreshGip(); \
+ } \
+ else \
+ pGip = NULL; \
+ } else do { } while (0)
+#else
+# define RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP() do { } while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Initialize once. */
+static RTONCE g_MpInitOnce = RTONCE_INITIALIZER;
+#ifdef IPRT_WITH_GIP_MP_INFO
+/** Initialize once using GIP. */
+static RTONCE g_MpInitOnceGip = RTONCE_INITIALIZER;
+#endif
+
+static decltype(GetMaximumProcessorCount) *g_pfnGetMaximumProcessorCount;
+//static decltype(GetActiveProcessorCount) *g_pfnGetActiveProcessorCount;
+static decltype(GetCurrentProcessorNumber) *g_pfnGetCurrentProcessorNumber;
+static decltype(GetCurrentProcessorNumberEx) *g_pfnGetCurrentProcessorNumberEx;
+static decltype(GetLogicalProcessorInformation) *g_pfnGetLogicalProcessorInformation;
+static decltype(GetLogicalProcessorInformationEx) *g_pfnGetLogicalProcessorInformationEx;
+
+
+/** The required buffer size for getting group relations. */
+static uint32_t g_cbRtMpWinGrpRelBuf;
+/** The max number of CPUs (RTMpGetCount). */
+static uint32_t g_cRtMpWinMaxCpus;
+/** The max number of CPU cores (RTMpGetCoreCount). */
+static uint32_t g_cRtMpWinMaxCpuCores;
+/** The max number of groups. */
+static uint32_t g_cRtMpWinMaxCpuGroups;
+/** The number of active CPUs the last time we checked. */
+static uint32_t volatile g_cRtMpWinActiveCpus;
+/** Static per group info.
+ * @remarks With 256 entries this takes up 33KB.
+ * @sa g_aRtMpNtCpuGroups */
+static struct
+{
+ /** The max CPUs in the group. */
+ uint16_t cMaxCpus;
+ /** The number of active CPUs at the time of initialization. */
+ uint16_t cActiveCpus;
+ /** CPU set indexes for each CPU in the group. */
+ int16_t aidxCpuSetMembers[64];
+} g_aRtMpWinCpuGroups[256];
+/** Maps CPU set indexes to RTCPUID.
+ * @sa g_aidRtMpNtByCpuSetIdx */
+RTCPUID g_aidRtMpWinByCpuSetIdx[RTCPUSET_MAX_CPUS];
+
+
+/**
+ * @callback_method_impl{FNRTONCE,
+ * Resolves dynamic imports and initializes globals.}
+ */
+static DECLCALLBACK(int32_t) rtMpWinInitOnce(void *pvUser)
+{
+ RT_NOREF(pvUser);
+
+ Assert(g_WinOsInfoEx.dwOSVersionInfoSize != 0);
+ Assert(g_hModKernel32 != NULL);
+
+ /*
+ * Resolve dynamic APIs.
+ */
+#define RESOLVE_API(a_szMod, a_FnName) \
+ do { \
+ RT_CONCAT(g_pfn,a_FnName) = (decltype(a_FnName) *)GetProcAddress(g_hModKernel32, #a_FnName); \
+ } while (0)
+ RESOLVE_API("kernel32.dll", GetMaximumProcessorCount);
+ //RESOLVE_API("kernel32.dll", GetActiveProcessorCount); - slow :/
+ RESOLVE_API("kernel32.dll", GetCurrentProcessorNumber);
+ RESOLVE_API("kernel32.dll", GetCurrentProcessorNumberEx);
+ RESOLVE_API("kernel32.dll", GetLogicalProcessorInformation);
+ RESOLVE_API("kernel32.dll", GetLogicalProcessorInformationEx);
+
+ /*
+ * Reset globals.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx); i++)
+ g_aidRtMpWinByCpuSetIdx[i] = NIL_RTCPUID;
+ for (unsigned idxGroup = 0; idxGroup < RT_ELEMENTS(g_aRtMpWinCpuGroups); idxGroup++)
+ {
+ g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = 0;
+ g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = 0;
+ for (unsigned idxMember = 0; idxMember < RT_ELEMENTS(g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers); idxMember++)
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = -1;
+ }
+
+ /*
+ * Query group information, partitioning CPU IDs and CPU set indexes.
+ *
+ * We ASSUME the GroupInfo index is the same as the group number.
+ *
+ * We CANNOT ASSUME that the kernel CPU indexes are assigned in any given
+ * way, though they usually are in group order by active processor. So,
+ * we do that to avoid trouble. We must use information provided thru GIP
+ * if we want the kernel CPU set indexes. Even there, the inactive CPUs
+ * wont have sensible indexes. Sigh.
+ *
+ * We try to assign IDs to inactive CPUs in the same manner as mp-r0drv-nt.cpp
+ *
+ * Note! We will die (AssertFatal) if there are too many processors!
+ */
+ union
+ {
+ SYSTEM_INFO SysInfo;
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Info;
+ uint8_t abPaddingG[ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
+ + sizeof(PROCESSOR_GROUP_INFO) * 256];
+ uint8_t abPaddingC[ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
+ + (sizeof(PROCESSOR_RELATIONSHIP) + sizeof(GROUP_AFFINITY))
+ * RTCPUSET_MAX_CPUS];
+ } uBuf;
+ if (g_pfnGetLogicalProcessorInformationEx)
+ {
+ /* Query the information. */
+ DWORD cbData = sizeof(uBuf);
+ AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationGroup, &uBuf.Info, &cbData) != FALSE,
+ ("last error = %u, cbData = %u (in %u)\n", GetLastError(), cbData, sizeof(uBuf)));
+ AssertFatalMsg(uBuf.Info.Relationship == RelationGroup,
+ ("Relationship = %u, expected %u!\n", uBuf.Info.Relationship, RelationGroup));
+ AssertFatalMsg(uBuf.Info.Group.MaximumGroupCount <= RT_ELEMENTS(g_aRtMpWinCpuGroups),
+ ("MaximumGroupCount is %u, we only support up to %u!\n",
+ uBuf.Info.Group.MaximumGroupCount, RT_ELEMENTS(g_aRtMpWinCpuGroups)));
+
+ AssertMsg(uBuf.Info.Group.MaximumGroupCount == uBuf.Info.Group.ActiveGroupCount, /* 2nd assumption mentioned above. */
+ ("%u vs %u\n", uBuf.Info.Group.MaximumGroupCount, uBuf.Info.Group.ActiveGroupCount));
+ AssertFatal(uBuf.Info.Group.MaximumGroupCount >= uBuf.Info.Group.ActiveGroupCount);
+
+ g_cRtMpWinMaxCpuGroups = uBuf.Info.Group.MaximumGroupCount;
+
+ /* Count max cpus (see mp-r0drv0-nt.cpp) why we don't use GetMaximumProcessorCount(ALL). */
+ uint32_t idxGroup;
+ g_cRtMpWinMaxCpus = 0;
+ for (idxGroup = 0; idxGroup < uBuf.Info.Group.ActiveGroupCount; idxGroup++)
+ g_cRtMpWinMaxCpus += uBuf.Info.Group.GroupInfo[idxGroup].MaximumProcessorCount;
+
+ /* Process the active groups. */
+ uint32_t cActive = 0;
+ uint32_t cInactive = 0;
+ uint32_t idxCpu = 0;
+ uint32_t idxCpuSetNextInactive = g_cRtMpWinMaxCpus - 1;
+ for (idxGroup = 0; idxGroup < uBuf.Info.Group.ActiveGroupCount; idxGroup++)
+ {
+ PROCESSOR_GROUP_INFO const *pGroupInfo = &uBuf.Info.Group.GroupInfo[idxGroup];
+ g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = pGroupInfo->MaximumProcessorCount;
+ g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = pGroupInfo->ActiveProcessorCount;
+ for (uint32_t idxMember = 0; idxMember < pGroupInfo->MaximumProcessorCount; idxMember++)
+ {
+ if (pGroupInfo->ActiveProcessorMask & RT_BIT_64(idxMember))
+ {
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpu;
+ g_aidRtMpWinByCpuSetIdx[idxCpu] = idxCpu;
+ idxCpu++;
+ cActive++;
+ }
+ else
+ {
+ if (idxCpuSetNextInactive >= idxCpu)
+ {
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive;
+ g_aidRtMpWinByCpuSetIdx[idxCpuSetNextInactive] = idxCpuSetNextInactive;
+ idxCpuSetNextInactive--;
+ }
+ cInactive++;
+ }
+ }
+ }
+ g_cRtMpWinActiveCpus = cActive;
+ Assert(cActive + cInactive <= g_cRtMpWinMaxCpus);
+ Assert(idxCpu <= idxCpuSetNextInactive + 1);
+ Assert(idxCpu <= g_cRtMpWinMaxCpus);
+
+ /* Just in case the 2nd assumption doesn't hold true and there are inactive groups. */
+ for (; idxGroup < uBuf.Info.Group.MaximumGroupCount; idxGroup++)
+ {
+ DWORD cMaxMembers = g_pfnGetMaximumProcessorCount(idxGroup);
+ g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = cMaxMembers;
+ g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = 0;
+ for (uint32_t idxMember = 0; idxMember < cMaxMembers; idxMember++)
+ {
+ if (idxCpuSetNextInactive >= idxCpu)
+ {
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive;
+ g_aidRtMpWinByCpuSetIdx[idxCpuSetNextInactive] = idxCpuSetNextInactive;
+ idxCpuSetNextInactive--;
+ }
+ cInactive++;
+ }
+ }
+ Assert(cActive + cInactive <= g_cRtMpWinMaxCpus);
+ Assert(idxCpu <= idxCpuSetNextInactive + 1);
+ }
+ else
+ {
+ /* Legacy: */
+ GetSystemInfo(&uBuf.SysInfo);
+ g_cRtMpWinMaxCpuGroups = 1;
+ g_cRtMpWinMaxCpus = uBuf.SysInfo.dwNumberOfProcessors;
+ g_aRtMpWinCpuGroups[0].cMaxCpus = uBuf.SysInfo.dwNumberOfProcessors;
+ g_aRtMpWinCpuGroups[0].cActiveCpus = uBuf.SysInfo.dwNumberOfProcessors;
+
+ for (uint32_t idxMember = 0; idxMember < uBuf.SysInfo.dwNumberOfProcessors; idxMember++)
+ {
+ g_aRtMpWinCpuGroups[0].aidxCpuSetMembers[idxMember] = idxMember;
+ g_aidRtMpWinByCpuSetIdx[idxMember] = idxMember;
+ }
+ }
+
+ AssertFatalMsg(g_cRtMpWinMaxCpus <= RTCPUSET_MAX_CPUS,
+ ("g_cRtMpWinMaxCpus=%u (%#x); RTCPUSET_MAX_CPUS=%u\n", g_cRtMpWinMaxCpus, g_cRtMpWinMaxCpus, RTCPUSET_MAX_CPUS));
+
+ g_cbRtMpWinGrpRelBuf = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
+ + (g_cRtMpWinMaxCpuGroups + 2) * sizeof(PROCESSOR_GROUP_INFO);
+
+ /*
+ * Get information about cores.
+ *
+ * Note! This will only give us info about active processors according to
+ * MSDN, we'll just have to hope that CPUs aren't hotplugged after we
+ * initialize here (or that the API consumers doesn't care too much).
+ */
+ /** @todo A hot CPU plug event would be nice. */
+ g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus;
+ if (g_pfnGetLogicalProcessorInformationEx)
+ {
+ /* Query the information. */
+ DWORD cbData = sizeof(uBuf);
+ AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationProcessorCore, &uBuf.Info, &cbData) != FALSE,
+ ("last error = %u, cbData = %u (in %u)\n", GetLastError(), cbData, sizeof(uBuf)));
+ g_cRtMpWinMaxCpuCores = 0;
+ for (uint32_t off = 0; off < cbData; )
+ {
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pCur = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)&uBuf.abPaddingG[off];
+ AssertFatalMsg(pCur->Relationship == RelationProcessorCore,
+ ("off = %#x, Relationship = %u, expected %u!\n", off, pCur->Relationship, RelationProcessorCore));
+ g_cRtMpWinMaxCpuCores++;
+ off += pCur->Size;
+ }
+
+#if ARCH_BITS == 32
+ if (g_cRtMpWinMaxCpuCores > g_cRtMpWinMaxCpus)
+ {
+ /** @todo WOW64 trouble where the emulation environment has folded the high
+ * processor masks (63..32) into the low (31..0), hiding some
+ * processors from us. Currently we don't deal with that. */
+ g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus;
+ }
+ else
+ AssertStmt(g_cRtMpWinMaxCpuCores > 0, g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus);
+#else
+ AssertStmt(g_cRtMpWinMaxCpuCores > 0 && g_cRtMpWinMaxCpuCores <= g_cRtMpWinMaxCpus,
+ g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus);
+#endif
+ }
+ else
+ {
+ /*
+ * Sadly, on XP and Server 2003, even if the API is present, it does not tell us
+ * how many physical cores there are (any package will look like a single core).
+ * That is worse than not using the API at all, so just skip it unless it's Vista+.
+ */
+ if ( g_pfnGetLogicalProcessorInformation
+ && g_WinOsInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && g_WinOsInfoEx.dwMajorVersion >= 6)
+ {
+ /* Query the info. */
+ DWORD cbSysProcInfo = _4K;
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION paSysInfo = NULL;
+ BOOL fRc = FALSE;
+ do
+ {
+ cbSysProcInfo = RT_ALIGN_32(cbSysProcInfo, 256);
+ void *pv = RTMemRealloc(paSysInfo, cbSysProcInfo);
+ if (!pv)
+ break;
+ paSysInfo = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)pv;
+ fRc = g_pfnGetLogicalProcessorInformation(paSysInfo, &cbSysProcInfo);
+ } while (!fRc && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
+ if (fRc)
+ {
+ /* Count the cores in the result. */
+ g_cRtMpWinMaxCpuCores = 0;
+ uint32_t i = cbSysProcInfo / sizeof(paSysInfo[0]);
+ while (i-- > 0)
+ if (paSysInfo[i].Relationship == RelationProcessorCore)
+ g_cRtMpWinMaxCpuCores++;
+
+ AssertStmt(g_cRtMpWinMaxCpuCores > 0 && g_cRtMpWinMaxCpuCores <= g_cRtMpWinMaxCpus,
+ g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus);
+ }
+ RTMemFree(paSysInfo);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+#ifdef IPRT_WITH_GIP_MP_INFO
+/**
+ * @callback_method_impl{FNRTONCE, Updates globals with information from GIP.}
+ */
+static DECLCALLBACK(int32_t) rtMpWinInitOnceGip(void *pvUser)
+{
+ RT_NOREF(pvUser);
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
+ if ( pGip
+ && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC)
+ {
+ /*
+ * Update globals.
+ */
+ if (g_cRtMpWinMaxCpus != pGip->cPossibleCpus)
+ g_cRtMpWinMaxCpus = pGip->cPossibleCpus;
+ if (g_cRtMpWinActiveCpus != pGip->cOnlineCpus)
+ g_cRtMpWinActiveCpus = pGip->cOnlineCpus;
+ Assert(g_cRtMpWinMaxCpuGroups == pGip->cPossibleCpuGroups);
+ if (g_cRtMpWinMaxCpuGroups != pGip->cPossibleCpuGroups)
+ {
+ g_cRtMpWinMaxCpuGroups = pGip->cPossibleCpuGroups;
+ g_cbRtMpWinGrpRelBuf = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
+ + (g_cRtMpWinMaxCpuGroups + 2) * sizeof(PROCESSOR_GROUP_INFO);
+ }
+
+ /*
+ * Update CPU set IDs.
+ */
+ for (unsigned i = g_cRtMpWinMaxCpus; i < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx); i++)
+ g_aidRtMpWinByCpuSetIdx[i] = NIL_RTCPUID;
+
+ unsigned const cbGip = pGip->cPages * PAGE_SIZE;
+ for (uint32_t idxGroup = 0; idxGroup < g_cRtMpWinMaxCpuGroups; idxGroup++)
+ {
+ uint32_t idxMember;
+ uint32_t offCpuGroup = pGip->aoffCpuGroup[idxGroup];
+ if (offCpuGroup < cbGip)
+ {
+ PSUPGIPCPUGROUP pGipCpuGrp = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offCpuGroup);
+ uint32_t cMaxMembers = pGipCpuGrp->cMaxMembers;
+ AssertStmt(cMaxMembers <= RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers),
+ cMaxMembers = RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers));
+ g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = cMaxMembers;
+ g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers);
+
+ for (idxMember = 0; idxMember < cMaxMembers; idxMember++)
+ {
+ int16_t idxSet = pGipCpuGrp->aiCpuSetIdxs[idxMember];
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxSet;
+ if ((unsigned)idxSet < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx))
+# ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ g_aidRtMpWinByCpuSetIdx[idxSet] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember);
+# else
+ g_aidRtMpWinByCpuSetIdx[idxSet] = idxSet;
+# endif
+ }
+ }
+ else
+ idxMember = 0;
+ for (; idxMember < RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers); idxMember++)
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = -1;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Refreshes globals from GIP after one or more CPUs were added.
+ *
+ * There are potential races here. We might race other threads and we may race
+ * more CPUs being added.
+ */
+static void rtMpWinRefreshGip(void)
+{
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
+ if ( pGip
+ && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC)
+ {
+ /*
+ * Since CPUs cannot be removed, we only have to update the IDs and
+ * indexes of CPUs that we think are inactive and the group member counts.
+ */
+ for (;;)
+ {
+ unsigned const cbGip = pGip->cPages * PAGE_SIZE;
+ uint32_t const cGipActiveCpus = pGip->cOnlineCpus;
+ uint32_t const cMyActiveCpus = ASMAtomicReadU32(&g_cRtMpWinActiveCpus);
+ ASMCompilerBarrier();
+
+ for (uint32_t idxGroup = 0; idxGroup < g_cRtMpWinMaxCpuGroups; idxGroup++)
+ {
+ uint32_t offCpuGroup = pGip->aoffCpuGroup[idxGroup];
+ if (offCpuGroup < cbGip)
+ {
+ PSUPGIPCPUGROUP pGipCpuGrp = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offCpuGroup);
+ uint32_t cMaxMembers = pGipCpuGrp->cMaxMembers;
+ AssertStmt(cMaxMembers <= RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers),
+ cMaxMembers = RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers));
+ for (uint32_t idxMember = g_aRtMpWinCpuGroups[idxGroup].cActiveCpus; idxMember < cMaxMembers; idxMember++)
+ {
+ int16_t idxSet = pGipCpuGrp->aiCpuSetIdxs[idxMember];
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxSet;
+ if ((unsigned)idxSet < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx))
+# ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ g_aidRtMpWinByCpuSetIdx[idxSet] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember);
+# else
+ g_aidRtMpWinByCpuSetIdx[idxSet] = idxSet;
+# endif
+ }
+ g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers);
+ g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers);
+ }
+ else
+ Assert(g_aRtMpWinCpuGroups[idxGroup].cActiveCpus == 0);
+ }
+
+ ASMCompilerBarrier();
+ if (cGipActiveCpus == pGip->cOnlineCpus)
+ if (ASMAtomicCmpXchgU32(&g_cRtMpWinActiveCpus, cGipActiveCpus, cMyActiveCpus))
+ break;
+ }
+ }
+}
+
+#endif /* IPRT_WITH_GIP_MP_INFO */
+
+
+/*
+ * Conversion between CPU ID and set index.
+ */
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ if (idCpu != NIL_RTCPUID)
+ return RTMpSetIndexFromCpuGroupMember(rtMpCpuIdGetGroup(idCpu), rtMpCpuIdGetGroupMember(idCpu));
+ return -1;
+
+#else
+ /* 1:1 mapping, just do range checking. */
+ return idCpu < g_cRtMpWinMaxCpus ? idCpu : -1;
+#endif
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ if ((unsigned)iCpu < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx))
+ {
+ RTCPUID idCpu = g_aidRtMpWinByCpuSetIdx[iCpu];
+
+#if defined(IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER) && defined(RT_STRICT)
+ /* Check the correctness of the mapping table. */
+ RTCPUID idCpuGip = NIL_RTCPUID;
+ if ( pGip
+ && (unsigned)iCpu < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx))
+ {
+ unsigned idxSupCpu = pGip->aiCpuFromCpuSetIdx[idxGuess];
+ if (idxSupCpu < pGip->cCpus)
+ if (pGip->aCPUs[idxSupCpu].enmState != SUPGIPCPUSTATE_INVALID)
+ idCpuGip = pGip->aCPUs[idxSupCpu].idCpu;
+ }
+ AssertMsg(idCpu == idCpuGip, ("table:%#x gip:%#x\n", idCpu, idCpuGip));
+#endif
+
+ return idCpu;
+ }
+ return NIL_RTCPUID;
+}
+
+
+RTDECL(int) RTMpSetIndexFromCpuGroupMember(uint32_t idxGroup, uint32_t idxMember)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ if (idxGroup < g_cRtMpWinMaxCpuGroups)
+ if (idxMember < g_aRtMpWinCpuGroups[idxGroup].cMaxCpus)
+ return g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember];
+ return -1;
+}
+
+
+RTDECL(uint32_t) RTMpGetCpuGroupCounts(uint32_t idxGroup, uint32_t *pcActive)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ if (idxGroup < g_cRtMpWinMaxCpuGroups)
+ {
+ if (pcActive)
+ *pcActive = g_aRtMpWinCpuGroups[idxGroup].cActiveCpus;
+ return g_aRtMpWinCpuGroups[idxGroup].cMaxCpus;
+ }
+ if (pcActive)
+ *pcActive = 0;
+ return 0;
+}
+
+
+RTDECL(uint32_t) RTMpGetMaxCpuGroupCount(void)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ return g_cRtMpWinMaxCpuGroups;
+}
+
+
+
+/*
+ * Get current CPU.
+ */
+
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ PROCESSOR_NUMBER ProcNum;
+ ProcNum.Group = 0;
+ ProcNum.Number = 0xff;
+ if (g_pfnGetCurrentProcessorNumberEx)
+ g_pfnGetCurrentProcessorNumberEx(&ProcNum);
+ else if (g_pfnGetCurrentProcessorNumber)
+ {
+ DWORD iCpu = g_pfnGetCurrentProcessorNumber();
+ Assert(iCpu < g_cRtMpWinMaxCpus);
+ ProcNum.Number = iCpu;
+ }
+ else
+ {
+#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+ ProcNum.Number = ASMGetApicId();
+#else
+# error "Not ported to this architecture."
+ return NIL_RTCPUID;
+#endif
+ }
+
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ return RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number);
+#else
+ return RTMpSetIndexFromCpuGroupMember(ProcNum.Group, ProcNum.Number);
+#endif
+}
+
+
+/*
+ * Possible CPUs and cores.
+ */
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ return RTMPCPUID_FROM_GROUP_AND_NUMBER(g_cRtMpWinMaxCpuGroups - 1,
+ g_aRtMpWinCpuGroups[g_cRtMpWinMaxCpuGroups - 1].cMaxCpus - 1);
+#else
+ return g_cRtMpWinMaxCpus - 1;
+#endif
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ /* Any CPU between 0 and g_cRtMpWinMaxCpus are possible. */
+ return idCpu < g_cRtMpWinMaxCpus;
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCPUID iCpu = RTMpGetCount();
+ RTCpuSetEmpty(pSet);
+ while (iCpu-- > 0)
+ RTCpuSetAddByIndex(pSet, iCpu);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ return g_cRtMpWinMaxCpus;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCoreCount(void)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ return g_cRtMpWinMaxCpuCores;
+}
+
+
+/*
+ * Online CPUs and cores.
+ */
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+
+#ifdef IPRT_WITH_GIP_MP_INFO
+ RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP();
+ if (pGip)
+ {
+ *pSet = pGip->OnlineCpuSet;
+ return pSet;
+ }
+#endif
+
+ if (g_pfnGetLogicalProcessorInformationEx)
+ {
+ /*
+ * Get the group relation info.
+ *
+ * In addition to the ASSUMPTIONS that are documented in rtMpWinInitOnce,
+ * we ASSUME that PROCESSOR_GROUP_INFO::MaximumProcessorCount gives the
+ * active processor mask width.
+ */
+ /** @todo this is not correct for WOW64 */
+ DWORD cbInfo = g_cbRtMpWinGrpRelBuf;
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)alloca(cbInfo);
+ AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationGroup, pInfo, &cbInfo) != FALSE,
+ ("last error = %u, cbInfo = %u (in %u)\n", GetLastError(), cbInfo, g_cbRtMpWinGrpRelBuf));
+ AssertFatalMsg(pInfo->Relationship == RelationGroup,
+ ("Relationship = %u, expected %u!\n", pInfo->Relationship, RelationGroup));
+ AssertFatalMsg(pInfo->Group.MaximumGroupCount == g_cRtMpWinMaxCpuGroups,
+ ("MaximumGroupCount is %u, expected %u!\n", pInfo->Group.MaximumGroupCount, g_cRtMpWinMaxCpuGroups));
+
+ RTCpuSetEmpty(pSet);
+ for (uint32_t idxGroup = 0; idxGroup < pInfo->Group.MaximumGroupCount; idxGroup++)
+ {
+ Assert(pInfo->Group.GroupInfo[idxGroup].MaximumProcessorCount == g_aRtMpWinCpuGroups[idxGroup].cMaxCpus);
+ Assert(pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount <= g_aRtMpWinCpuGroups[idxGroup].cMaxCpus);
+
+ KAFFINITY fActive = pInfo->Group.GroupInfo[idxGroup].ActiveProcessorMask;
+ if (fActive != 0)
+ {
+#ifdef RT_STRICT
+ uint32_t cMembersLeft = pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount;
+#endif
+ int const cMembers = g_aRtMpWinCpuGroups[idxGroup].cMaxCpus;
+ for (int idxMember = 0; idxMember < cMembers; idxMember++)
+ {
+ if (fActive & 1)
+ {
+#ifdef RT_STRICT
+ cMembersLeft--;
+#endif
+ RTCpuSetAddByIndex(pSet, g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember]);
+ fActive >>= 1;
+ if (!fActive)
+ break;
+ }
+ else
+ {
+ fActive >>= 1;
+ }
+ }
+ Assert(cMembersLeft == 0);
+ }
+ else
+ Assert(pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount == 0);
+ }
+
+ return pSet;
+ }
+
+ /*
+ * Legacy fallback code.
+ */
+ SYSTEM_INFO SysInfo;
+ GetSystemInfo(&SysInfo);
+ return RTCpuSetFromU64(pSet, SysInfo.dwActiveProcessorMask);
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ RTCPUSET Set;
+ return RTCpuSetIsMember(RTMpGetOnlineSet(&Set), idCpu);
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+#ifdef IPRT_WITH_GIP_MP_INFO
+ RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP();
+ if (pGip)
+ return pGip->cOnlineCpus;
+#endif
+
+ RTCPUSET Set;
+ RTMpGetOnlineSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
+{
+ /** @todo this isn't entirely correct, but whatever. */
+ return RTMpGetCoreCount();
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp
new file mode 100644
index 00000000..5f74b4f5
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp
@@ -0,0 +1,57 @@
+/* $Id: nocrt-RTLogWriteStdErr-win.cpp $ */
+/** @file
+ * IPRT - Log To StdErr, Windows no-CRT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/log.h>
+#include "internal/iprt.h"
+
+#include <iprt/win/windows.h>
+
+
+RTDECL(void) RTLogWriteStdErr(const char *pch, size_t cb)
+{
+ HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);
+ if (hStdErr != NULL && hStdErr != INVALID_HANDLE_VALUE)
+ {
+ DWORD cbIgn; /* NT3.1 requires the return size parameter. */
+ WriteFile(hStdErr, pch, (DWORD)cb, &cbIgn, NULL); /** @todo do we need to translate \\n to \\r\\n? */
+ }
+}
+RT_EXPORT_SYMBOL(RTLogWriteStdErr);
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp
new file mode 100644
index 00000000..56e29134
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp
@@ -0,0 +1,58 @@
+/* $Id: nocrt-RTLogWriteStdOut-win.cpp $ */
+/** @file
+ * IPRT - Log To StdOut, Windows no-CRT.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/log.h>
+#include "internal/iprt.h"
+
+#include <iprt/win/windows.h>
+
+
+RTDECL(void) RTLogWriteStdOut(const char *pch, size_t cb)
+{
+ /** @todo should flush the stdout stream first... */
+ HANDLE hStdErr = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (hStdErr != NULL && hStdErr != INVALID_HANDLE_VALUE)
+ {
+ DWORD cbIgn; /* NT3.1 requires the return size parameter. */
+ WriteFile(hStdErr, pch, (DWORD)cb, &cbIgn, NULL); /** @todo do we need to translate \\n to \\r\\n? */
+ }
+}
+RT_EXPORT_SYMBOL(RTLogWriteStdOut);
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm b/src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm
new file mode 100644
index 00000000..1943ad69
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm
@@ -0,0 +1,44 @@
+; $Id: nocrt-WinMainCRTStartup-win.asm $
+;; @file
+; IPRT - Alias WinMainCRTStartup to CustomMainEntrypoint in nocrt-startup-exe-win.cpp.
+;
+
+;
+; Copyright (C) 2022-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+
+extern NAME(CustomMainEntrypoint)
+BEGINPROC WinMainCRTStartup
+ jmp NAME(CustomMainEntrypoint)
+ENDPROC WinMainCRTStartup
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp b/src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp
new file mode 100644
index 00000000..128b58df
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp
@@ -0,0 +1,115 @@
+/* $Id: nocrt-alloc-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - Basic allocators, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/nt/nt-and-windows.h>
+
+
+#undef RTMemTmpFree
+RTDECL(void) RTMemTmpFree(void *pv)
+{
+ HeapFree(GetProcessHeap(), 0, pv);
+}
+
+
+#undef RTMemFree
+RTDECL(void) RTMemFree(void *pv)
+{
+ HeapFree(GetProcessHeap(), 0, pv);
+}
+
+
+#undef RTMemTmpAllocTag
+RTDECL(void *) RTMemTmpAllocTag(size_t cb, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+ return HeapAlloc(GetProcessHeap(), 0, cb);
+}
+
+
+#undef RTMemTmpAllocZTag
+RTDECL(void *) RTMemTmpAllocZTag(size_t cb, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+ return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb);
+}
+
+
+#undef RTMemAllocTag
+RTDECL(void *) RTMemAllocTag(size_t cb, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+ return HeapAlloc(GetProcessHeap(), 0, cb);
+}
+
+
+#undef RTMemAllocZTag
+RTDECL(void *) RTMemAllocZTag(size_t cb, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+ return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb);
+}
+
+
+#undef RTMemAllocVarTag
+RTDECL(void *) RTMemAllocVarTag(size_t cbUnaligned, const char *pszTag)
+{
+ return RTMemAllocTag(cbUnaligned, pszTag);
+}
+
+
+#undef RTMemAllocZVarTag
+RTDECL(void *) RTMemAllocZVarTag(size_t cbUnaligned, const char *pszTag)
+{
+ return RTMemAllocZTag(cbUnaligned, pszTag);
+}
+
+
+#undef RTMemReallocTag
+RTDECL(void *) RTMemReallocTag(void *pvOld, size_t cbNew, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+ if (pvOld)
+ return HeapReAlloc(GetProcessHeap(), 0, pvOld, cbNew);
+ return HeapAlloc(GetProcessHeap(), 0, cbNew);
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-atexit-win.asm b/src/VBox/Runtime/r3/win/nocrt-atexit-win.asm
new file mode 100644
index 00000000..881463ec
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-atexit-win.asm
@@ -0,0 +1,44 @@
+; $Id: nocrt-atexit-win.asm $
+;; @file
+; IPRT - Alias atexit to rtnocrt_atexit in nocrt-startup-exe-win.cpp.
+;
+
+;
+; Copyright (C) 2022-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+
+extern NAME(nocrt_atexit)
+BEGINPROC atexit
+ jmp NAME(nocrt_atexit)
+ENDPROC atexit
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp b/src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp
new file mode 100644
index 00000000..c9bfa1b4
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp
@@ -0,0 +1,150 @@
+/* $Id: nocrt-fatal-write-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - Fatal Message Writing Primitives, Windows.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+
+#include <iprt/win/windows.h>
+#include <iprt/string.h>
+
+
+/** @todo invent some kind of weak linking with the debug and release
+ * loggers, e.g. theset some innocent function we can call to do
+ * logging and we'll call it if it isn't NULL. */
+
+void rtNoCrtFatalWrite(const char *pchMsg, size_t cchMsg)
+{
+ DWORD cbIgn;
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), pchMsg, (DWORD)cchMsg, &cbIgn, NULL);
+}
+
+
+void rtNoCrtFatalWriteBegin(const char *pchMsg, size_t cchMsg)
+{
+ rtNoCrtFatalWrite(pchMsg, cchMsg);
+}
+
+
+void rtNoCrtFatalWriteEnd(const char *pchMsg, size_t cchMsg)
+{
+ rtNoCrtFatalWrite(pchMsg, cchMsg);
+}
+
+
+void rtNoCrtFatalWritePtr(void const *pv)
+{
+ char szValue[128];
+#if ARCH_BITS == 64
+ ssize_t cchValue = RTStrFormatU64(szValue, sizeof(szValue), (uintptr_t)pv, 16, 16, 16, RTSTR_F_WIDTH | RTSTR_F_64BIT);
+#elif ARCH_BITS == 32
+ ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), (uintptr_t)pv, 16, 8, 8, RTSTR_F_WIDTH | RTSTR_F_32BIT);
+#else
+# error ARCH_BITS
+#endif
+ rtNoCrtFatalWrite(szValue, (size_t)cchValue);
+}
+
+
+void rtNoCrtFatalWriteX64(uint64_t uValue)
+{
+ char szValue[128];
+ ssize_t cchValue = RTStrFormatU64(szValue, sizeof(szValue), uValue, 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_64BIT);
+ rtNoCrtFatalWrite(szValue, (size_t)cchValue);
+}
+
+
+void rtNoCrtFatalWriteX32(uint32_t uValue)
+{
+ char szValue[128];
+ ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), uValue, 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_32BIT);
+ rtNoCrtFatalWrite(szValue, (size_t)cchValue);
+}
+
+
+void rtNoCrtFatalWriteRc(int rc)
+{
+ char szValue[128];
+ ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), rc, 10, 0, 0, RTSTR_F_32BIT | RTSTR_F_VALSIGNED);
+ rtNoCrtFatalWrite(szValue, (size_t)cchValue);
+}
+
+void rtNoCrtFatalWriteWinRc(uint32_t rc)
+{
+ char szValue[168];
+ ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), rc, 10, 0, 0, RTSTR_F_32BIT);
+ szValue[cchValue++] = ' ';
+ szValue[cchValue++] = '(';
+ cchValue += RTStrFormatU32(&szValue[cchValue], sizeof(szValue) - cchValue, rc, 16, 0, 0, RTSTR_F_32BIT | RTSTR_F_SPECIAL);
+ szValue[cchValue++] = ')';
+ rtNoCrtFatalWrite(szValue, (size_t)cchValue);
+}
+
+
+void rtNoCrtFatalWriteStr(const char *pszString)
+{
+ if (RT_VALID_PTR(pszString))
+ {
+ size_t cch = 0;
+ while (pszString[cch] != '\0')
+ cch++;
+ rtNoCrtFatalWrite(pszString, cch);
+ }
+ else
+ {
+ rtNoCrtFatalWrite(RT_STR_TUPLE("<pszString="));
+ rtNoCrtFatalWritePtr(pszString);
+ rtNoCrtFatalWrite(RT_STR_TUPLE(">"));
+ }
+}
+
+
+void rtNoCrtFatalMsg(const char *pchMsg, size_t cchMsg)
+{
+ rtNoCrtFatalWriteBegin(pchMsg, cchMsg);
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE(""));
+}
+
+
+void rtNoCrtFatalMsgWithRc(const char *pchMsg, size_t cchMsg, int rc)
+{
+ rtNoCrtFatalWriteBegin(pchMsg, cchMsg);
+ rtNoCrtFatalWriteRc(rc);
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n"));
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm b/src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm
new file mode 100644
index 00000000..54651dd1
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm
@@ -0,0 +1,44 @@
+; $Id: nocrt-mainCRTStartup-win.asm $
+;; @file
+; IPRT - Alias mainCRTStartup to CustomMainEntrypoint in nocrt-startup-exe-win.cpp.
+;
+
+;
+; Copyright (C) 2022-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+
+extern NAME(CustomMainEntrypoint)
+BEGINPROC mainCRTStartup
+ jmp NAME(CustomMainEntrypoint)
+ENDPROC mainCRTStartup
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp b/src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp
new file mode 100644
index 00000000..598339db
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp
@@ -0,0 +1,211 @@
+/* $Id: nocrt-startup-common-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - Common Windows startup code.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+#include "internal/process.h"
+
+#include <iprt/nt/nt-and-windows.h>
+#ifndef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+# include <iprt/assert.h>
+#endif
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "internal/compiler-vcc.h"
+#include "internal/process.h"
+
+
+#ifdef RT_ARCH_X86
+/**
+ * NT 3.1 does not know about the IMAGE_SECTION_HEADER::Misc.VirtualSize field
+ * and will therefore not handle merging initialized and uninitialized data into
+ * the same section.
+ *
+ * We work around this by manually zeroing the uninitialized data before any
+ * other code has been executed.
+ */
+void rtVccWinInitBssOnNt3(void *pvImageBase)
+{
+ /* We are called really early on, so we must figure out the NT version
+ on our own. It doesn't have to be all that accurate, though, as only
+ NT 3.10 is affected (3.50 isn't). */
+ DWORD const dwRawVer = GetVersion();
+ DWORD const uMajorVer = RT_BYTE1(dwRawVer);
+ DWORD const uMinorVer = RT_BYTE2(dwRawVer);
+ if (uMajorVer != 3 || uMinorVer >= 50)
+ return;
+
+ /*
+ * Locate the NT headers.
+ */
+ PIMAGE_NT_HEADERS pNtHdrs;
+ PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)pvImageBase;
+ if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
+ pNtHdrs = (PIMAGE_NT_HEADERS)((uintptr_t)pvImageBase + pDosHdr->e_lfanew);
+ else
+ pNtHdrs = (PIMAGE_NT_HEADERS)pvImageBase;
+ if (pNtHdrs->Signature == IMAGE_NT_SIGNATURE)
+ {
+ /*
+ * Locate the section table and walk thru it, memsetting anything that
+ * wasn't loaded from the file.
+ */
+ PIMAGE_SECTION_HEADER paSHdrs = (PIMAGE_SECTION_HEADER)( (uintptr_t)&pNtHdrs->OptionalHeader
+ + pNtHdrs->FileHeader.SizeOfOptionalHeader);
+ uint32_t const cSections = pNtHdrs->FileHeader.NumberOfSections;
+ for (uint32_t i = 0; i < cSections; i++)
+ {
+ if (paSHdrs[i].Misc.VirtualSize > paSHdrs[i].SizeOfRawData)
+ {
+ /* ASSUMES VirtualAddress is still an RVAs */
+ uint8_t *pbToZero = (uint8_t *)pvImageBase + paSHdrs[i].VirtualAddress + paSHdrs[i].SizeOfRawData;
+ uint32_t const cbToZero = paSHdrs[i].Misc.VirtualSize - paSHdrs[i].SizeOfRawData;
+
+# if 0 /* very very crude debugging */
+ const char *pszHex = "0123456789abcdef";
+ char szMsg[128];
+ char *psz = szMsg;
+ *psz++ = 'd'; *psz++ = 'b'; *psz++ = 'g'; *psz++ = ':'; *psz++ = ' ';
+ for (uint32_t q = 0, u = i; q < 8; q++, u >>= 4)
+ psz[7 - q] = pszHex[u & 0xf];
+ psz += 8;
+ *psz++ = ':';
+ *psz++ = ' ';
+ for (uint32_t q = 0, u = (uint32_t)pbToZero; q < 8; q++, u >>= 4)
+ psz[7 - q] = pszHex[u & 0xf];
+ psz += 8;
+ *psz++ = ' ';
+ *psz++ = 'L';
+ *psz++ = 'B';
+ *psz++ = ' ';
+ for (uint32_t q = 0, u = cbToZero; q < 8; q++, u >>= 4)
+ psz[7 - q] = pszHex[u & 0xf];
+ psz += 8;
+ *psz++ = ' ';
+ for (uint32_t q = 0; q < 8; q++)
+ *psz++ = paSHdrs[i].Name[q] ? paSHdrs[i].Name[q] : ' ';
+ *psz++ = ' '; *psz++ = '/'; *psz++ = ' '; *psz++ = 'v'; *psz++ = 'e'; *psz++ = 'r'; *psz++ = ' ';
+ *psz++ = pszHex[(uMajorVer >> 4) & 0xf];
+ *psz++ = pszHex[uMajorVer & 0xf];
+ *psz++ = '.';
+ *psz++ = pszHex[(uMinorVer >> 4) & 0xf];
+ *psz++ = pszHex[uMinorVer & 0xf];
+ *psz++ = '\r'; *psz++ = '\n';
+ *psz = '\0';
+ DWORD cbIgn;
+ HANDLE hOut = RTNtCurrentPeb()->ProcessParameters->StandardOutput;
+ if (hOut == NULL || hOut == INVALID_HANDLE_VALUE)
+ hOut = RTNtCurrentPeb()->ProcessParameters->StandardError;
+ if (hOut == NULL || hOut == INVALID_HANDLE_VALUE)
+ hOut = RTNtCurrentPeb()->ProcessParameters->ConsoleHandle;
+ if (hOut == NULL || hOut == INVALID_HANDLE_VALUE)
+ hOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ WriteFile(hOut, szMsg, psz - szMsg, &cbIgn, NULL);
+# endif
+
+ if (paSHdrs[i].Characteristics & IMAGE_SCN_MEM_WRITE)
+ memset(pbToZero, 0, cbToZero);
+ else
+ {
+ /* The section is not writable, so temporarily make it writable. */
+ PVOID pvAligned = pbToZero - ((uintptr_t)pbToZero & PAGE_OFFSET_MASK);
+ ULONG cbAligned = RT_ALIGN_32(cbToZero + ((uintptr_t)pbToZero & PAGE_OFFSET_MASK), PAGE_SIZE);
+ ULONG fNewProt = paSHdrs[i].Characteristics & IMAGE_SCN_MEM_EXECUTE
+ ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
+ ULONG fOldProt = fNewProt;
+ NTSTATUS rcNt = NtProtectVirtualMemory(NtCurrentProcess(), &pvAligned, &cbAligned, fNewProt, &fOldProt);
+ if (NT_SUCCESS(rcNt))
+ {
+ memset(pbToZero, 0, cbToZero);
+
+ rcNt = NtProtectVirtualMemory(NtCurrentProcess(), &pvAligned, &cbAligned, fOldProt, &fNewProt);
+ }
+ else
+ RT_BREAKPOINT();
+ }
+ }
+ }
+ }
+ else
+ RT_BREAKPOINT();
+}
+#endif
+
+
+void rtVccWinInitProcExecPath(void)
+{
+ WCHAR wszPath[RTPATH_MAX];
+ UINT cwcPath = GetModuleFileNameW(NULL, wszPath, RT_ELEMENTS(wszPath));
+ if (cwcPath)
+ {
+ char *pszDst = g_szrtProcExePath;
+ int rc = RTUtf16ToUtf8Ex(wszPath, cwcPath, &pszDst, sizeof(g_szrtProcExePath), &g_cchrtProcExePath);
+ if (RT_SUCCESS(rc))
+ {
+ g_cchrtProcExeDir = g_offrtProcName = RTPathFilename(pszDst) - g_szrtProcExePath;
+ while ( g_cchrtProcExeDir >= 2
+ && RTPATH_IS_SLASH(g_szrtProcExePath[g_cchrtProcExeDir - 1])
+ && g_szrtProcExePath[g_cchrtProcExeDir - 2] != ':')
+ g_cchrtProcExeDir--;
+ }
+ else
+ {
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("initProcExecPath: RTUtf16ToUtf8Ex failed: %Rrc\n", rc);
+#else
+ rtNoCrtFatalMsgWithRc(RT_STR_TUPLE("initProcExecPath: RTUtf16ToUtf8Ex failed: "), rc);
+#endif
+ }
+ }
+ else
+ {
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("initProcExecPath: GetModuleFileNameW failed: %Rhrc\n", GetLastError());
+#else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("initProcExecPath: GetModuleFileNameW failed: "));
+ rtNoCrtFatalWriteWinRc(GetLastError());
+ rtNoCrtFatalWrite(RT_STR_TUPLE("\r\n"));
+#endif
+ }
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp b/src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp
new file mode 100644
index 00000000..77bf0951
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp
@@ -0,0 +1,162 @@
+/* $Id: nocrt-startup-dll-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - Windows EXE startup code.
+ *
+ * @note Does not run static constructors and destructors!
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include "internal/process.h"
+
+#include <iprt/nt/nt-and-windows.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#ifdef IPRT_NO_CRT
+# include <iprt/asm.h>
+# include <iprt/nocrt/stdlib.h>
+#endif
+
+#include "internal/compiler-vcc.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static volatile int32_t g_cAttached = 0;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved);
+
+
+DECL_NO_INLINE(static, BOOL) rtVccDllMainForward(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved)
+{
+ return DllMain(hInstance, dwReason, pvReserved);
+}
+
+
+
+DECL_NO_INLINE(static, BOOL) rtVccDllMainProcessAttach(HINSTANCE hInstance, LPVOID pvReserved)
+{
+ /*
+ * Initialize the CRT the first time thru.
+ */
+ if (g_cAttached == 0)
+ {
+ rtVccWinInitProcExecPath();
+
+ int rc = rtVccInitializersRunInit();
+ if (RT_FAILURE(rc))
+ return FALSE;
+ }
+ g_cAttached++;
+
+ /*
+ * Call the DllMain function.
+ */
+ BOOL fRet = rtVccDllMainForward(hInstance, DLL_PROCESS_ATTACH, pvReserved);
+
+ /*
+ * On failure, we call the DllMain function again, decrement the init counter
+ * and probably run termination callbacks.
+ */
+ if (!fRet)
+ {
+ rtVccDllMainForward(hInstance, DLL_PROCESS_DETACH, pvReserved);
+ if (--g_cAttached == 0)
+ {
+ rtVccTermRunAtExit();
+ rtVccInitializersRunTerm();
+ }
+ }
+ return fRet;
+}
+
+
+DECL_NO_INLINE(static, BOOL) rtVccDllMainProcessDetach(HINSTANCE hInstance, LPVOID pvReserved)
+{
+ /*
+ * Make sure there isn't an imbalance before calling DllMain and shutting
+ * down our own internals.
+ */
+ if (g_cAttached <= 0)
+ return FALSE;
+
+ /*
+ * Call DllMain.
+ */
+ BOOL fRet = rtVccDllMainForward(hInstance, DLL_PROCESS_DETACH, pvReserved);
+
+ /*
+ * Work g_cAttached and probably do uninitialization. We'll do this regardless
+ * of what DllMain returned.
+ */
+ if (--g_cAttached == 0)
+ {
+ rtVccTermRunAtExit();
+ rtVccInitializersRunTerm();
+ }
+ return fRet;
+}
+
+
+extern "C" BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved)
+{
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+#ifdef RT_ARCH_X86
+ rtVccWinInitBssOnNt3((PVOID)hInstance);
+#endif
+ rtVccInitSecurityCookie(); /* This function must be minimal because of this! */
+ return rtVccDllMainProcessAttach(hInstance, pvReserved);
+
+ case DLL_PROCESS_DETACH:
+ return rtVccDllMainProcessDetach(hInstance, pvReserved);
+
+ default:
+ return rtVccDllMainForward(hInstance, dwReason, pvReserved);
+ }
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp b/src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp
new file mode 100644
index 00000000..543ea05c
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp
@@ -0,0 +1,174 @@
+/* $Id: nocrt-startup-exe-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - Windows EXE startup code.
+ *
+ * @note Does not run static constructors and destructors!
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+#include "internal/process.h"
+
+#include <iprt/nt/nt-and-windows.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "internal/compiler-vcc.h"
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+extern int main(int argc, char **argv, char **envp); /* in program */
+#ifndef IPRT_NO_CRT
+extern DECLHIDDEN(void) InitStdHandles(PRTL_USER_PROCESS_PARAMETERS pParams); /* nocrt-streams-win.cpp */ /** @todo put in header */
+#endif
+
+
+static int rtTerminateProcess(int32_t rcExit, bool fDoAtExit)
+{
+#ifdef IPRT_NO_CRT
+ /*
+ * Run atexit callback in reverse order.
+ */
+ if (fDoAtExit)
+ {
+ rtVccTermRunAtExit();
+ rtVccInitializersRunTerm();
+ }
+#else
+ RT_NOREF(fDoAtExit);
+#endif
+
+ /*
+ * Terminate.
+ */
+ for (;;)
+ NtTerminateProcess(NtCurrentProcess(), rcExit);
+}
+
+
+DECLASM(void) CustomMainEntrypoint(void)
+{
+ /* Looks like might have gotten the PPEB as parameter here before NT4,
+ however, there the EXE entry function clearly takes no parameters.
+ So, we have to retrieve the PEB our selves here. */
+ PPEB_COMMON const pPeb = RTNtCurrentPeb();
+
+ /*
+ * Initialize stuff.
+ */
+#ifdef IPRT_NO_CRT
+# ifdef RT_ARCH_X86
+ rtVccWinInitBssOnNt3(pPeb->ImageBaseAddress);
+# endif
+ rtVccInitSecurityCookie();
+#else
+ InitStdHandles(pPeb->ProcessParameters);
+#endif
+ rtVccWinInitProcExecPath();
+
+ RTEXITCODE rcExit;
+#ifdef IPRT_NO_CRT
+ AssertCompile(sizeof(rcExit) == sizeof(int));
+ rcExit = (RTEXITCODE)rtVccInitializersRunInit();
+ if (rcExit == RTEXITCODE_SUCCESS)
+#endif
+ {
+ /*
+ * Get and convert the command line to argc/argv format.
+ */
+ rcExit = RTEXITCODE_INIT;
+ UNICODE_STRING const *pCmdLine = pPeb->ProcessParameters ? &pPeb->ProcessParameters->CommandLine : NULL;
+ if (pCmdLine)
+ {
+ char *pszCmdLine = NULL;
+ int rc = RTUtf16ToUtf8Ex(pCmdLine->Buffer, pCmdLine->Length / sizeof(WCHAR), &pszCmdLine, 0, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ char **papszArgv;
+ int cArgs = 0;
+ rc = RTGetOptArgvFromString(&papszArgv, &cArgs, pszCmdLine,
+ RTGETOPTARGV_CNV_MODIFY_INPUT | RTGETOPTARGV_CNV_QUOTE_MS_CRT, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Call the main function.
+ */
+ AssertCompile(sizeof(rcExit) == sizeof(int));
+ rcExit = (RTEXITCODE)main(cArgs, papszArgv, NULL /*envp*/);
+ }
+ else
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("Error parsing command line: %Rrc\n", rc);
+#else
+ rtNoCrtFatalMsgWithRc(RT_STR_TUPLE("Error parsing command line: "), rc);
+#endif
+ }
+ else
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("Failed to convert command line to UTF-8: %Rrc\n", rc);
+#else
+ rtNoCrtFatalMsgWithRc(RT_STR_TUPLE("Failed to convert command line to UTF-8: "), rc);
+#endif
+ }
+ else
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("No command line\n");
+#else
+ rtNoCrtFatalMsg(RT_STR_TUPLE("No command line\r\n"));
+#endif
+ rtTerminateProcess(rcExit, true /*fDoAtExit*/);
+ }
+#ifdef IPRT_NO_CRT
+ else
+ {
+# ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("A C static initializor failed (%d)\n", rcExit);
+# else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("A C static initializor failed ("));
+ rtNoCrtFatalWriteWinRc(rcExit);
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n"));
+# endif
+ rtTerminateProcess(rcExit, false /*fDoAtExit*/);
+ }
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-streams-win.cpp b/src/VBox/Runtime/r3/win/nocrt-streams-win.cpp
new file mode 100644
index 00000000..269c9bd2
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-streams-win.cpp
@@ -0,0 +1,223 @@
+/* $Id: nocrt-streams-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - minimal stream implementation
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/stream.h>
+
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/string.h>
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct PRINTFBUF
+{
+ HANDLE hNative;
+ size_t offBuf;
+ char szBuf[128];
+} PRINTFBUF;
+
+struct RTSTREAM
+{
+ int iStream;
+ HANDLE hNative;
+ RTFILE hFile;
+};
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define MAKE_SURE_WE_HAVE_HFILE_RETURN(a_pStream) do { \
+ if ((a_pStream)->hFile != NIL_RTFILE) \
+ break; \
+ int rc = RTFileFromNative(&(a_pStream)->hFile, (uintptr_t)(a_pStream)->hNative); \
+ AssertRCReturn(rc, rc); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+RTSTREAM g_aStdStreams[3] =
+{
+ { 0, NULL, NIL_RTFILE },
+ { 1, NULL, NIL_RTFILE },
+ { 2, NULL, NIL_RTFILE },
+};
+
+RTSTREAM *g_pStdIn = &g_aStdStreams[0];
+RTSTREAM *g_pStdOut = &g_aStdStreams[1];
+RTSTREAM *g_pStdErr = &g_aStdStreams[2];
+
+
+
+DECLHIDDEN(void) InitStdHandles(PRTL_USER_PROCESS_PARAMETERS pParams)
+{
+ if (pParams)
+ {
+ g_pStdIn->hNative = pParams->StandardInput;
+ g_pStdOut->hNative = pParams->StandardOutput;
+ g_pStdErr->hNative = pParams->StandardError;
+ }
+}
+
+
+static void FlushPrintfBuffer(PRINTFBUF *pBuf)
+{
+ if (pBuf->offBuf)
+ {
+ DWORD cbWritten = 0;
+ WriteFile(pBuf->hNative, pBuf->szBuf, (DWORD)pBuf->offBuf, &cbWritten, NULL);
+ pBuf->offBuf = 0;
+ pBuf->szBuf[0] = '\0';
+ }
+}
+
+
+/** @callback_method_impl{FNRTSTROUTPUT} */
+static DECLCALLBACK(size_t) MyPrintfOutputter(void *pvArg, const char *pachChars, size_t cbChars)
+{
+ PRINTFBUF *pBuf = (PRINTFBUF *)pvArg;
+ if (cbChars != 0)
+ {
+ size_t offSrc = 0;
+ while (offSrc < cbChars)
+ {
+ size_t cbLeft = sizeof(pBuf->szBuf) - pBuf->offBuf - 1;
+ if (cbLeft > 0)
+ {
+ size_t cbToCopy = RT_MIN(cbChars - offSrc, cbLeft);
+ memcpy(&pBuf->szBuf[pBuf->offBuf], &pachChars[offSrc], cbToCopy);
+ pBuf->offBuf += cbToCopy;
+ pBuf->szBuf[pBuf->offBuf] = '\0';
+ if (cbLeft > cbToCopy)
+ break;
+ offSrc += cbToCopy;
+ }
+ FlushPrintfBuffer(pBuf);
+ }
+ }
+ else /* Special zero byte write at the end of the formatting. */
+ FlushPrintfBuffer(pBuf);
+ return cbChars;
+}
+
+
+RTR3DECL(int) RTStrmPrintfV(PRTSTREAM pStream, const char *pszFormat, va_list args)
+{
+ PRINTFBUF Buf;
+ Buf.hNative = pStream->hNative;
+ Buf.offBuf = 0;
+ Buf.szBuf[0] = '\0';
+
+ return (int)RTStrFormatV(MyPrintfOutputter, &Buf, NULL, NULL, pszFormat, args);
+}
+
+
+RTR3DECL(int) RTStrmPrintf(PRTSTREAM pStream, const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ int rc = RTStrmPrintfV(pStream, pszFormat, args);
+ va_end(args);
+ return rc;
+}
+
+
+RTR3DECL(int) RTPrintfV(const char *pszFormat, va_list va)
+{
+ PRINTFBUF Buf;
+ Buf.hNative = g_pStdOut->hNative;
+ Buf.offBuf = 0;
+ Buf.szBuf[0] = '\0';
+
+ return (int)RTStrFormatV(MyPrintfOutputter, &Buf, NULL, NULL, pszFormat, va);
+}
+
+
+RTR3DECL(int) RTPrintf(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTPrintfV(pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+#ifndef IPRT_MINIMAL_STREAM
+
+# if 0
+RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ MAKE_SURE_WE_HAVE_HFILE_RETURN(pStream);
+ return RTFileRead(pStream->hFile, pvBuf, cbToRead, pcbRead);
+}
+# endif
+
+
+RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ MAKE_SURE_WE_HAVE_HFILE_RETURN(pStream);
+ return RTFileWrite(pStream->hFile, pvBuf, cbToWrite, pcbWritten);
+}
+
+
+RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream)
+{
+ MAKE_SURE_WE_HAVE_HFILE_RETURN(pStream);
+ return RTFileFlush(pStream->hFile);
+}
+
+
+RTR3DECL(int) RTStrmSetMode(PRTSTREAM pStream, int fBinary, int fCurrentCodeSet)
+{
+ AssertReturn(fBinary != (int)false, VERR_NOT_IMPLEMENTED);
+ AssertReturn(fCurrentCodeSet <= (int)false, VERR_NOT_IMPLEMENTED);
+ RT_NOREF(pStream);
+ return VINF_SUCCESS;
+}
+
+#endif
diff --git a/src/VBox/Runtime/r3/win/ntdll-mini-implib.def b/src/VBox/Runtime/r3/win/ntdll-mini-implib.def
new file mode 100644
index 00000000..c382847e
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/ntdll-mini-implib.def
@@ -0,0 +1,163 @@
+; $Id: ntdll-mini-implib.def $
+;; @file
+; IPRT - Minimal NTDLL import library defintion file.
+;
+
+;
+; Copyright (C) 2010-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+LIBRARY ntdll.dll
+EXPORTS
+ ; Exported name - The name x86 name sought by the linker.
+ ; - This needs to be defined as a symbol, we generate assembly.
+
+ CsrClientCallServer ;;= _CsrClientCallServer@16
+
+ NtAlertThread ;;= _NtAlertThread@4
+ NtAllocateVirtualMemory ;;= _NtAllocateVirtualMemory@24
+ NtCancelIoFile ;;= _NtCancelIoFile@8
+ NtClearEvent ;;= _NtClearEvent@4
+ NtClose ;;= _NtClose@4
+ NtCreateEvent ;;= _NtCreateEvent@20
+ NtCreateFile ;;= _NtCreateFile@44
+ NtCreateSection ;;= _NtCreateSection@28
+ NtCreateSymbolicLinkObject ;;= _NtCreateSymbolicLinkObject@16
+ NtDelayExecution ;;= _NtDelayExecution@8
+ NtDeviceIoControlFile ;;= _NtDeviceIoControlFile@40
+ NtDuplicateObject ;;= _NtDuplicateObject@28
+ NtEnumerateKey ;;= _NtEnumerateKey@24
+ NtFlushBuffersFile ;;= _NtFlushBuffersFile@8
+ NtFlushVirtualMemory ;;= _NtFlushVirtualMemory@16
+ NtFreeVirtualMemory ;;= _NtFreeVirtualMemory@16
+ NtGetContextThread ;;= _NtGetContextThread@8
+ NtMapViewOfSection ;;= _NtMapViewOfSection@40
+ NtOpenDirectoryObject ;;= _NtOpenDirectoryObject@12
+ NtOpenEvent ;;= _NtOpenEvent@12
+ NtOpenKey ;;= _NtOpenKey@12
+ NtOpenProcess ;;= _NtOpenProcess@16
+ NtOpenProcessToken ;;= _NtOpenProcessToken@12
+ NtOpenSymbolicLinkObject ;;= _NtOpenSymbolicLinkObject@12
+ NtOpenThread ;;= _NtOpenThread@16
+ NtOpenThreadToken ;;= _NtOpenThreadToken@16
+ NtProtectVirtualMemory ;;= _NtProtectVirtualMemory@20
+ NtQueryAttributesFile ;;= _NtQueryAttributesFile@8
+ NtQueryDirectoryFile ;;= _NtQueryDirectoryFile@44
+ NtQueryDirectoryObject ;;= _NtQueryDirectoryObject@28
+ NtQueryFullAttributesFile ;;= _NtQueryFullAttributesFile@8
+ NtQueryEvent ;;= _NtQueryEvent@20
+ NtQueryInformationFile ;;= _NtQueryInformationFile@20
+ NtQueryInformationProcess ;;= _NtQueryInformationProcess@20
+ NtQueryInformationThread ;;= _NtQueryInformationThread@20
+ NtQueryInformationToken ;;= _NtQueryInformationToken@20
+ NtQueryKey ;;= _NtQueryKey@20
+ NtQueryObject ;;= _NtQueryObject@20
+ NtQuerySection ;;= _NtQuerySection@20
+ NtQuerySecurityObject ;;= _NtQuerySecurityObject@20
+ NtQuerySymbolicLinkObject ;;= _NtQuerySymbolicLinkObject@12
+ NtQuerySystemInformation ;;= _NtQuerySystemInformation@16
+ NtQueryTimerResolution ;;= _NtQueryTimerResolution@12
+ NtQueryValueKey ;;= _NtQueryValueKey@24
+ NtQueryVirtualMemory ;;= _NtQueryVirtualMemory@24
+ NtQueryVolumeInformationFile ;;= _NtQueryVolumeInformationFile@20
+ NtReadFile ;;= _NtReadFile@36
+ NtReadVirtualMemory ;;= _NtReadVirtualMemory@20
+ NtResetEvent ;;= _NtResetEvent@8
+ NtResumeProcess ;;= _NtResumeProcess@4
+ NtResumeThread ;;= _NtResumeThread@8
+ NtSetContextThread ;;= _NtSetContextThread@8
+ NtSetEvent ;;= _NtSetEvent@8
+ NtSetInformationFile ;;= _NtSetInformationFile@20
+ NtSetInformationObject ;;= _NtSetInformationObject@16
+ NtSetInformationProcess ;;= _NtSetInformationProcess@16
+ NtSetInformationThread ;;= _NtSetInformationThread@16
+ NtSetTimerResolution ;;= _NtSetTimerResolution@12
+ NtSuspendProcess ;;= _NtSuspendProcess@4
+ NtSuspendThread ;;= _NtSuspendThread@8
+ NtTerminateProcess ;;= _NtTerminateProcess@8
+ NtTerminateThread ;;= _NtTerminateThread@8
+ NtUnmapViewOfSection ;;= _NtUnmapViewOfSection@8
+ NtWaitForMultipleObjects ;;= _NtWaitForMultipleObjects@20
+ NtWaitForSingleObject ;;= _NtWaitForSingleObject@12
+ NtWriteFile ;;= _NtWriteFile@36
+ NtWriteVirtualMemory ;;= _NtWriteVirtualMemory@20
+ NtYieldExecution ;;= _NtYieldExecution@0
+
+ LdrInitializeThunk ;;= _LdrInitializeThunk@12
+ LdrRegisterDllNotification ;;= _LdrRegisterDllNotification@16
+ LdrLoadDll ;;= _LdrLoadDll@16
+ LdrUnloadDll ;;= _LdrUnloadDll@4
+ LdrGetDllHandle ;;= _LdrGetDllHandle@16
+ LdrGetDllHandleEx ;;= _LdrGetDllHandleEx@20
+ LdrGetDllHandleByMapping ;;= _LdrGetDllHandleByMapping@8
+ LdrGetDllHandleByName ;;= _LdrGetDllHandleByName@12
+ LdrAddRefDll ;;= _LdrAddRefDll@8
+ LdrGetProcedureAddress ;;= _LdrGetProcedureAddress@12
+ LdrGetProcedureAddressEx ;;= _LdrGetProcedureAddressEx@16
+ LdrLockLoaderLock ;;= _LdrLockLoaderLock@12
+ LdrUnlockLoaderLock ;;= _LdrUnlockLoaderLock@8
+
+ RtlAcquirePebLock ;;= _RtlAcquirePebLock@0
+ RtlAddAccessAllowedAce ;;= _RtlAddAccessAllowedAce@16
+ RtlAddAccessDeniedAce ;;= _RtlAddAccessDeniedAce@16
+ RtlAllocateHeap ;;= _RtlAllocateHeap@12
+ RtlCompactHeap ;;= _RtlCompactHeap@8
+ RtlCopySid ;;= _RtlCopySid@12
+ RtlCreateAcl ;;= _RtlCreateAcl@12
+ RtlCreateHeap ;;= _RtlCreateHeap@24
+ RtlCreateProcessParameters ;;= _RtlCreateProcessParameters@40
+ RtlCreateSecurityDescriptor ;;= _RtlCreateSecurityDescriptor@8
+ RtlCreateUserProcess ;;= _RtlCreateUserProcess@40
+ RtlCreateUserThread ;;= _RtlCreateUserThread@40
+ RtlDestroyProcessParameters ;;= _RtlDestroyProcessParameters@4
+ RtlDosApplyFileIsolationRedirection_Ustr ;;= _RtlDosApplyFileIsolationRedirection_Ustr@36
+ RtlEqualSid ;;= _RtlEqualSid@8
+ RtlExitUserProcess ;;= _RtlExitUserProcess@4
+ RtlExitUserThread ;;= _RtlExitUserThread@4
+ RtlExpandEnvironmentStrings_U ;;= _RtlExpandEnvironmentStrings_U@16
+ RtlFreeHeap ;;= _RtlFreeHeap@12
+ RtlFreeUnicodeString ;;= _RtlFreeUnicodeString@4
+ RtlGetLastNtStatus ;;= _RtlGetLastNtStatus@0
+ RtlGetLastWin32Error ;;= _RtlGetLastWin32Error@0
+ RtlGetVersion ;;= _RtlGetVersion@4
+ RtlGetNtProductType ;;= _RtlGetNtProductType@4
+ RtlInitializeSid ;;= _RtlInitializeSid@12
+ RtlNtStatusToDosError ;;= _RtlNtStatusToDosError@4
+ RtlReAllocateHeap ;;= _RtlReAllocateHeap@16
+ RtlReleasePebLock ;;= _RtlReleasePebLock@0
+ RtlRestoreLastWin32Error ;;= _RtlRestoreLastWin32Error@4
+ RtlSetDaclSecurityDescriptor ;;= _RtlSetDaclSecurityDescriptor@16
+ RtlSetLastWin32Error ;;= _RtlSetLastWin32Error@4
+ RtlSetLastWin32ErrorAndNtStatusFromNtStatus ;;= _RtlSetLastWin32ErrorAndNtStatusFromNtStatus@4
+ RtlSizeHeap ;;= _RtlSizeHeap@12
+ RtlSubAuthoritySid ;;= _RtlSubAuthoritySid@8
+ RtlQueryPerformanceCounter ;;= _RtlQueryPerformanceCounter@4
+ RtlGetSystemTimePrecise ;;= _RtlGetSystemTimePrecise@0
+
diff --git a/src/VBox/Runtime/r3/win/path-win.cpp b/src/VBox/Runtime/r3/win/path-win.cpp
new file mode 100644
index 00000000..587de0f6
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/path-win.cpp
@@ -0,0 +1,743 @@
+/* $Id: path-win.cpp $ */
+/** @file
+ * IPRT - Path manipulation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PATH
+#include <iprt/win/windows.h>
+#include <iprt/win/shlobj.h>
+
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/utf16.h>
+#include "internal/path.h"
+#include "internal/fs.h"
+
+/* Needed for lazy loading SHGetFolderPathW in RTPathUserDocuments(). */
+typedef HRESULT WINAPI FNSHGETFOLDERPATHW(HWND, int, HANDLE, DWORD, LPWSTR);
+typedef FNSHGETFOLDERPATHW *PFNSHGETFOLDERPATHW;
+
+
+RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath)
+{
+ /*
+ * Convert to UTF-16, call Win32 APIs, convert back.
+ */
+ PRTUTF16 pwszPath;
+ int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ LPWSTR lpFile;
+ WCHAR wsz[RTPATH_MAX];
+ rc = GetFullPathNameW((LPCWSTR)pwszPath, RT_ELEMENTS(wsz), &wsz[0], &lpFile);
+ if (rc > 0 && rc < RT_ELEMENTS(wsz))
+ {
+ /* Check that it exists. (Use RTPathAbs() to just resolve the name.) */
+ DWORD dwAttr = GetFileAttributesW(wsz);
+ if (dwAttr != INVALID_FILE_ATTRIBUTES)
+ rc = RTUtf16ToUtf8Ex((PRTUTF16)&wsz[0], RTSTR_MAX, &pszRealPath, cchRealPath, NULL);
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (rc <= 0)
+ rc = RTErrConvertFromWin32(GetLastError());
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+
+ RTPathWinFree(pwszPath);
+ }
+ return rc;
+}
+
+#if 0
+RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
+{
+ /*
+ * Validation.
+ */
+ AssertPtr(pszAbsPath);
+ AssertPtr(pszPath);
+ if (RT_UNLIKELY(!*pszPath))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Convert to UTF-16, call Win32 API, convert back.
+ */
+ LPWSTR pwszPath;
+ int rc = RTStrToUtf16(pszPath, &pwszPath);
+ if (!RT_SUCCESS(rc))
+ return (rc);
+
+ LPWSTR pwszFile; /* Ignored */
+ RTUTF16 wsz[RTPATH_MAX];
+ rc = GetFullPathNameW(pwszPath, RT_ELEMENTS(wsz), &wsz[0], &pwszFile);
+ if (rc > 0 && rc < RT_ELEMENTS(wsz))
+ {
+ size_t cch;
+ rc = RTUtf16ToUtf8Ex(&wsz[0], RTSTR_MAX, &pszAbsPath, cchAbsPath, &cch);
+ if (RT_SUCCESS(rc))
+ {
+# if 1 /** @todo This code is completely bonkers. */
+ /*
+ * Remove trailing slash if the path may be pointing to a directory.
+ * (See posix variant.)
+ */
+ if ( cch > 1
+ && RTPATH_IS_SLASH(pszAbsPath[cch - 1])
+ && !RTPATH_IS_VOLSEP(pszAbsPath[cch - 2])
+ && !RTPATH_IS_SLASH(pszAbsPath[cch - 2]))
+ pszAbsPath[cch - 1] = '\0';
+# endif
+ }
+ }
+ else if (rc <= 0)
+ rc = RTErrConvertFromWin32(GetLastError());
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+
+ RTUtf16Free(pwszPath);
+ return rc;
+}
+#endif
+
+
+RTDECL(int) RTPathUserHome(char *pszPath, size_t cchPath)
+{
+ /*
+ * Validate input
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(cchPath, VERR_INVALID_PARAMETER);
+
+ RTUTF16 wszPath[RTPATH_MAX];
+ bool fValidFolderPath = false;
+
+ /*
+ * Try with Windows XP+ functionality first.
+ */
+ RTLDRMOD hShell32;
+ int rc = RTLdrLoadSystem("Shell32.dll", true /*fNoUnload*/, &hShell32);
+ if (RT_SUCCESS(rc))
+ {
+ PFNSHGETFOLDERPATHW pfnSHGetFolderPathW;
+ rc = RTLdrGetSymbol(hShell32, "SHGetFolderPathW", (void**)&pfnSHGetFolderPathW);
+ if (RT_SUCCESS(rc))
+ {
+ HRESULT hrc = pfnSHGetFolderPathW(0, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, wszPath);
+ fValidFolderPath = (hrc == S_OK);
+ }
+ RTLdrClose(hShell32);
+ }
+
+ DWORD dwAttr;
+ if ( !fValidFolderPath
+ || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
+ || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ /*
+ * Fall back to Windows specific environment variables. HOME is not used.
+ */
+ if ( !GetEnvironmentVariableW(L"USERPROFILE", &wszPath[0], RTPATH_MAX)
+ || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
+ || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ /* %HOMEDRIVE%%HOMEPATH% */
+ if (!GetEnvironmentVariableW(L"HOMEDRIVE", &wszPath[0], RTPATH_MAX))
+ return VERR_PATH_NOT_FOUND;
+ size_t const cwc = RTUtf16Len(&wszPath[0]);
+ if ( !GetEnvironmentVariableW(L"HOMEPATH", &wszPath[cwc], RTPATH_MAX - (DWORD)cwc)
+ || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
+ || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
+ return VERR_PATH_NOT_FOUND;
+ }
+ }
+
+ /*
+ * Convert and return.
+ */
+ return RTUtf16ToUtf8Ex(&wszPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
+}
+
+
+RTDECL(int) RTPathUserDocuments(char *pszPath, size_t cchPath)
+{
+ /*
+ * Validate input
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(cchPath, VERR_INVALID_PARAMETER);
+
+ RTLDRMOD hShell32;
+ int rc = RTLdrLoadSystem("Shell32.dll", true /*fNoUnload*/, &hShell32);
+ if (RT_SUCCESS(rc))
+ {
+ PFNSHGETFOLDERPATHW pfnSHGetFolderPathW;
+ rc = RTLdrGetSymbol(hShell32, "SHGetFolderPathW", (void**)&pfnSHGetFolderPathW);
+ if (RT_SUCCESS(rc))
+ {
+ RTUTF16 wszPath[RTPATH_MAX];
+ HRESULT hrc = pfnSHGetFolderPathW(0, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, wszPath);
+ if ( hrc == S_OK /* Found */
+ || hrc == S_FALSE) /* Found, but doesn't exist */
+ {
+ /*
+ * Convert and return.
+ */
+ RTLdrClose(hShell32);
+ return RTUtf16ToUtf8Ex(&wszPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
+ }
+ }
+ RTLdrClose(hShell32);
+ }
+ return VERR_PATH_NOT_FOUND;
+}
+
+
+#if 0 /* use nt version of this */
+
+RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
+}
+#endif
+#if 0
+
+
+RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
+ AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
+ && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+ ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Query file info.
+ */
+ uint32_t uReparseTag = RTFSMODE_SYMLINK_REPARSE_TAG;
+ WIN32_FILE_ATTRIBUTE_DATA Data;
+ PRTUTF16 pwszPath;
+ int rc = RTStrToUtf16(pszPath, &pwszPath);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (!GetFileAttributesExW(pwszPath, GetFileExInfoStandard, &Data))
+ {
+ /* Fallback to FindFileFirst in case of sharing violation. */
+ if (GetLastError() == ERROR_SHARING_VIOLATION)
+ {
+ WIN32_FIND_DATAW FindData;
+ HANDLE hDir = FindFirstFileW(pwszPath, &FindData);
+ if (hDir == INVALID_HANDLE_VALUE)
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTUtf16Free(pwszPath);
+ return rc;
+ }
+ FindClose(hDir);
+
+ Data.dwFileAttributes = FindData.dwFileAttributes;
+ Data.ftCreationTime = FindData.ftCreationTime;
+ Data.ftLastAccessTime = FindData.ftLastAccessTime;
+ Data.ftLastWriteTime = FindData.ftLastWriteTime;
+ Data.nFileSizeHigh = FindData.nFileSizeHigh;
+ Data.nFileSizeLow = FindData.nFileSizeLow;
+ uReparseTag = FindData.dwReserved0;
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTUtf16Free(pwszPath);
+ return rc;
+ }
+ }
+
+ /*
+ * Getting the information for the link target is a bit annoying and
+ * subject to the same access violation mess as above.. :/
+ */
+ /** @todo we're too lazy wrt to error paths here... */
+ if ( (Data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ && ((fFlags & RTPATH_F_FOLLOW_LINK) || uReparseTag != RTFSMODE_SYMLINK_REPARSE_TAG))
+ {
+ HANDLE hFinal = CreateFileW(pwszPath,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hFinal != INVALID_HANDLE_VALUE)
+ {
+ BY_HANDLE_FILE_INFORMATION FileData;
+ if (GetFileInformationByHandle(hFinal, &FileData))
+ {
+ Data.dwFileAttributes = FileData.dwFileAttributes;
+ Data.ftCreationTime = FileData.ftCreationTime;
+ Data.ftLastAccessTime = FileData.ftLastAccessTime;
+ Data.ftLastWriteTime = FileData.ftLastWriteTime;
+ Data.nFileSizeHigh = FileData.nFileSizeHigh;
+ Data.nFileSizeLow = FileData.nFileSizeLow;
+ uReparseTag = 0;
+ }
+ CloseHandle(hFinal);
+ }
+ else if (GetLastError() != ERROR_SHARING_VIOLATION)
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTUtf16Free(pwszPath);
+ return rc;
+ }
+ }
+
+ RTUtf16Free(pwszPath);
+
+ /*
+ * Setup the returned data.
+ */
+ pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32)
+ | (uint64_t)Data.nFileSizeLow;
+ pObjInfo->cbAllocated = pObjInfo->cbObject;
+
+ Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime));
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime);
+ pObjInfo->ChangeTime = pObjInfo->ModificationTime;
+
+ pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszPath, strlen(pszPath), uReparseTag);
+
+ /*
+ * Requested attributes (we cannot provide anything actually).
+ */
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pObjInfo->Attr.u.Unix.uid = ~0U;
+ pObjInfo->Attr.u.Unix.gid = ~0U;
+ pObjInfo->Attr.u.Unix.cHardlinks = 1;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /** @todo use volume serial number */
+ pObjInfo->Attr.u.Unix.INodeId = 0; /** @todo use fileid (see GetFileInformationByHandle). */
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pObjInfo->Attr.u.UnixOwner.uid = ~0U;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pObjInfo->Attr.u.UnixGroup.gid = ~0U;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+#endif /* using NT version*/
+
+
+RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ return RTPathSetTimesEx(pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, RTPATH_F_ON_LINK);
+}
+
+
+RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the path.
+ */
+ PRTUTF16 pwszPath;
+ int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile;
+ if (fFlags & RTPATH_F_FOLLOW_LINK)
+ hFile = CreateFileW(pwszPath,
+ FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */
+ NULL, /* security attribs */
+ OPEN_EXISTING, /* dwCreationDisposition */
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ else
+ {
+/** @todo Symlink: Test RTPathSetTimesEx on Windows. (The code is disabled
+ * because it's not tested yet.) */
+#if 0 //def FILE_FLAG_OPEN_REPARSE_POINT
+ hFile = CreateFileW(pwszPath,
+ FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */
+ NULL, /* security attribs */
+ OPEN_EXISTING, /* dwCreationDisposition */
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
+#endif
+ hFile = CreateFileW(pwszPath,
+ FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */
+ NULL, /* security attribs */
+ OPEN_EXISTING, /* dwCreationDisposition */
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ }
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Check if it's a no-op.
+ */
+ if (!pAccessTime && !pModificationTime && !pBirthTime)
+ rc = VINF_SUCCESS; /* NOP */
+ else
+ {
+ /*
+ * Convert the input and call the API.
+ */
+ FILETIME CreationTimeFT;
+ PFILETIME pCreationTimeFT = NULL;
+ if (pBirthTime)
+ pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT);
+
+ FILETIME LastAccessTimeFT;
+ PFILETIME pLastAccessTimeFT = NULL;
+ if (pAccessTime)
+ pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT);
+
+ FILETIME LastWriteTimeFT;
+ PFILETIME pLastWriteTimeFT = NULL;
+ if (pModificationTime)
+ pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT);
+
+ if (SetFileTime(hFile, pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT))
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTPathSetTimes('%s', %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n",
+ pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc));
+ }
+ }
+ BOOL fRc = CloseHandle(hFile); Assert(fRc); NOREF(fRc);
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and lasterr=%u\n", pszPath, rc, Err));
+ }
+
+ RTPathWinFree(pwszPath);
+ }
+
+ LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
+ pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
+ pChangeTime, pChangeTime, pBirthTime, pBirthTime));
+ return rc;
+}
+
+
+
+
+/**
+ * Internal worker for RTFileRename and RTFileMove.
+ *
+ * @returns iprt status code.
+ * @param pszSrc The source filename.
+ * @param pszDst The destination filename.
+ * @param fFlags The windows MoveFileEx flags.
+ * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
+ * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
+ * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
+ * not a directory (we are NOT checking whether it's a file).
+ */
+DECLHIDDEN(int) rtPathWin32MoveRename(const char *pszSrc, const char *pszDst, uint32_t fFlags, RTFMODE fFileType)
+{
+ /*
+ * Convert the strings.
+ */
+ PRTUTF16 pwszSrc;
+ int rc = RTPathWinFromUtf8(&pwszSrc, pszSrc, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszDst;
+ rc = RTPathWinFromUtf8(&pwszDst, pszDst, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check object type if requested.
+ * This is open to race conditions.
+ */
+ if (fFileType)
+ {
+ DWORD dwAttr = GetFileAttributesW(pwszSrc);
+ if (dwAttr == INVALID_FILE_ATTRIBUTES)
+ rc = RTErrConvertFromWin32(GetLastError());
+ else if (RTFS_IS_DIRECTORY(fFileType))
+ rc = dwAttr & FILE_ATTRIBUTE_DIRECTORY ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
+ else
+ rc = dwAttr & FILE_ATTRIBUTE_DIRECTORY ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ if (MoveFileExW(pwszSrc, pwszDst, fFlags))
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("MoveFileExW('%s', '%s', %#x, %RTfmode): fails with rc=%Rrc & lasterr=%d\n",
+ pszSrc, pszDst, fFlags, fFileType, rc, Err));
+ }
+ }
+ RTPathWinFree(pwszDst);
+ }
+ RTPathWinFree(pwszSrc);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Call the worker.
+ */
+ int rc = rtPathWin32MoveRename(pszSrc, pszDst, fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0, 0);
+
+ LogFlow(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathUnlink(const char *pszPath, uint32_t fUnlink)
+{
+ RT_NOREF_PV(pszPath); RT_NOREF_PV(fUnlink);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTDECL(bool) RTPathExists(const char *pszPath)
+{
+ return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
+}
+
+
+RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, false);
+ AssertReturn(*pszPath, false);
+ Assert(RTPATH_F_IS_VALID(fFlags, 0));
+
+ /*
+ * Try query file info.
+ */
+ DWORD dwAttr;
+ PRTUTF16 pwszPath;
+ int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ dwAttr = GetFileAttributesW(pwszPath);
+ RTPathWinFree(pwszPath);
+ }
+ else
+ dwAttr = INVALID_FILE_ATTRIBUTES;
+ if (dwAttr == INVALID_FILE_ATTRIBUTES)
+ return false;
+
+#ifdef FILE_ATTRIBUTE_REPARSE_POINT
+ if ( (fFlags & RTPATH_F_FOLLOW_LINK)
+ && (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT))
+ {
+ AssertFailed();
+ /** @todo Symlinks: RTPathExists+RTPathExistsEx is misbehaving on symbolic
+ * links on Windows. */
+ }
+#endif
+
+ return true;
+}
+
+
+RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
+{
+ int rc;
+
+ if (cchPath > 0)
+ {
+ /*
+ * GetCurrentDirectory may in some cases omit the drive letter, according
+ * to MSDN, thus the GetFullPathName call.
+ */
+ RTUTF16 wszCurPath[RTPATH_MAX];
+ if (GetCurrentDirectoryW(RTPATH_MAX, wszCurPath))
+ {
+ RTUTF16 wszFullPath[RTPATH_MAX];
+ if (GetFullPathNameW(wszCurPath, RTPATH_MAX, wszFullPath, NULL))
+ {
+ if ( wszFullPath[1] == ':'
+ && RT_C_IS_LOWER(wszFullPath[0]))
+ wszFullPath[0] = RT_C_TO_UPPER(wszFullPath[0]);
+
+ rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ return rc;
+}
+
+
+RTDECL(int) RTPathSetCurrent(const char *pszPath)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+
+ /*
+ * This interface is almost identical to the Windows API.
+ */
+ PRTUTF16 pwszPath;
+ int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo improve the slash stripping a bit? */
+ size_t cwc = RTUtf16Len(pwszPath);
+ if ( cwc >= 2
+ && ( pwszPath[cwc - 1] == L'/'
+ || pwszPath[cwc - 1] == L'\\')
+ && pwszPath[cwc - 2] != ':')
+ pwszPath[cwc - 1] = L'\0';
+
+ if (!SetCurrentDirectoryW(pwszPath))
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTPathWinFree(pwszPath);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPathGetCurrentOnDrive(char chDrive, char *pszPath, size_t cbPath)
+{
+ int rc;
+ if (cbPath > 0)
+ {
+ WCHAR wszInput[4];
+ wszInput[0] = chDrive;
+ wszInput[1] = ':';
+ wszInput[2] = '\0';
+ RTUTF16 wszFullPath[RTPATH_MAX];
+ if (GetFullPathNameW(wszInput, RTPATH_MAX, wszFullPath, NULL))
+ rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cbPath, NULL);
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/pathint-win.cpp b/src/VBox/Runtime/r3/win/pathint-win.cpp
new file mode 100644
index 00000000..81799cc5
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/pathint-win.cpp
@@ -0,0 +1,204 @@
+/* $Id: pathint-win.cpp $ */
+/** @file
+ * IPRT - Windows, Internal Path stuff.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include <iprt/path.h>
+
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include <iprt/nt/nt-and-windows.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The max number of non-null characters we pass to an Win32 API.
+ * You would think that MAX_PATH gives this length, however CreateDirectoryW was
+ * found to fail on Windows 10 (1803++) if given a perfectly formed path argument
+ * of 248 or more characters. Same when going thru UNC.
+ *
+ * So, to be conservative, we put the max number of characters in a non-\\?\
+ * path to 243, not counting the terminator.
+ */
+#define ACTUAL_MAX_PATH 243
+
+
+DECL_NO_INLINE(static, bool) rtPathWinTryConvertToAbs(PRTUTF16 *ppwszPath)
+{
+ RTUTF16 wszFullPath[MAX_PATH + 1];
+ DWORD cwcFull = GetFullPathNameW(*ppwszPath, MAX_PATH + 1, wszFullPath, NULL);
+ if (cwcFull <= ACTUAL_MAX_PATH)
+ {
+ RTUtf16Free(*ppwszPath);
+ PRTUTF16 const pwszCopy = RTUtf16Dup(wszFullPath);
+ *ppwszPath = pwszCopy;
+ if (pwszCopy)
+ return true;
+ }
+ return false;
+}
+
+
+RTDECL(int) RTPathWinFromUtf8(PRTUTF16 *ppwszPath, const char *pszPath, uint32_t fFlags)
+{
+ Assert(fFlags == 0);
+ RT_NOREF(fFlags);
+
+ /*
+ * Do a straight conversion first.
+ */
+ *ppwszPath = NULL;
+ size_t cwcResult = 0;
+ int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, ppwszPath, 0, &cwcResult);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check the resulting length. This is straight forward for absolute
+ * paths, but gets complicated for relative ones.
+ */
+ if (cwcResult <= ACTUAL_MAX_PATH)
+ {
+ if (RT_C_IS_ALPHA(pszPath[0]) && pszPath[1] == ':')
+ {
+ if (RTPATH_IS_SLASH(pszPath[2]))
+ return VINF_SUCCESS;
+
+ /* Drive relative path. Found no simple way of getting the current
+ path of a drive, so we try convert it to an absolute path and see
+ how that works out. It is what the API we're calling will have to
+ do anyway, so this should perform just as well. */
+ if (rtPathWinTryConvertToAbs(ppwszPath))
+ return VINF_SUCCESS;
+ }
+ else if (RTPATH_IS_SLASH(pszPath[0]))
+ {
+ if ( RTPATH_IS_SLASH(pszPath[1])
+ && !RTPATH_IS_SLASH(pszPath[2])
+ && pszPath[2] != '\0')
+ {
+ /* Passthru prefix '\\?\' is fine. */
+ if ( pszPath[2] == '?'
+ && !RTPATH_IS_SLASH(pszPath[3]))
+ return VINF_SUCCESS;
+
+ /* UNC requires a longer prefix ('\??\UNC\' instead of '\??\'), so
+ subtract 3 chars from the max limit to be on the safe side. */
+ if (cwcResult <= ACTUAL_MAX_PATH - 3)
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ /* Drive relative. Win32 will prepend a two letter drive specification. */
+ if (cwcResult <= ACTUAL_MAX_PATH - 2)
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ /* Relative to CWD. We can use the API to get it's current length.
+ Any race conditions here is entirely the caller's problem. */
+ size_t cwcCwd = GetCurrentDirectoryW(0, NULL);
+ if (cwcCwd + cwcResult <= ACTUAL_MAX_PATH - 1)
+ return VINF_SUCCESS;
+ }
+ }
+ /*
+ * We're good if the caller already supplied the passthru/length prefix: '\\?\'
+ */
+ else if ( pszPath[1] == '?'
+ && RTPATH_IS_SLASH(pszPath[3])
+ && RTPATH_IS_SLASH(pszPath[1])
+ && RTPATH_IS_SLASH(pszPath[0]))
+ return VINF_SUCCESS;
+
+ /*
+ * Long path requiring \\?\ prefixing.
+ *
+ * We piggy back on the NT conversion here and ASSUME RTUtf16Free is the right
+ * way to free the result.
+ */
+ RTUtf16Free(*ppwszPath);
+ *ppwszPath = NULL;
+
+ struct _UNICODE_STRING NtName = { 0, 0, NULL };
+ HANDLE hRootDir = NULL;
+ rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ /* No root dir handle. */
+ if (hRootDir == NULL)
+ {
+ /* Convert the NT '\??\' prefix to a win32 passthru prefix '\\?\' */
+ if ( NtName.Buffer[0] == '\\'
+ && NtName.Buffer[1] == '?'
+ && NtName.Buffer[2] == '?'
+ && NtName.Buffer[3] == '\\')
+ {
+ NtName.Buffer[1] = '\\';
+
+ /* Zero termination paranoia. */
+ if (NtName.Buffer[NtName.Length / sizeof(RTUTF16)] == '\0')
+ {
+ *ppwszPath = NtName.Buffer;
+ return VINF_SUCCESS;
+ }
+ AssertMsgFailed(("Length=%u %.*ls\n", NtName.Length, NtName.Length / sizeof(RTUTF16), NtName.Buffer));
+ }
+ else
+ AssertMsgFailed(("%ls\n", NtName.Buffer));
+ }
+ else
+ AssertMsgFailed(("%s\n", pszPath));
+ RTNtPathFree(&NtName, &hRootDir);
+ }
+ }
+ return rc;
+}
+
+
+RTDECL(void) RTPathWinFree(PRTUTF16 pwszPath)
+{
+ RTUtf16Free(pwszPath);
+}
+
diff --git a/src/VBox/Runtime/r3/win/pipe-win.cpp b/src/VBox/Runtime/r3/win/pipe-win.cpp
new file mode 100644
index 00000000..93bf9afc
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/pipe-win.cpp
@@ -0,0 +1,1537 @@
+/* $Id: pipe-win.cpp $ */
+/** @file
+ * IPRT - Anonymous Pipes, Windows Implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/pipe.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/pipe.h"
+#include "internal/magics.h"
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The pipe buffer size we prefer. */
+#define RTPIPE_NT_SIZE _64K
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTPIPEINTERNAL
+{
+ /** Magic value (RTPIPE_MAGIC). */
+ uint32_t u32Magic;
+ /** The pipe handle. */
+ HANDLE hPipe;
+ /** Set if this is the read end, clear if it's the write end. */
+ bool fRead;
+ /** RTPipeFromNative: Leave native handle open on RTPipeClose. */
+ bool fLeaveOpen;
+ /** Set if there is already pending I/O. */
+ bool fIOPending;
+ /** Set if the zero byte read that the poll code using is pending. */
+ bool fZeroByteRead;
+ /** Set if the pipe is broken. */
+ bool fBrokenPipe;
+ /** Set if we've promised that the handle is writable. */
+ bool fPromisedWritable;
+ /** Set if created inheritable. */
+ bool fCreatedInheritable;
+ /** Usage counter. */
+ uint32_t cUsers;
+ /** The overlapped I/O structure we use. */
+ OVERLAPPED Overlapped;
+ /** Bounce buffer for writes. */
+ uint8_t *pbBounceBuf;
+ /** Amount of used buffer space. */
+ size_t cbBounceBufUsed;
+ /** Amount of allocated buffer space. */
+ size_t cbBounceBufAlloc;
+ /** The handle of the poll set currently polling on this pipe.
+ * We can only have one poller at the time (lazy bird). */
+ RTPOLLSET hPollSet;
+ /** Critical section protecting the above members.
+ * (Taking the lazy/simple approach.) */
+ RTCRITSECT CritSect;
+ /** Buffer for the zero byte read. */
+ uint8_t abBuf[8];
+} RTPIPEINTERNAL;
+
+
+
+/**
+ * Wrapper for getting FILE_PIPE_LOCAL_INFORMATION via the NT API.
+ *
+ * @returns Success indicator (true/false).
+ * @param pThis The pipe.
+ * @param pInfo The info structure.
+ */
+static bool rtPipeQueryNtInfo(RTPIPEINTERNAL *pThis, FILE_PIPE_LOCAL_INFORMATION *pInfo)
+{
+ IO_STATUS_BLOCK Ios;
+ RT_ZERO(Ios);
+ RT_ZERO(*pInfo);
+ NTSTATUS rcNt = NtQueryInformationFile(pThis->hPipe, &Ios, pInfo, sizeof(*pInfo), FilePipeLocalInformation);
+ return rcNt >= 0;
+}
+
+
+RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
+ AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the read end of the pipe.
+ */
+ DWORD dwErr;
+ HANDLE hPipeR;
+ HANDLE hPipeW;
+ int rc;
+ for (;;)
+ {
+ static volatile uint32_t g_iNextPipe = 0;
+ char szName[128];
+ RTStrPrintf(szName, sizeof(szName), "\\\\.\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe));
+
+ SECURITY_ATTRIBUTES SecurityAttributes;
+ PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
+ if (fFlags & RTPIPE_C_INHERIT_READ)
+ {
+ SecurityAttributes.nLength = sizeof(SecurityAttributes);
+ SecurityAttributes.lpSecurityDescriptor = NULL;
+ SecurityAttributes.bInheritHandle = TRUE;
+ pSecurityAttributes = &SecurityAttributes;
+ }
+
+ DWORD dwOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED;
+#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
+ dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
+#endif
+
+ DWORD dwPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT;
+#ifdef PIPE_REJECT_REMOTE_CLIENTS
+ dwPipeMode |= PIPE_REJECT_REMOTE_CLIENTS;
+#endif
+
+ hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
+ NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
+#ifdef PIPE_REJECT_REMOTE_CLIENTS
+ if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
+ {
+ dwPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS;
+ hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
+ NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
+ }
+#endif
+#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
+ if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
+ {
+ dwOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE;
+ hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
+ NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
+ }
+#endif
+ if (hPipeR != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Connect to the pipe (the write end).
+ * We add FILE_READ_ATTRIBUTES here to make sure we can query the
+ * pipe state later on.
+ */
+ pSecurityAttributes = NULL;
+ if (fFlags & RTPIPE_C_INHERIT_WRITE)
+ {
+ SecurityAttributes.nLength = sizeof(SecurityAttributes);
+ SecurityAttributes.lpSecurityDescriptor = NULL;
+ SecurityAttributes.bInheritHandle = TRUE;
+ pSecurityAttributes = &SecurityAttributes;
+ }
+
+ hPipeW = CreateFileA(szName,
+ GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
+ 0 /*dwShareMode*/,
+ pSecurityAttributes,
+ OPEN_EXISTING /* dwCreationDisposition */,
+ FILE_FLAG_OVERLAPPED /*dwFlagsAndAttributes*/,
+ NULL /*hTemplateFile*/);
+ if (hPipeW != INVALID_HANDLE_VALUE)
+ break;
+ dwErr = GetLastError();
+ CloseHandle(hPipeR);
+ }
+ else
+ dwErr = GetLastError();
+ if ( dwErr != ERROR_PIPE_BUSY /* already exist - compatible */
+ && dwErr != ERROR_ACCESS_DENIED /* already exist - incompatible */)
+ return RTErrConvertFromWin32(dwErr);
+ /* else: try again with a new name */
+ }
+
+ /*
+ * Create the two handles.
+ */
+ RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (pThisR)
+ {
+ RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (pThisW)
+ {
+ rc = RTCritSectInit(&pThisR->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&pThisW->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThisR->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
+ TRUE /*fInitialState*/, NULL /*pName*/);
+ if (pThisR->Overlapped.hEvent != NULL)
+ {
+ pThisW->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
+ TRUE /*fInitialState*/, NULL /*pName*/);
+ if (pThisW->Overlapped.hEvent != NULL)
+ {
+ pThisR->u32Magic = RTPIPE_MAGIC;
+ pThisW->u32Magic = RTPIPE_MAGIC;
+ pThisR->hPipe = hPipeR;
+ pThisW->hPipe = hPipeW;
+ pThisR->fRead = true;
+ pThisW->fRead = false;
+ pThisR->fLeaveOpen = false;
+ pThisW->fLeaveOpen = false;
+ //pThisR->fIOPending = false;
+ //pThisW->fIOPending = false;
+ //pThisR->fZeroByteRead = false;
+ //pThisW->fZeroByteRead = false;
+ //pThisR->fBrokenPipe = false;
+ //pThisW->fBrokenPipe = false;
+ //pThisW->fPromisedWritable = false;
+ //pThisR->fPromisedWritable = false;
+ pThisW->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_WRITE);
+ pThisR->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_READ);
+ //pThisR->cUsers = 0;
+ //pThisW->cUsers = 0;
+ //pThisR->pbBounceBuf = NULL;
+ //pThisW->pbBounceBuf = NULL;
+ //pThisR->cbBounceBufUsed = 0;
+ //pThisW->cbBounceBufUsed = 0;
+ //pThisR->cbBounceBufAlloc = 0;
+ //pThisW->cbBounceBufAlloc = 0;
+ pThisR->hPollSet = NIL_RTPOLLSET;
+ pThisW->hPollSet = NIL_RTPOLLSET;
+
+ *phPipeRead = pThisR;
+ *phPipeWrite = pThisW;
+ return VINF_SUCCESS;
+ }
+ CloseHandle(pThisR->Overlapped.hEvent);
+ }
+ RTCritSectDelete(&pThisW->CritSect);
+ }
+ RTCritSectDelete(&pThisR->CritSect);
+ }
+ RTMemFree(pThisW);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTMemFree(pThisR);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ CloseHandle(hPipeR);
+ CloseHandle(hPipeW);
+ return rc;
+}
+
+
+/**
+ * Common worker for handling I/O completion.
+ *
+ * This is used by RTPipeClose, RTPipeWrite and RTPipeWriteBlocking.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ */
+static int rtPipeWriteCheckCompletion(RTPIPEINTERNAL *pThis)
+{
+ int rc;
+ DWORD dwRc = WaitForSingleObject(pThis->Overlapped.hEvent, 0);
+ if (dwRc == WAIT_OBJECT_0)
+ {
+ DWORD cbWritten = 0;
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE))
+ {
+ for (;;)
+ {
+ if (cbWritten >= pThis->cbBounceBufUsed)
+ {
+ pThis->fIOPending = false;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* resubmit the remainder of the buffer - can this actually happen? */
+ pThis->cbBounceBufUsed -= cbWritten;
+ memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed);
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ if (!WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed, &cbWritten, &pThis->Overlapped))
+ {
+ DWORD const dwErr = GetLastError();
+ if (dwErr == ERROR_IO_PENDING)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fIOPending = false;
+ if (dwErr == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ }
+ break;
+ }
+ Assert(cbWritten > 0);
+ }
+ }
+ else
+ {
+ pThis->fIOPending = false;
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else if (dwRc == WAIT_TIMEOUT)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fIOPending = false;
+ if (dwRc == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ return rc;
+}
+
+
+
+RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ if (pThis == NIL_RTPIPE)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
+ RTCritSectEnter(&pThis->CritSect);
+ Assert(pThis->cUsers == 0);
+
+ if (!pThis->fRead && pThis->fIOPending)
+ rtPipeWriteCheckCompletion(pThis);
+
+ if (!fLeaveOpen && !pThis->fLeaveOpen)
+ CloseHandle(pThis->hPipe);
+ pThis->hPipe = INVALID_HANDLE_VALUE;
+
+ CloseHandle(pThis->Overlapped.hEvent);
+ pThis->Overlapped.hEvent = NULL;
+
+ RTMemFree(pThis->pbBounceBuf);
+ pThis->pbBounceBuf = NULL;
+
+ RTCritSectLeave(&pThis->CritSect);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeClose(RTPIPE hPipe)
+{
+ return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/);
+}
+
+
+RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER);
+ AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
+
+ /*
+ * Get and validate the pipe handle info.
+ */
+ HANDLE hNative = (HANDLE)hNativePipe;
+ AssertReturn(GetFileType(hNative) == FILE_TYPE_PIPE, VERR_INVALID_HANDLE);
+
+ DWORD cMaxInstances;
+ DWORD fInfo;
+ if (!GetNamedPipeInfo(hNative, &fInfo, NULL, NULL, &cMaxInstances))
+ return RTErrConvertFromWin32(GetLastError());
+ /* Doesn't seem to matter to much if the pipe is message or byte type. Cygwin
+ seems to hand us such pipes when capturing output (@bugref{9397}), so just
+ ignore skip this check:
+ AssertReturn(!(fInfo & PIPE_TYPE_MESSAGE), VERR_INVALID_HANDLE); */
+ AssertReturn(cMaxInstances == 1, VERR_INVALID_HANDLE);
+
+ DWORD cInstances;
+ DWORD fState;
+ if (!GetNamedPipeHandleState(hNative, &fState, &cInstances, NULL, NULL, NULL, 0))
+ return RTErrConvertFromWin32(GetLastError());
+ AssertReturn(!(fState & PIPE_NOWAIT), VERR_INVALID_HANDLE);
+ AssertReturn(!(fState & PIPE_READMODE_MESSAGE), VERR_INVALID_HANDLE);
+ AssertReturn(cInstances <= 1, VERR_INVALID_HANDLE);
+
+ /*
+ * Looks kind of OK, create a handle so we can try rtPipeQueryNtInfo on it
+ * and see if we need to duplicate it to make that call work.
+ */
+ RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ int rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
+ TRUE /*fInitialState*/, NULL /*pName*/);
+ if (pThis->Overlapped.hEvent != NULL)
+ {
+ pThis->u32Magic = RTPIPE_MAGIC;
+ pThis->hPipe = hNative;
+ pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ);
+ pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN);
+ //pThis->fIOPending = false;
+ //pThis->fZeroByteRead = false;
+ //pThis->fBrokenPipe = false;
+ //pThis->fPromisedWritable = false;
+ pThis->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_N_INHERIT);
+ //pThis->cUsers = 0;
+ //pThis->pbBounceBuf = NULL;
+ //pThis->cbBounceBufUsed = 0;
+ //pThis->cbBounceBufAlloc = 0;
+ pThis->hPollSet = NIL_RTPOLLSET;
+
+ HANDLE hNative2 = INVALID_HANDLE_VALUE;
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ RT_ZERO(Info);
+ if ( g_pfnSetHandleInformation
+ && rtPipeQueryNtInfo(pThis, &Info))
+ rc = VINF_SUCCESS;
+ else
+ {
+ if (DuplicateHandle(GetCurrentProcess() /*hSrcProcess*/, hNative /*hSrcHandle*/,
+ GetCurrentProcess() /*hDstProcess*/, &hNative2 /*phDstHandle*/,
+ pThis->fRead ? GENERIC_READ : GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
+ !!(fFlags & RTPIPE_N_INHERIT) /*fInheritHandle*/,
+ 0 /*dwOptions*/))
+ {
+ pThis->hPipe = hNative2;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ pThis->fLeaveOpen = false;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = VERR_ACCESS_DENIED;
+ CloseHandle(hNative2);
+ }
+ }
+ else
+ hNative2 = INVALID_HANDLE_VALUE;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Verify the pipe state and correct the inheritability.
+ */
+ AssertStmt( Info.NamedPipeState == FILE_PIPE_CONNECTED_STATE
+ || Info.NamedPipeState == FILE_PIPE_CLOSING_STATE
+ || Info.NamedPipeState == FILE_PIPE_DISCONNECTED_STATE,
+ VERR_INVALID_HANDLE);
+ AssertStmt( Info.NamedPipeConfiguration
+ == ( Info.NamedPipeEnd == FILE_PIPE_SERVER_END
+ ? (pThis->fRead ? FILE_PIPE_INBOUND : FILE_PIPE_OUTBOUND)
+ : (pThis->fRead ? FILE_PIPE_OUTBOUND : FILE_PIPE_INBOUND) )
+ || Info.NamedPipeConfiguration == FILE_PIPE_FULL_DUPLEX,
+ VERR_INVALID_HANDLE);
+ if ( RT_SUCCESS(rc)
+ && hNative2 == INVALID_HANDLE_VALUE
+ && !g_pfnSetHandleInformation(hNative,
+ HANDLE_FLAG_INHERIT /*dwMask*/,
+ fFlags & RTPIPE_N_INHERIT ? HANDLE_FLAG_INHERIT : 0))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("%Rrc\n", rc));
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Ok, we're good! If we replaced the handle, make sure it's not a standard
+ * handle if we think we need to close it.
+ */
+ if (hNative2 != INVALID_HANDLE_VALUE)
+ {
+ if ( !(fFlags & RTPIPE_N_LEAVE_OPEN)
+ && hNative != GetStdHandle(STD_INPUT_HANDLE)
+ && hNative != GetStdHandle(STD_OUTPUT_HANDLE)
+ && hNative != GetStdHandle(STD_ERROR_HANDLE) )
+ CloseHandle(hNative);
+ }
+ *phPipe = pThis;
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* Bail out. */
+ if (hNative2 != INVALID_HANDLE_VALUE)
+ CloseHandle(hNative2);
+ CloseHandle(pThis->Overlapped.hEvent);
+ }
+ RTCritSectDelete(&pThis->CritSect);
+ }
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
+
+ return (RTHCINTPTR)pThis->hPipe;
+}
+
+
+RTDECL(int) RTPipeGetCreationInheritability(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, false);
+
+ return pThis->fCreatedInheritable;
+}
+
+
+RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbRead);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent readers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ /*
+ * Kick off a an overlapped read. It should return immediately if
+ * there are bytes in the buffer. If not, we'll cancel it and see
+ * what we get back.
+ */
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbRead = 0;
+ if ( cbToRead == 0
+ || ReadFile(pThis->hPipe, pvBuf,
+ cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
+ &cbRead, &pThis->Overlapped))
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->fIOPending = true;
+ RTCritSectLeave(&pThis->CritSect);
+
+ /* We use NtCancelIoFile here because the CancelIo API
+ providing access to it wasn't available till NT4. This
+ code needs to work (or at least load) with NT 3.1 */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtCancelIoFile(pThis->hPipe, &Ios);
+ if (!NT_SUCCESS(rcNt))
+ WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
+
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_OPERATION_ABORTED)
+ {
+ *pcbRead = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fIOPending = false;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent readers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ size_t cbTotalRead = 0;
+ while (cbToRead > 0)
+ {
+ /*
+ * Kick of a an overlapped read. It should return immediately if
+ * there is bytes in the buffer. If not, we'll cancel it and see
+ * what we get back.
+ */
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbRead = 0;
+ pThis->fIOPending = true;
+ RTCritSectLeave(&pThis->CritSect);
+
+ if (ReadFile(pThis->hPipe, pvBuf,
+ cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
+ &cbRead, &pThis->Overlapped))
+ rc = VINF_SUCCESS;
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fIOPending = false;
+ if (RT_FAILURE(rc))
+ break;
+
+ /* advance */
+ cbToRead -= cbRead;
+ cbTotalRead += cbRead;
+ pvBuf = (uint8_t *)pvBuf + cbRead;
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ if (pcbRead)
+ {
+ *pcbRead = cbTotalRead;
+ if ( RT_FAILURE(rc)
+ && cbTotalRead
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbWritten);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent writers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ /* If I/O is pending, check if it has completed. */
+ if (pThis->fIOPending)
+ rc = rtPipeWriteCheckCompletion(pThis);
+ else
+ rc = VINF_SUCCESS;
+ if (rc == VINF_SUCCESS)
+ {
+ Assert(!pThis->fIOPending);
+
+ /* Adjust the number of bytes to write to fit into the current
+ buffer quota, unless we've promised stuff in RTPipeSelectOne.
+ WriteQuotaAvailable better not be zero when it shouldn't!! */
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ if ( !pThis->fPromisedWritable
+ && cbToWrite > 0
+ && rtPipeQueryNtInfo(pThis, &Info))
+ {
+ if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
+ rc = VERR_BROKEN_PIPE;
+ /** @todo fixme: To get the pipe writing support to work the
+ * block below needs to be commented out until a
+ * way is found to address the problem of the incorrectly
+ * set field Info.WriteQuotaAvailable.
+ * Update: We now just write up to RTPIPE_NT_SIZE more. This is quite
+ * possibely what lead to the misunderstanding here wrt to
+ * WriteQuotaAvailable updating. */
+#if 0
+ else if ( cbToWrite >= Info.WriteQuotaAvailable
+ && Info.OutboundQuota != 0
+ && (Info.WriteQuotaAvailable || pThis->cbBounceBufAlloc)
+ )
+ {
+ cbToWrite = Info.WriteQuotaAvailable;
+ if (!cbToWrite)
+ rc = VINF_TRY_AGAIN;
+ }
+#endif
+ }
+ pThis->fPromisedWritable = false;
+
+ /* Do the bounce buffering. */
+ if ( pThis->cbBounceBufAlloc < cbToWrite
+ && pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE)
+ {
+ if (cbToWrite > RTPIPE_NT_SIZE)
+ cbToWrite = RTPIPE_NT_SIZE;
+ void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
+ if (pv)
+ {
+ pThis->pbBounceBuf = (uint8_t *)pv;
+ pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else if (cbToWrite > RTPIPE_NT_SIZE)
+ cbToWrite = RTPIPE_NT_SIZE;
+ if (RT_SUCCESS(rc) && cbToWrite)
+ {
+ memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
+ pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
+
+ /* Submit the write. */
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbWritten = 0;
+ if (WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
+ &cbWritten, &pThis->Overlapped))
+ {
+ *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ *pcbWritten = cbToWrite;
+ pThis->fIOPending = true;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (RT_SUCCESS(rc))
+ *pcbWritten = 0;
+ }
+ else if (RT_SUCCESS(rc))
+ *pcbWritten = 0;
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+ AssertPtrNull(pcbWritten);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent writers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ /*
+ * If I/O is pending, wait for it to complete.
+ */
+ if (pThis->fIOPending)
+ {
+ rc = rtPipeWriteCheckCompletion(pThis);
+ while (rc == VINF_TRY_AGAIN)
+ {
+ Assert(pThis->fIOPending);
+ HANDLE hEvent = pThis->Overlapped.hEvent;
+ RTCritSectLeave(&pThis->CritSect);
+ WaitForSingleObject(hEvent, INFINITE);
+ RTCritSectEnter(&pThis->CritSect);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ Assert(!pThis->fIOPending);
+ pThis->fPromisedWritable = false;
+
+ /*
+ * Try write everything.
+ * No bounce buffering, cUsers protects us.
+ */
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ pThis->fIOPending = true;
+ RTCritSectLeave(&pThis->CritSect);
+
+ DWORD cbWritten = 0;
+ DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
+ if (WriteFile(pThis->hPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Overlapped))
+ rc = VINF_SUCCESS;
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE /*fWait*/))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (GetLastError() == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fIOPending = false;
+ if (RT_FAILURE(rc))
+ break;
+
+ /* advance */
+ if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
+ cbWritten = cbToWriteInThisIteration;
+ pvBuf = (char const *)pvBuf + cbWritten;
+ cbTotalWritten += cbWritten;
+ cbToWrite -= cbWritten;
+ }
+
+ if (pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ if ( RT_FAILURE(rc)
+ && cbTotalWritten
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+
+#if 0 /** @todo r=bird: What's this? */
+ int rc = rtPipeTryBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
+ if (cbWritten < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+
+ /* advance */
+ pvBuf = (char const *)pvBuf + cbWritten;
+ cbTotalWritten += cbWritten;
+ cbToWrite -= cbWritten;
+ }
+
+ if (pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ if ( RT_FAILURE(rc)
+ && cbTotalWritten
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+
+ ASMAtomicDecU32(&pThis->u32State);
+ }
+ return rc;
+#endif
+}
+
+
+RTDECL(int) RTPipeFlush(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+
+ if (!FlushFileBuffers(pThis->hPipe))
+ {
+ int rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ uint64_t const StartMsTS = RTTimeMilliTS();
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+ for (unsigned iLoop = 0;; iLoop++)
+ {
+ HANDLE hWait = INVALID_HANDLE_VALUE;
+ if (pThis->fRead)
+ {
+ if (pThis->fIOPending)
+ hWait = pThis->Overlapped.hEvent;
+ else
+ {
+ /* Peek at the pipe buffer and see how many bytes it contains. */
+ DWORD cbAvailable;
+ if ( PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)
+ && cbAvailable > 0)
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* Start a zero byte read operation that we can wait on. */
+ if (cMillies == 0)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ AssertBreakStmt(pThis->cUsers == 0, rc = VERR_INTERNAL_ERROR_5);
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbRead = 0;
+ if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
+ {
+ rc = VINF_SUCCESS;
+ if (iLoop > 10)
+ RTThreadYield();
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->cUsers++;
+ pThis->fIOPending = true;
+ pThis->fZeroByteRead = true;
+ hWait = pThis->Overlapped.hEvent;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ {
+ if (pThis->fIOPending)
+ {
+ rc = rtPipeWriteCheckCompletion(pThis);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ if (pThis->fIOPending)
+ hWait = pThis->Overlapped.hEvent;
+ else
+ {
+ FILE_PIPE_LOCAL_INFORMATION Info;
+#if 1
+ /* We can always write one bounce buffer full of data regardless of
+ the pipe buffer state. We must of course take this into account,
+ or code like "Full write buffer" test in tstRTPipe gets confused. */
+ rc = VINF_SUCCESS;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ /* Check for broken pipe. */
+ if (Info.NamedPipeState != FILE_PIPE_CLOSING_STATE)
+ pThis->fPromisedWritable = true;
+ else
+ rc = VERR_BROKEN_PIPE;
+ }
+ else
+ pThis->fPromisedWritable = true;
+ break;
+
+#else /* old code: */
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ /* Check for broken pipe. */
+ if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
+ {
+ rc = VERR_BROKEN_PIPE;
+ break;
+ }
+
+ /* Check for available write buffer space. */
+ if (Info.WriteQuotaAvailable > 0)
+ {
+ pThis->fPromisedWritable = false;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* delayed buffer alloc or timeout: phony promise
+ later: See if we still can associate a semaphore with
+ the pipe, like on OS/2. */
+ if (Info.OutboundQuota == 0 || cMillies)
+ {
+ pThis->fPromisedWritable = true;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ else
+ {
+ pThis->fPromisedWritable = true;
+ rc = VINF_SUCCESS;
+ break;
+ }
+#endif
+ }
+ }
+ if (RT_FAILURE(rc))
+ break;
+
+ /*
+ * Check for timeout.
+ */
+ DWORD cMsMaxWait = INFINITE;
+ if ( cMillies != RT_INDEFINITE_WAIT
+ && ( hWait != INVALID_HANDLE_VALUE
+ || iLoop > 10)
+ )
+ {
+ uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
+ if (cElapsed >= cMillies)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ cMsMaxWait = cMillies - (uint32_t)cElapsed;
+ }
+
+ /*
+ * Wait.
+ */
+ if (hWait != INVALID_HANDLE_VALUE)
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ DWORD dwRc = WaitForSingleObject(hWait, cMsMaxWait);
+ if (dwRc == WAIT_OBJECT_0)
+ rc = VINF_SUCCESS;
+ else if (dwRc == WAIT_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else if (dwRc == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if ( RT_FAILURE(rc)
+ && pThis->u32Magic != RTPIPE_MAGIC)
+ return rc;
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (pThis->fZeroByteRead)
+ {
+ pThis->cUsers--;
+ pThis->fIOPending = false;
+ if (rc != VINF_SUCCESS)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NtCancelIoFile(pThis->hPipe, &Ios);
+ }
+ DWORD cbRead = 0;
+ GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/);
+ }
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ RTCritSectLeave(&pThis->CritSect);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
+ AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /** @todo The file size should give the same info and be slightly faster... */
+ DWORD cbAvailable = 0;
+ if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
+ {
+#if ARCH_BITS == 32
+ /*
+ * Kludge!
+ *
+ * Prior to XP SP1 (?), the returned cbAvailable value was not adjusted
+ * by the read position in the current message/buffer, so it could
+ * potentially be too high. This may cause the caller to try read more
+ * data than what's actually available, which may cause the read to
+ * block when the caller thought it wouldn't.
+ *
+ * To get an accurate readable size, we have to provide an output
+ * buffer and see how much we actually get back in it, as the data
+ * peeking works correctly (as you would expect).
+ */
+ if (cbAvailable == 0 || g_enmWinVer >= kRTWinOSType_XP64)
+ { /* No data available or kernel shouldn't be affected. */ }
+ else
+ {
+ for (unsigned i = 0; ; i++)
+ {
+ uint8_t abBufStack[_16K];
+ void *pvBufFree = NULL;
+ void *pvBuf;
+ DWORD cbBuf = RT_ALIGN_32(cbAvailable + i * 256, 64);
+ if (cbBuf <= sizeof(abBufStack))
+ {
+ pvBuf = abBufStack;
+ /* No cbBuf = sizeof(abBufStack) here! PeekNamedPipe bounce buffers the request on the heap. */
+ }
+ else
+ {
+ pvBufFree = pvBuf = RTMemTmpAlloc(cbBuf);
+ if (!pvBuf)
+ {
+ rc = VERR_NO_TMP_MEMORY;
+ cbAvailable = 1;
+ break;
+ }
+ }
+
+ DWORD cbAvailable2 = 0;
+ DWORD cbRead = 0;
+ BOOL fRc = PeekNamedPipe(pThis->hPipe, pvBuf, cbBuf, &cbRead, &cbAvailable2, NULL);
+ Log(("RTPipeQueryReadable: #%u: cbAvailable=%#x cbRead=%#x cbAvailable2=%#x (cbBuf=%#x)\n",
+ i, cbAvailable, cbRead, cbAvailable2, cbBuf));
+
+ RTMemTmpFree(pvBufFree);
+
+ if (fRc)
+ {
+ if (cbAvailable2 <= cbBuf || i >= 10)
+ cbAvailable = cbRead;
+ else
+ {
+ cbAvailable = cbAvailable2;
+ continue;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ cbAvailable = 1;
+ }
+ break;
+ }
+ }
+#endif
+ *pcbReadable = cbAvailable;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectLeave(&pThis->CritSect);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, 0);
+
+ rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
+
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ pObjInfo->cbAllocated = pThis->fRead ? Info.InboundQuota : Info.OutboundQuota;
+ pObjInfo->cbObject = pThis->fRead ? Info.ReadDataAvailable : Info.WriteQuotaAvailable;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+
+int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
+
+ /* Later: Try register an event handle with the pipe like on OS/2, there is
+ a file control for doing this obviously intended for the OS/2 subsys.
+ The question is whether this still exists on Vista and W7. */
+ *phNative = (RTHCINTPTR)pThis->Overlapped.hEvent;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks for pending events.
+ *
+ * @returns Event mask or 0.
+ * @param pThis The pipe handle.
+ * @param fEvents The desired events.
+ */
+static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents)
+{
+ uint32_t fRetEvents = 0;
+ if (pThis->fBrokenPipe)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ else if (pThis->fRead)
+ {
+ if (!pThis->fIOPending)
+ {
+ DWORD cbAvailable;
+ if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
+ {
+ if ( (fEvents & RTPOLL_EVT_READ)
+ && cbAvailable > 0)
+ fRetEvents |= RTPOLL_EVT_READ;
+ }
+ else
+ {
+ if (GetLastError() == ERROR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ }
+ }
+ }
+ else
+ {
+ if (pThis->fIOPending)
+ {
+ rtPipeWriteCheckCompletion(pThis);
+ if (pThis->fBrokenPipe)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ }
+ if ( !pThis->fIOPending
+ && !fRetEvents)
+ {
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ /* Check for broken pipe. */
+ if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
+ {
+ fRetEvents = RTPOLL_EVT_ERROR;
+ pThis->fBrokenPipe = true;
+ }
+
+ /* Check if there is available buffer space. */
+ if ( !fRetEvents
+ && (fEvents & RTPOLL_EVT_WRITE)
+ && ( Info.WriteQuotaAvailable > 0
+ || Info.OutboundQuota == 0)
+ )
+ fRetEvents |= RTPOLL_EVT_WRITE;
+ }
+ else if (fEvents & RTPOLL_EVT_WRITE)
+ fRetEvents |= RTPOLL_EVT_WRITE;
+ }
+ }
+
+ return fRetEvents;
+}
+
+
+/**
+ * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
+ * clear, starts whatever actions we've got running during the poll call.
+ *
+ * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
+ * Event mask (in @a fEvents) and no actions if the handle is ready
+ * already.
+ * UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
+ * different poll set.
+ *
+ * @param hPipe The pipe handle.
+ * @param hPollSet The poll set handle (for access checks).
+ * @param fEvents The events we're polling for.
+ * @param fFinalEntry Set if this is the final entry for this handle
+ * in this poll set. This can be used for dealing
+ * with duplicate entries.
+ * @param fNoWait Set if it's a zero-wait poll call. Clear if
+ * we'll wait for an event to occur.
+ */
+uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
+{
+ /** @todo All this polling code could be optimized to make fewer system
+ * calls; like for instance the ResetEvent calls. */
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
+ RT_NOREF_PV(fFinalEntry);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, UINT32_MAX);
+
+ /* Check that this is the only current use of this pipe. */
+ uint32_t fRetEvents;
+ if ( pThis->cUsers == 0
+ || pThis->hPollSet == hPollSet)
+ {
+ /* Check what the current events are. */
+ fRetEvents = rtPipePollCheck(pThis, fEvents);
+ if ( !fRetEvents
+ && !fNoWait)
+ {
+ /* Make sure the event semaphore has been reset. */
+ if (!pThis->fIOPending)
+ {
+ rc = ResetEvent(pThis->Overlapped.hEvent);
+ Assert(rc == TRUE);
+ }
+
+ /* Kick off the zero byte read thing if applicable. */
+ if ( !pThis->fIOPending
+ && pThis->fRead
+ && (fEvents & RTPOLL_EVT_READ)
+ )
+ {
+ DWORD cbRead = 0;
+ if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
+ fRetEvents = rtPipePollCheck(pThis, fEvents);
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->fIOPending = true;
+ pThis->fZeroByteRead = true;
+ }
+ else
+ fRetEvents = RTPOLL_EVT_ERROR;
+ }
+
+ /* If we're still set for the waiting, record the poll set and
+ mark the pipe used. */
+ if (!fRetEvents)
+ {
+ pThis->cUsers++;
+ pThis->hPollSet = hPollSet;
+ }
+ }
+ }
+ else
+ {
+ AssertFailed();
+ fRetEvents = UINT32_MAX;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ return fRetEvents;
+}
+
+
+/**
+ * Called after a WaitForMultipleObjects returned in order to check for pending
+ * events and stop whatever actions that rtPipePollStart() initiated.
+ *
+ * @returns Event mask or 0.
+ *
+ * @param hPipe The pipe handle.
+ * @param fEvents The events we're polling for.
+ * @param fFinalEntry Set if this is the final entry for this handle
+ * in this poll set. This can be used for dealing
+ * with duplicate entries. Only keep in mind that
+ * this method is called in reverse order, so the
+ * first call will have this set (when the entire
+ * set was processed).
+ * @param fHarvestEvents Set if we should check for pending events.
+ */
+uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+ RT_NOREF_PV(fFinalEntry);
+ RT_NOREF_PV(fHarvestEvents);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, 0);
+
+ Assert(pThis->cUsers > 0);
+
+
+ /* Cancel the zero byte read. */
+ uint32_t fRetEvents = 0;
+ if (pThis->fZeroByteRead)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NtCancelIoFile(pThis->hPipe, &Ios);
+
+ DWORD cbRead = 0;
+ if ( !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)
+ && GetLastError() != ERROR_OPERATION_ABORTED)
+ fRetEvents = RTPOLL_EVT_ERROR;
+
+ pThis->fIOPending = false;
+ pThis->fZeroByteRead = false;
+ }
+
+ /* harvest events. */
+ fRetEvents |= rtPipePollCheck(pThis, fEvents);
+
+ /* update counters. */
+ pThis->cUsers--;
+ /** @todo This isn't sane, or is it? See OS/2 impl. */
+ if (!pThis->cUsers)
+ pThis->hPollSet = NIL_RTPOLLSET;
+
+ RTCritSectLeave(&pThis->CritSect);
+ return fRetEvents;
+}
+
diff --git a/src/VBox/Runtime/r3/win/process-win.cpp b/src/VBox/Runtime/r3/win/process-win.cpp
new file mode 100644
index 00000000..18ed9ab0
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/process-win.cpp
@@ -0,0 +1,2750 @@
+/* $Id: process-win.cpp $ */
+/** @file
+ * IPRT - Process, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <iprt/asm.h> /* hack */
+
+#include <iprt/nt/nt-and-windows.h>
+#include <Userenv.h>
+#include <tlhelp32.h>
+#ifndef IPRT_NO_CRT
+# include <process.h>
+# include <errno.h>
+# include <Strsafe.h>
+#endif
+#include <LsaLookup.h>
+#include <Lmcons.h>
+
+#define _NTDEF_ /* Prevents redefining (P)UNICODE_STRING. */
+#include <Ntsecapi.h>
+
+#include <iprt/process.h>
+#include "internal-r3-win.h"
+
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/env.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/string.h>
+#include <iprt/socket.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/* kernel32.dll: */
+//typedef DWORD (WINAPI *PFNWTSGETACTIVECONSOLESESSIONID)(VOID);
+typedef HANDLE (WINAPI *PFNCREATETOOLHELP32SNAPSHOT)(DWORD, DWORD);
+typedef BOOL (WINAPI *PFNPROCESS32FIRSTW)(HANDLE, LPPROCESSENTRY32W);
+typedef BOOL (WINAPI *PFNPROCESS32NEXTW)(HANDLE, LPPROCESSENTRY32W);
+
+/* psapi.dll: */
+typedef BOOL (WINAPI *PFNENUMPROCESSES)(LPDWORD, DWORD, LPDWORD);
+typedef DWORD (WINAPI *PFNGETMODULEBASENAMEW)(HANDLE, HMODULE, LPWSTR, DWORD);
+
+/* advapi32.dll: */
+typedef BOOL (WINAPI *PFNCREATEPROCESSWITHLOGON)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPCWSTR, LPWSTR, DWORD,
+ LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
+typedef NTSTATUS (NTAPI *PFNLSALOOKUPNAMES2)(LSA_HANDLE, ULONG, ULONG, PLSA_UNICODE_STRING,
+ PLSA_REFERENCED_DOMAIN_LIST*, PLSA_TRANSLATED_SID2*);
+
+/* userenv.dll: */
+typedef BOOL (WINAPI *PFNCREATEENVIRONMENTBLOCK)(LPVOID *, HANDLE, BOOL);
+typedef BOOL (WINAPI *PFNPFNDESTROYENVIRONMENTBLOCK)(LPVOID);
+typedef BOOL (WINAPI *PFNLOADUSERPROFILEW)(HANDLE, LPPROFILEINFOW);
+typedef BOOL (WINAPI *PFNUNLOADUSERPROFILE)(HANDLE, HANDLE);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Init once structure. */
+static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER;
+/** Critical section protecting the process array. */
+static RTCRITSECT g_CritSect;
+/** The number of processes in the array. */
+static uint32_t g_cProcesses;
+/** The current allocation size. */
+static uint32_t g_cProcessesAlloc;
+/** Array containing the live or non-reaped child processes. */
+static struct RTPROCWINENTRY
+{
+ /** The process ID. */
+ ULONG_PTR pid;
+ /** The process handle. */
+ HANDLE hProcess;
+} *g_paProcesses;
+
+/** Structure for storing a user's account info.
+ * Must be free'd with rtProcWinFreeAccountInfo(). */
+typedef struct RTPROCWINACCOUNTINFO
+{
+ /** User name. */
+ PRTUTF16 pwszUserName;
+ /** Domain this account is tied to. Can be NULL if no domain is being used. */
+ PRTUTF16 pwszDomain;
+} RTPROCWINACCOUNTINFO, *PRTPROCWINACCOUNTINFO;
+
+/** @name userenv.dll imports (we don't unload it).
+ * They're all optional. So in addition to using g_rtProcWinResolveOnce, the
+ * caller must also check if any of the necessary APIs are NULL pointers.
+ * @{ */
+/** Init once structure for run-as-user functions we need. */
+static RTONCE g_rtProcWinResolveOnce = RTONCE_INITIALIZER;
+/* kernel32.dll: */
+static PFNCREATETOOLHELP32SNAPSHOT g_pfnCreateToolhelp32Snapshot = NULL;
+static PFNPROCESS32FIRSTW g_pfnProcess32FirstW = NULL;
+static PFNPROCESS32NEXTW g_pfnProcess32NextW = NULL;
+/* psapi.dll: */
+static PFNGETMODULEBASENAMEW g_pfnGetModuleBaseNameW = NULL;
+static PFNENUMPROCESSES g_pfnEnumProcesses = NULL;
+/* advapi32.dll: */
+static PFNCREATEPROCESSWITHLOGON g_pfnCreateProcessWithLogonW = NULL;
+static decltype(LogonUserW) *g_pfnLogonUserW = NULL;
+static decltype(CreateProcessAsUserW) *g_pfnCreateProcessAsUserW = NULL;
+/* user32.dll: */
+static decltype(OpenWindowStationW) *g_pfnOpenWindowStationW = NULL;
+static decltype(CloseWindowStation) *g_pfnCloseWindowStation = NULL;
+/* userenv.dll: */
+static PFNCREATEENVIRONMENTBLOCK g_pfnCreateEnvironmentBlock = NULL;
+static PFNPFNDESTROYENVIRONMENTBLOCK g_pfnDestroyEnvironmentBlock = NULL;
+static PFNLOADUSERPROFILEW g_pfnLoadUserProfileW = NULL;
+static PFNUNLOADUSERPROFILE g_pfnUnloadUserProfile = NULL;
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int rtProcWinFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, PRTUTF16 *ppwszExec);
+static int rtProcWinCreateEnvBlockAndFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec,
+ PRTUTF16 *ppwszzBlock, PRTUTF16 *ppwszExec);
+
+
+/**
+ * Clean up the globals.
+ *
+ * @param enmReason Ignored.
+ * @param iStatus Ignored.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
+{
+ NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
+
+ RTCritSectDelete(&g_CritSect);
+
+ size_t i = g_cProcesses;
+ while (i-- > 0)
+ {
+ CloseHandle(g_paProcesses[i].hProcess);
+ g_paProcesses[i].hProcess = NULL;
+ }
+ RTMemFree(g_paProcesses);
+
+ g_paProcesses = NULL;
+ g_cProcesses = 0;
+ g_cProcessesAlloc = 0;
+}
+
+
+/**
+ * Initialize the globals.
+ *
+ * @returns IPRT status code.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser)
+{
+ NOREF(pvUser);
+
+ g_cProcesses = 0;
+ g_cProcessesAlloc = 0;
+ g_paProcesses = NULL;
+ int rc = RTCritSectInit(&g_CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo init once, terminate once - this is a generic thing which should
+ * have some kind of static and simpler setup! */
+ rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
+ if (RT_SUCCESS(rc))
+ return rc;
+ RTCritSectDelete(&g_CritSect);
+ }
+ return rc;
+}
+
+
+/**
+ * Gets the process handle for a process from g_paProcesses.
+ *
+ * @returns Process handle if found, NULL if not.
+ * @param pid The process to remove (pid).
+ */
+static HANDLE rtProcWinFindPid(RTPROCESS pid)
+{
+ HANDLE hProcess = NULL;
+
+ RTCritSectEnter(&g_CritSect);
+ uint32_t i = g_cProcesses;
+ while (i-- > 0)
+ if (g_paProcesses[i].pid == pid)
+ {
+ hProcess = g_paProcesses[i].hProcess;
+ break;
+ }
+ RTCritSectLeave(&g_CritSect);
+
+ return hProcess;
+}
+
+
+/**
+ * Removes a process from g_paProcesses and closes the process handle.
+ *
+ * @param pid The process to remove (pid).
+ */
+static void rtProcWinRemovePid(RTPROCESS pid)
+{
+ RTCritSectEnter(&g_CritSect);
+ uint32_t i = g_cProcesses;
+ while (i-- > 0)
+ if (g_paProcesses[i].pid == pid)
+ {
+ HANDLE hProcess = g_paProcesses[i].hProcess;
+
+ g_cProcesses--;
+ uint32_t cToMove = g_cProcesses - i;
+ if (cToMove)
+ memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
+
+ RTCritSectLeave(&g_CritSect);
+ CloseHandle(hProcess);
+ return;
+ }
+ RTCritSectLeave(&g_CritSect);
+}
+
+
+/**
+ * Adds a process to g_paProcesses.
+ *
+ * @returns IPRT status code.
+ * @param pid The process id.
+ * @param hProcess The process handle.
+ */
+static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
+{
+ RTCritSectEnter(&g_CritSect);
+
+ uint32_t i = g_cProcesses;
+ if (i >= g_cProcessesAlloc)
+ {
+ void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
+ if (RT_UNLIKELY(!pvNew))
+ {
+ RTCritSectLeave(&g_CritSect);
+ return VERR_NO_MEMORY;
+ }
+ g_paProcesses = (struct RTPROCWINENTRY *)pvNew;
+ g_cProcessesAlloc = i + 16;
+ }
+
+ g_paProcesses[i].pid = pid;
+ g_paProcesses[i].hProcess = hProcess;
+ g_cProcesses = i + 1;
+
+ RTCritSectLeave(&g_CritSect);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initialize the import APIs for run-as-user and special environment support.
+ *
+ * @returns IPRT status code.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int) rtProcWinResolveOnce(void *pvUser)
+{
+ int rc;
+ RTLDRMOD hMod;
+ RT_NOREF_PV(pvUser);
+
+ /*
+ * kernel32.dll APIs introduced after NT4.
+ */
+ g_pfnCreateToolhelp32Snapshot = (PFNCREATETOOLHELP32SNAPSHOT)GetProcAddress(g_hModKernel32, "CreateToolhelp32Snapshot");
+ g_pfnProcess32FirstW = (PFNPROCESS32FIRSTW )GetProcAddress(g_hModKernel32, "Process32FirstW");
+ g_pfnProcess32NextW = (PFNPROCESS32NEXTW )GetProcAddress(g_hModKernel32, "Process32NextW");
+
+ /*
+ * psapi.dll APIs, if none of the above are available.
+ */
+ if ( !g_pfnCreateToolhelp32Snapshot
+ || !g_pfnProcess32FirstW
+ || !g_pfnProcess32NextW)
+ {
+ Assert(!g_pfnCreateToolhelp32Snapshot && !g_pfnProcess32FirstW && !g_pfnProcess32NextW);
+
+ rc = RTLdrLoadSystem("psapi.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "GetModuleBaseNameW", (void **)&g_pfnGetModuleBaseNameW);
+ AssertStmt(RT_SUCCESS(rc), g_pfnGetModuleBaseNameW = NULL);
+
+ rc = RTLdrGetSymbol(hMod, "EnumProcesses", (void **)&g_pfnEnumProcesses);
+ AssertStmt(RT_SUCCESS(rc), g_pfnEnumProcesses = NULL);
+
+ RTLdrClose(hMod);
+ }
+ }
+
+ /*
+ * advapi32.dll APIs.
+ */
+ rc = RTLdrLoadSystem("advapi32.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "CreateProcessWithLogonW", (void **)&g_pfnCreateProcessWithLogonW);
+ if (RT_FAILURE(rc)) { g_pfnCreateProcessWithLogonW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); }
+
+ rc = RTLdrGetSymbol(hMod, "LogonUserW", (void **)&g_pfnLogonUserW);
+ if (RT_FAILURE(rc)) { g_pfnLogonUserW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT350); }
+
+ rc = RTLdrGetSymbol(hMod, "CreateProcessAsUserW", (void **)&g_pfnCreateProcessAsUserW);
+ if (RT_FAILURE(rc)) { g_pfnCreateProcessAsUserW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT350); }
+
+ RTLdrClose(hMod);
+ }
+
+ /*
+ * user32.dll APIs.
+ */
+ rc = RTLdrLoadSystem("user32.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "OpenWindowStationW", (void **)&g_pfnOpenWindowStationW);
+ if (RT_FAILURE(rc)) { g_pfnOpenWindowStationW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT310); }
+
+ rc = RTLdrGetSymbol(hMod, "CloseWindowStation", (void **)&g_pfnCloseWindowStation);
+ if (RT_FAILURE(rc)) { g_pfnCloseWindowStation = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT310); }
+
+ RTLdrClose(hMod);
+ }
+
+ /*
+ * userenv.dll APIs.
+ */
+ rc = RTLdrLoadSystem("userenv.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "LoadUserProfileW", (void **)&g_pfnLoadUserProfileW);
+ if (RT_FAILURE(rc)) { g_pfnLoadUserProfileW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); }
+
+ rc = RTLdrGetSymbol(hMod, "UnloadUserProfile", (void **)&g_pfnUnloadUserProfile);
+ if (RT_FAILURE(rc)) { g_pfnUnloadUserProfile = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); }
+
+ rc = RTLdrGetSymbol(hMod, "CreateEnvironmentBlock", (void **)&g_pfnCreateEnvironmentBlock);
+ if (RT_FAILURE(rc)) { g_pfnCreateEnvironmentBlock = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); }
+
+ rc = RTLdrGetSymbol(hMod, "DestroyEnvironmentBlock", (void **)&g_pfnDestroyEnvironmentBlock);
+ if (RT_FAILURE(rc)) { g_pfnDestroyEnvironmentBlock = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); }
+
+ RTLdrClose(hMod);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
+{
+ return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
+ NULL, NULL, NULL, /* standard handles */
+ NULL /*pszAsUser*/, NULL /* pszPassword*/,
+ NULL /*pvExtraData*/, pProcess);
+}
+
+
+/**
+ * The following NT call is for v3.51 and does the equivalent of:
+ * DuplicateTokenEx(hSrcToken, MAXIMUM_ALLOWED, NULL,
+ * SecurityIdentification, TokenPrimary, phToken);
+ */
+static int rtProcWinDuplicateToken(HANDLE hSrcToken, PHANDLE phToken)
+{
+ int rc;
+ if (g_pfnNtDuplicateToken)
+ {
+ SECURITY_QUALITY_OF_SERVICE SecQoS;
+ SecQoS.Length = sizeof(SecQoS);
+ SecQoS.ImpersonationLevel = SecurityIdentification;
+ SecQoS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecQoS.EffectiveOnly = FALSE;
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, NULL /*Name*/, 0 /*OBJ_XXX*/, NULL /*Root*/, NULL /*SecDesc*/);
+ ObjAttr.SecurityQualityOfService = &SecQoS;
+
+ NTSTATUS rcNt = g_pfnNtDuplicateToken(hSrcToken, MAXIMUM_ALLOWED, &ObjAttr, FALSE, TokenPrimary, phToken);
+ if (NT_SUCCESS(rcNt))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = VERR_SYMBOL_NOT_FOUND; /** @todo do we really need to duplicate the token? */
+ return rc;
+}
+
+
+/**
+ * Get the token assigned to the thread indicated by @a hThread.
+ *
+ * Only used when RTPROC_FLAGS_AS_IMPERSONATED_TOKEN is in effect and the
+ * purpose is to get a duplicate the impersonated token of the current thread.
+ *
+ * @returns IPRT status code.
+ * @param hThread The thread handle (current thread).
+ * @param phToken Where to return the a duplicate of the thread token
+ * handle on success. (The caller closes it.)
+ */
+static int rtProcWinGetThreadTokenHandle(HANDLE hThread, PHANDLE phToken)
+{
+ AssertPtr(phToken);
+
+ int rc;
+ HANDLE hTokenThread;
+ if (OpenThreadToken(hThread,
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE
+ | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
+ TRUE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
+ &hTokenThread))
+ {
+ rc = rtProcWinDuplicateToken(hTokenThread, phToken);
+ CloseHandle(hTokenThread);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ return rc;
+}
+
+
+/**
+ * Get the token assigned the process indicated by @a hProcess.
+ *
+ * Only used when pwszUser is NULL and RTPROC_FLAGS_AS_IMPERSONATED_TOKEN isn't
+ * set.
+ *
+ * @returns IPRT status code.
+ * @param hProcess The process handle (current process).
+ * @param phToken Where to return the a duplicate of the thread token
+ * handle on success. (The caller closes it.)
+ */
+static int rtProcWinGetProcessTokenHandle(HANDLE hProcess, PHANDLE phToken)
+{
+ AssertPtr(phToken);
+
+ int rc;
+ HANDLE hTokenProcess;
+ if (OpenProcessToken(hProcess,
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE
+ | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
+ &hTokenProcess))
+ {
+ rc = rtProcWinDuplicateToken(hTokenProcess, phToken); /* not sure if this is strictly necessary */
+ CloseHandle(hTokenProcess);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ return rc;
+}
+
+
+/**
+ * Get the process token of the process indicated by @a dwPID if the @a pSid and
+ * @a idSessionDesired matches.
+ *
+ * @returns IPRT status code.
+ * @param dwPid The process identifier.
+ * @param pSid The secure identifier of the user.
+ * @param idDesiredSession The session the process candidate should
+ * preferably belong to, UINT32_MAX if anything
+ * goes.
+ * @param phToken Where to return the a duplicate of the process token
+ * handle on success. (The caller closes it.)
+ */
+static int rtProcWinGetProcessTokenHandle(DWORD dwPid, PSID pSid, DWORD idDesiredSession, PHANDLE phToken)
+{
+ AssertPtr(pSid);
+ AssertPtr(phToken);
+
+ int rc;
+ HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, dwPid);
+ if (hProc != NULL)
+ {
+ HANDLE hTokenProc;
+ if (OpenProcessToken(hProc,
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE
+ | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
+ &hTokenProc))
+ {
+ /*
+ * Query the user SID from the token.
+ */
+ SetLastError(NO_ERROR);
+ DWORD dwSize = 0;
+ BOOL fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize);
+ DWORD dwErr = GetLastError();
+ if ( !fRc
+ && dwErr == ERROR_INSUFFICIENT_BUFFER
+ && dwSize > 0)
+ {
+ PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemTmpAllocZ(dwSize);
+ if (pTokenUser)
+ {
+ if (GetTokenInformation(hTokenProc, TokenUser, pTokenUser, dwSize, &dwSize))
+ {
+ /*
+ * Match token user with the user we're want to create a process as.
+ */
+ if ( IsValidSid(pTokenUser->User.Sid)
+ && EqualSid(pTokenUser->User.Sid, pSid))
+ {
+ /*
+ * Do we need to match the session ID?
+ */
+ rc = VINF_SUCCESS;
+ if (idDesiredSession != UINT32_MAX)
+ {
+ DWORD idCurSession = UINT32_MAX;
+ if (GetTokenInformation(hTokenProc, TokenSessionId, &idCurSession, sizeof(DWORD), &dwSize))
+ rc = idDesiredSession == idCurSession ? VINF_SUCCESS : VERR_NOT_FOUND;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Got a match. Duplicate the token. This duplicated token will
+ * be used for the actual CreateProcessAsUserW() call then.
+ */
+ rc = rtProcWinDuplicateToken(hTokenProc, phToken);
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTMemTmpFree(pTokenUser);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else if (fRc || dwErr == NO_ERROR)
+ rc = VERR_IPE_UNEXPECTED_STATUS;
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ CloseHandle(hTokenProc);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ CloseHandle(hProc);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ return rc;
+}
+
+
+/**
+ * Fallback method for rtProcWinFindTokenByProcess that uses the older NT4
+ * PSAPI.DLL API.
+ *
+ * @returns Success indicator.
+ * @param papszNames The process candidates, in prioritized order.
+ * @param pSid The secure identifier of the user.
+ * @param phToken Where to return the token handle - duplicate,
+ * caller closes it on success.
+ *
+ * @remarks NT4 needs a copy of "PSAPI.dll" (redistributed by Microsoft and not
+ * part of the OS) in order to get a lookup. If we don't have this DLL
+ * we are not able to get a token and therefore no UI will be visible.
+ */
+static bool rtProcWinFindTokenByProcessAndPsApi(const char * const *papszNames, PSID pSid, PHANDLE phToken)
+{
+ /*
+ * Load PSAPI.DLL and resolve the two symbols we need.
+ */
+ if ( !g_pfnGetModuleBaseNameW
+ || !g_pfnEnumProcesses)
+ return false;
+
+ /*
+ * Get a list of PID. We retry if it looks like there are more PIDs
+ * to be returned than what we supplied buffer space for.
+ */
+ bool fFound = false;
+ int rc = VINF_SUCCESS;
+ DWORD cbPidsAllocated = 4096;
+ DWORD cbPidsReturned = 0; /* (MSC maybe used uninitialized) */
+ DWORD *paPids;
+ for (;;)
+ {
+ paPids = (DWORD *)RTMemTmpAlloc(cbPidsAllocated);
+ AssertBreakStmt(paPids, rc = VERR_NO_TMP_MEMORY);
+ cbPidsReturned = 0;
+ if (!g_pfnEnumProcesses(paPids, cbPidsAllocated, &cbPidsReturned))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailedBreak(("%Rrc\n", rc));
+ }
+ if ( cbPidsReturned < cbPidsAllocated
+ || cbPidsAllocated >= _512K)
+ break;
+ RTMemTmpFree(paPids);
+ cbPidsAllocated *= 2;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Search for the process.
+ *
+ * We ASSUME that the caller won't be specifying any names longer
+ * than RTPATH_MAX.
+ */
+ PRTUTF16 pwszProcName = (PRTUTF16)RTMemTmpAllocZ(RTPATH_MAX * sizeof(pwszProcName[0]));
+ if (pwszProcName)
+ {
+ for (size_t i = 0; papszNames[i] && !fFound; i++)
+ {
+ const DWORD cPids = cbPidsReturned / sizeof(DWORD);
+ for (DWORD iPid = 0; iPid < cPids && !fFound; iPid++)
+ {
+ HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, paPids[iPid]);
+ if (hProc)
+ {
+ *pwszProcName = '\0';
+ DWORD cbRet = g_pfnGetModuleBaseNameW(hProc, 0 /*hModule = exe */, pwszProcName, RTPATH_MAX);
+ if ( cbRet > 0
+ && RTUtf16ICmpAscii(pwszProcName, papszNames[i]) == 0
+ && RT_SUCCESS(rtProcWinGetProcessTokenHandle(paPids[iPid], pSid, UINT32_MAX, phToken)))
+ fFound = true;
+ CloseHandle(hProc);
+ }
+ }
+ }
+ RTMemTmpFree(pwszProcName);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ RTMemTmpFree(paPids);
+
+ return fFound;
+}
+
+
+/**
+ * Finds a one of the processes in @a papszNames running with user @a pSid and possibly
+ * in the required windows session. Returns a duplicate handle to its token.
+ *
+ * @returns Success indicator.
+ * @param papszNames The process candidates, in prioritized order.
+ * @param pSid The secure identifier of the user.
+ * @param idDesiredSession The session the process candidate should
+ * belong to if possible, UINT32_MAX if anything
+ * goes.
+ * @param phToken Where to return the token handle - duplicate,
+ * caller closes it on success.
+ */
+static bool rtProcWinFindTokenByProcess(const char * const *papszNames, PSID pSid, uint32_t idDesiredSession, PHANDLE phToken)
+{
+ AssertPtr(papszNames);
+ AssertPtr(pSid);
+ AssertPtr(phToken);
+
+ bool fFound = false;
+
+ /*
+ * On modern systems (W2K+) try the Toolhelp32 API first; this is more stable
+ * and reliable. Fallback to EnumProcess on NT4.
+ */
+ bool fFallback = true;
+ if (g_pfnProcess32NextW && g_pfnProcess32FirstW && g_pfnCreateToolhelp32Snapshot)
+ {
+ HANDLE hSnap = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ Assert(hSnap != INVALID_HANDLE_VALUE);
+ if (hSnap != INVALID_HANDLE_VALUE)
+ {
+ fFallback = false;
+ for (size_t i = 0; papszNames[i] && !fFound; i++)
+ {
+ PROCESSENTRY32W ProcEntry;
+ ProcEntry.dwSize = sizeof(PROCESSENTRY32);
+ ProcEntry.szExeFile[0] = '\0';
+ if (g_pfnProcess32FirstW(hSnap, &ProcEntry))
+ {
+ do
+ {
+ if (RTUtf16ICmpAscii(ProcEntry.szExeFile, papszNames[i]) == 0)
+ {
+ int rc = rtProcWinGetProcessTokenHandle(ProcEntry.th32ProcessID, pSid, idDesiredSession, phToken);
+ if (RT_SUCCESS(rc))
+ {
+ fFound = true;
+ break;
+ }
+ }
+ } while (g_pfnProcess32NextW(hSnap, &ProcEntry));
+ }
+ else
+ AssertMsgFailed(("dwErr=%u (%x)\n", GetLastError(), GetLastError()));
+ }
+ CloseHandle(hSnap);
+ }
+ }
+
+ /* If we couldn't take a process snapshot for some reason or another, fall
+ back on the NT4 compatible API. */
+ if (fFallback)
+ fFound = rtProcWinFindTokenByProcessAndPsApi(papszNames, pSid, phToken);
+ return fFound;
+}
+
+
+/**
+ * Logs on a specified user and returns its primary token.
+ *
+ * @returns IPRT status code.
+ * @param pwszUser User name. A domain name can be specified (as part of a UPN, User Principal Name),
+ * e.g. "joedoe@example.com".
+ * @param pwszPassword Password.
+ * @param phToken Pointer to store the logon token.
+ */
+static int rtProcWinUserLogon(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, HANDLE *phToken)
+{
+ AssertPtrReturn(pwszUser, VERR_INVALID_POINTER);
+ AssertPtrReturn(pwszPassword, VERR_INVALID_POINTER);
+ AssertPtrReturn(phToken, VERR_INVALID_POINTER);
+ if (!g_pfnLogonUserW)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Because we have to deal with http://support.microsoft.com/kb/245683
+ * for NULL domain names when running on NT4 here, pass an empty string if so.
+ * However, passing FQDNs should work!
+ *
+ * The SE_TCB_NAME (Policy: Act as part of the operating system) right
+ * is required on older windows versions (NT4, W2K, possibly XP).
+ */
+ PCRTUTF16 pwszDomainNone = g_enmWinVer < kRTWinOSType_2K ? L"" /* NT4 and older */ : NULL /* Windows 2000 and up */;
+ BOOL fRc = g_pfnLogonUserW(pwszUser,
+ /* The domain always is passed as part of the UPN (user name). */
+ pwszDomainNone,
+ pwszPassword,
+ LOGON32_LOGON_INTERACTIVE,
+ LOGON32_PROVIDER_DEFAULT,
+ phToken);
+ if (fRc)
+ return VINF_SUCCESS;
+
+ DWORD dwErr = GetLastError();
+ int rc = dwErr == ERROR_PRIVILEGE_NOT_HELD ? VERR_PROC_TCB_PRIV_NOT_HELD : RTErrConvertFromWin32(dwErr);
+ if (rc == VERR_UNRESOLVED_ERROR)
+ LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
+ return rc;
+}
+
+
+/**
+ * Returns the environment to use for the child process.
+ *
+ * This implements the RTPROC_FLAGS_ENV_CHANGE_RECORD and environment related
+ * parts of RTPROC_FLAGS_PROFILE.
+ *
+ * @returns IPRT status code.
+ * @param hToken The user token to use if RTPROC_FLAGS_PROFILE is given.
+ * The caller must have loaded profile for this.
+ * @param hEnv The environment passed in by the RTProcCreateEx caller.
+ * @param fFlags The process creation flags passed in by the
+ * RTProcCreateEx caller (RTPROC_FLAGS_XXX).
+ * @param phEnv Where to return the environment to use. This can either
+ * be a newly created environment block or @a hEnv. In the
+ * former case, the caller must destroy it.
+ */
+static int rtProcWinCreateEnvFromToken(HANDLE hToken, RTENV hEnv, uint32_t fFlags, PRTENV phEnv)
+{
+ int rc;
+
+ /*
+ * Query the environment from the user profile associated with the token if
+ * the caller has specified it directly or indirectly.
+ */
+ if ( (fFlags & RTPROC_FLAGS_PROFILE)
+ && ( hEnv == RTENV_DEFAULT
+ || (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) )
+ {
+ if (g_pfnCreateEnvironmentBlock && g_pfnDestroyEnvironmentBlock)
+ {
+ LPVOID pvEnvBlockProfile = NULL;
+ if (g_pfnCreateEnvironmentBlock(&pvEnvBlockProfile, hToken, FALSE /* Don't inherit from parent. */))
+ {
+ rc = RTEnvCloneUtf16Block(phEnv, (PCRTUTF16)pvEnvBlockProfile, 0 /*fFlags*/);
+ if ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
+ && RT_SUCCESS(rc)
+ && hEnv != RTENV_DEFAULT)
+ {
+ rc = RTEnvApplyChanges(*phEnv, hEnv);
+ if (RT_FAILURE(rc))
+ RTEnvDestroy(*phEnv);
+ }
+ g_pfnDestroyEnvironmentBlock(pvEnvBlockProfile);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_SYMBOL_NOT_FOUND;
+ }
+ /*
+ * We we've got an incoming change record, combine it with the default environment.
+ */
+ else if (hEnv != RTENV_DEFAULT && (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD))
+ {
+ rc = RTEnvClone(phEnv, RTENV_DEFAULT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTEnvApplyChanges(*phEnv, hEnv);
+ if (RT_FAILURE(rc))
+ RTEnvDestroy(*phEnv);
+ }
+ }
+ /*
+ * Otherwise we can return the incoming environment directly.
+ */
+ else
+ {
+ *phEnv = hEnv;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Figures which privilege we're missing for success application of
+ * CreateProcessAsUserW.
+ *
+ * @returns IPRT error status.
+ */
+static int rtProcWinFigureWhichPrivilegeNotHeld2(void)
+{
+ HANDLE hToken;
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
+ {
+ static struct
+ {
+ const char *pszName;
+ int rc;
+ } const s_aPrivileges[] =
+ {
+ { SE_TCB_NAME, VERR_PROC_TCB_PRIV_NOT_HELD },
+ { SE_ASSIGNPRIMARYTOKEN_NAME, VERR_PROC_APT_PRIV_NOT_HELD },
+ { SE_INCREASE_QUOTA_NAME, VERR_PROC_IQ_PRIV_NOT_HELD },
+ };
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aPrivileges); i++)
+ {
+ union
+ {
+ TOKEN_PRIVILEGES TokPriv;
+ char abAlloced[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
+ } uNew, uOld;
+ uNew.TokPriv.PrivilegeCount = 1;
+ uNew.TokPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ AssertContinue(LookupPrivilegeValue(NULL, s_aPrivileges[i].pszName, &uNew.TokPriv.Privileges[0].Luid));
+ uOld = uNew;
+ SetLastError(NO_ERROR);
+ DWORD cbActual = RT_UOFFSETOF(TOKEN_PRIVILEGES, Privileges[1]);
+ AdjustTokenPrivileges(hToken, FALSE /*fDisableAllPrivileges*/, &uNew.TokPriv, cbActual, &uOld.TokPriv, &cbActual);
+ if (GetLastError() != NO_ERROR)
+ {
+ CloseHandle(hToken);
+ return s_aPrivileges[i].rc;
+ }
+ if (uOld.TokPriv.Privileges[0].Attributes == 0)
+ AdjustTokenPrivileges(hToken, FALSE /*fDisableAllPrivileges*/, &uOld.TokPriv, 0, NULL, NULL);
+ }
+ AssertFailed();
+ CloseHandle(hToken);
+ }
+ else
+ AssertFailed();
+ return VERR_PRIVILEGE_NOT_HELD;
+}
+
+#if 0 /* debug code */
+
+static char *rtProcWinSidToString(char *psz, PSID pSid)
+{
+ char *pszRet = psz;
+
+ *psz++ = 'S';
+ *psz++ = '-';
+ *psz++ = '1';
+ *psz++ = '-';
+
+ PISID pISid = (PISID)pSid;
+
+ psz += RTStrFormatU32(psz, 32, RT_MAKE_U32_FROM_U8(pISid->IdentifierAuthority.Value[5],
+ pISid->IdentifierAuthority.Value[4],
+ pISid->IdentifierAuthority.Value[3],
+ pISid->IdentifierAuthority.Value[2]),
+ 10, 0, 0, 0);
+ for (unsigned i = 0; i < pISid->SubAuthorityCount; i++)
+ {
+ *psz++ = '-';
+ psz += RTStrFormatU32(psz, 32, pISid->SubAuthority[i], 10, 0, 0, 0);
+ }
+ *psz++ = '\0';
+ return pszRet;
+}
+
+static void rtProcWinLogAcl(PACL pAcl)
+{
+ if (!pAcl)
+ RTAssertMsg2("ACL is NULL\n");
+ else
+ {
+ RTAssertMsg2("AceCount=%d AclSize=%#x AclRevision=%d\n", pAcl->AceCount, pAcl->AclSize, pAcl->AclRevision);
+ for (uint32_t i = 0; i < pAcl->AceCount; i++)
+ {
+ PACE_HEADER pAceHdr = NULL;
+ if (GetAce(pAcl, i, (PVOID *)&pAceHdr))
+ {
+ RTAssertMsg2(" ACE[%u]: Flags=%#x Type=%#x Size=%#x", i, pAceHdr->AceFlags, pAceHdr->AceType, pAceHdr->AceSize);
+ char szTmp[256];
+ if (pAceHdr->AceType == ACCESS_ALLOWED_ACE_TYPE)
+ RTAssertMsg2(" Mask=%#x %s\n", ((ACCESS_ALLOWED_ACE *)pAceHdr)->Mask,
+ rtProcWinSidToString(szTmp, &((ACCESS_ALLOWED_ACE *)pAceHdr)->SidStart));
+ else
+ RTAssertMsg2(" ACE[%u]: Flags=%#x Type=%#x Size=%#x\n", i, pAceHdr->AceFlags, pAceHdr->AceType, pAceHdr->AceSize);
+ }
+ }
+ }
+}
+
+static bool rtProcWinLogSecAttr(HANDLE hUserObj)
+{
+ /*
+ * Get the security descriptor for the user interface object.
+ */
+ uint32_t cbSecDesc = _64K;
+ PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
+ DWORD cbNeeded;
+ AssertReturn(pSecDesc, false);
+ if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
+ {
+ RTMemTmpFree(pSecDesc);
+ AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, false);
+ cbSecDesc = cbNeeded + 128;
+ pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ AssertReturn(pSecDesc, false);
+ if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
+ {
+ RTMemTmpFree(pSecDesc);
+ AssertFailedReturn(false);
+ }
+ }
+
+ /*
+ * Get the discretionary access control list (if we have one).
+ */
+ BOOL fDaclDefaulted;
+ BOOL fDaclPresent;
+ PACL pDacl;
+ if (GetSecurityDescriptorDacl(pSecDesc, &fDaclPresent, &pDacl, &fDaclDefaulted))
+ rtProcWinLogAcl(pDacl);
+ else
+ RTAssertMsg2("GetSecurityDescriptorDacl failed\n");
+
+ RTMemFree(pSecDesc);
+ return true;
+}
+
+#endif /* debug */
+
+/**
+ * Get the user SID from a token.
+ *
+ * @returns Pointer to the SID on success. Free by calling RTMemFree.
+ * @param hToken The token..
+ * @param prc Optional return code.
+ */
+static PSID rtProcWinGetTokenUserSid(HANDLE hToken, int *prc)
+{
+ int rcIgn;
+ if (!prc)
+ prc = &rcIgn;
+ *prc = VERR_NO_MEMORY;
+
+ /*
+ * Get the groups associated with the token. We just try a size first then
+ * reallocates if it's insufficient.
+ */
+ DWORD cbUser = _1K;
+ PTOKEN_USER pUser = (PTOKEN_USER)RTMemTmpAlloc(cbUser);
+ AssertReturn(pUser, NULL);
+ DWORD cbNeeded = 0;
+ if (!GetTokenInformation(hToken, TokenUser, pUser, cbUser, &cbNeeded))
+ {
+ DWORD dwErr = GetLastError();
+ RTMemTmpFree(pUser);
+ AssertLogRelMsgReturnStmt(dwErr == ERROR_INSUFFICIENT_BUFFER,
+ ("rtProcWinGetTokenUserSid: GetTokenInformation failed with %u\n", dwErr),
+ *prc = RTErrConvertFromWin32(dwErr), NULL);
+ cbUser = cbNeeded + 128;
+ pUser = (PTOKEN_USER)RTMemTmpAlloc(cbUser);
+ AssertReturn(pUser, NULL);
+ if (!GetTokenInformation(hToken, TokenUser, pUser, cbUser, &cbNeeded))
+ {
+ dwErr = GetLastError();
+ *prc = RTErrConvertFromWin32(dwErr);
+ RTMemTmpFree(pUser);
+ AssertLogRelMsgFailedReturn(("rtProcWinGetTokenUserSid: GetTokenInformation failed with %u\n", dwErr), NULL);
+ }
+ }
+
+ DWORD cbSid = GetLengthSid(pUser->User.Sid);
+ PSID pSidRet = RTMemDup(pUser->User.Sid, cbSid);
+ Assert(pSidRet);
+ RTMemTmpFree(pUser);
+ *prc = VINF_SUCCESS;
+ return pSidRet;
+}
+
+
+#if 0 /* not used */
+/**
+ * Get the login SID from a token.
+ *
+ * @returns Pointer to the SID on success. Free by calling RTMemFree.
+ * @param hToken The token..
+ */
+static PSID rtProcWinGetTokenLogonSid(HANDLE hToken)
+{
+ /*
+ * Get the groups associated with the token. We just try a size first then
+ * reallocates if it's insufficient.
+ */
+ DWORD cbGroups = _1K;
+ PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)RTMemTmpAlloc(cbGroups);
+ AssertReturn(pGroups, NULL);
+ DWORD cbNeeded = 0;
+ if (!GetTokenInformation(hToken, TokenGroups, pGroups, cbGroups, &cbNeeded))
+ {
+ RTMemTmpFree(pGroups);
+ AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, NULL);
+ cbGroups = cbNeeded + 128;
+ pGroups = (PTOKEN_GROUPS)RTMemTmpAlloc(cbGroups);
+ AssertReturn(pGroups, NULL);
+ if (!GetTokenInformation(hToken, TokenGroups, pGroups, cbGroups, &cbNeeded))
+ {
+ RTMemTmpFree(pGroups);
+ AssertFailedReturn(NULL);
+ }
+ }
+
+ /*
+ * Locate the logon sid.
+ */
+ PSID pSidRet = NULL;
+ uint32_t i = pGroups->GroupCount;
+ while (i-- > 0)
+ if ((pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
+ {
+ DWORD cbSid = GetLengthSid(pGroups->Groups[i].Sid);
+ pSidRet = RTMemDup(pGroups->Groups[i].Sid, cbSid);
+ break;
+ }
+
+ RTMemTmpFree(pGroups);
+ Assert(pSidRet);
+ return pSidRet;
+}
+#endif /* unused */
+
+
+/**
+ * Retrieves the DACL security descriptor of the give GUI object.
+ *
+ * @returns Pointer to the security descriptor.
+ * @param hUserObj The GUI object handle.
+ * @param pcbSecDesc Where to return the size of the security descriptor.
+ * @param ppDacl Where to return the DACL pointer.
+ * @param pfDaclPresent Where to return the DACL-present indicator.
+ * @param pDaclSizeInfo Where to return the DACL size information.
+ */
+static PSECURITY_DESCRIPTOR rtProcWinGetUserObjDacl(HANDLE hUserObj, uint32_t *pcbSecDesc, PACL *ppDacl,
+ BOOL *pfDaclPresent, ACL_SIZE_INFORMATION *pDaclSizeInfo)
+{
+ /*
+ * Get the security descriptor for the user interface object.
+ */
+ uint32_t cbSecDesc = _1K;
+ PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
+ DWORD cbNeeded;
+ AssertReturn(pSecDesc, NULL);
+ if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
+ {
+ RTMemTmpFree(pSecDesc);
+ AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, NULL);
+ cbSecDesc = cbNeeded + 128;
+ pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ AssertReturn(pSecDesc, NULL);
+ if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
+ {
+ RTMemTmpFree(pSecDesc);
+ AssertFailedReturn(NULL);
+ }
+ }
+ *pcbSecDesc = cbNeeded;
+
+ /*
+ * Get the discretionary access control list (if we have one).
+ */
+ BOOL fDaclDefaulted;
+ if (GetSecurityDescriptorDacl(pSecDesc, pfDaclPresent, ppDacl, &fDaclDefaulted))
+ {
+ RT_ZERO(*pDaclSizeInfo);
+ pDaclSizeInfo->AclBytesInUse = sizeof(ACL);
+ if ( !*ppDacl
+ || GetAclInformation(*ppDacl, pDaclSizeInfo, sizeof(*pDaclSizeInfo), AclSizeInformation))
+ return pSecDesc;
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ RTMemTmpFree(pSecDesc);
+ return NULL;
+}
+
+
+/**
+ * Copy ACEs from one ACL to another.
+ *
+ * @returns true on success, false on failure.
+ * @param pDst The destination ACL.
+ * @param pSrc The source ACL.
+ * @param cAces The number of ACEs to copy.
+ */
+static bool rtProcWinCopyAces(PACL pDst, PACL pSrc, uint32_t cAces)
+{
+ for (uint32_t i = 0; i < cAces; i++)
+ {
+ PACE_HEADER pAceHdr;
+ AssertReturn(GetAce(pSrc, i, (PVOID *)&pAceHdr), false);
+ AssertReturn(AddAce(pDst, ACL_REVISION, MAXDWORD, pAceHdr, pAceHdr->AceSize), false);
+ }
+ return true;
+}
+
+
+/**
+ * Adds an access-allowed access control entry to an ACL.
+ *
+ * @returns true on success, false on failure.
+ * @param pDstAcl The ACL.
+ * @param fAceFlags The ACE flags.
+ * @param fMask The ACE access mask.
+ * @param pSid The SID to go with the ACE.
+ * @param cbSid The size of the SID.
+ */
+static bool rtProcWinAddAccessAllowedAce(PACL pDstAcl, uint32_t fAceFlags, uint32_t fMask, PSID pSid, uint32_t cbSid)
+{
+ struct
+ {
+ ACCESS_ALLOWED_ACE Core;
+ DWORD abPadding[128]; /* More than enough, AFAIK. */
+ } AceBuf;
+ RT_ZERO(AceBuf);
+ uint32_t const cbAllowedAce = RT_UOFFSETOF(ACCESS_ALLOWED_ACE, SidStart) + cbSid;
+ AssertReturn(cbAllowedAce <= sizeof(AceBuf), false);
+
+ AceBuf.Core.Header.AceSize = cbAllowedAce;
+ AceBuf.Core.Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AceBuf.Core.Header.AceFlags = fAceFlags;
+ AceBuf.Core.Mask = fMask;
+ AssertReturn(CopySid(cbSid, &AceBuf.Core.SidStart, pSid), false);
+
+ uint32_t i = pDstAcl->AceCount;
+ while (i-- > 0)
+ {
+ PACE_HEADER pAceHdr;
+ AssertContinue(GetAce(pDstAcl, i, (PVOID *)&pAceHdr));
+ if ( pAceHdr->AceSize == cbAllowedAce
+ && memcmp(pAceHdr, &AceBuf.Core, cbAllowedAce) == 0)
+ return true;
+
+ }
+ AssertMsgReturn(AddAce(pDstAcl, ACL_REVISION, MAXDWORD, &AceBuf.Core, cbAllowedAce), ("%u\n", GetLastError()), false);
+ return true;
+}
+
+
+/** All window station rights we know about */
+#define MY_WINSTATION_ALL_RIGHTS ( WINSTA_ACCESSCLIPBOARD | WINSTA_ACCESSGLOBALATOMS | WINSTA_CREATEDESKTOP \
+ | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_EXITWINDOWS | WINSTA_READATTRIBUTES \
+ | WINSTA_READSCREEN | WINSTA_WRITEATTRIBUTES | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER )
+/** All desktop rights we know about */
+#define MY_DESKTOP_ALL_RIGHTS ( DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL \
+ | DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD | DESKTOP_READOBJECTS \
+ | DESKTOP_SWITCHDESKTOP | DESKTOP_WRITEOBJECTS | DELETE | READ_CONTROL | WRITE_DAC \
+ | WRITE_OWNER )
+/** Generic rights. */
+#define MY_GENERIC_ALL_RIGHTS ( GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL )
+
+
+/**
+ * Grants the given SID full access to the given window station.
+ *
+ * @returns true on success, false on failure.
+ * @param hWinStation The window station.
+ * @param pSid The SID.
+ */
+static bool rtProcWinAddSidToWinStation(HWINSTA hWinStation, PSID pSid)
+{
+ bool fRet = false;
+
+ /*
+ * Get the current DACL.
+ */
+ uint32_t cbSecDesc;
+ PACL pDacl;
+ ACL_SIZE_INFORMATION DaclSizeInfo;
+ BOOL fDaclPresent;
+ PSECURITY_DESCRIPTOR pSecDesc = rtProcWinGetUserObjDacl(hWinStation, &cbSecDesc, &pDacl, &fDaclPresent, &DaclSizeInfo);
+ if (pSecDesc)
+ {
+ /*
+ * Create a new DACL. This will contain two extra ACEs.
+ */
+ PSECURITY_DESCRIPTOR pNewSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ if ( pNewSecDesc
+ && InitializeSecurityDescriptor(pNewSecDesc, SECURITY_DESCRIPTOR_REVISION))
+ {
+ uint32_t const cbSid = GetLengthSid(pSid);
+ uint32_t const cbNewDacl = DaclSizeInfo.AclBytesInUse + (sizeof(ACCESS_ALLOWED_ACE) + cbSid) * 2;
+ PACL pNewDacl = (PACL)RTMemTmpAlloc(cbNewDacl);
+ if ( pNewDacl
+ && InitializeAcl(pNewDacl, cbNewDacl, ACL_REVISION)
+ && rtProcWinCopyAces(pNewDacl, pDacl, fDaclPresent ? DaclSizeInfo.AceCount : 0))
+ {
+ /*
+ * Add the two new SID ACEs.
+ */
+ if ( rtProcWinAddAccessAllowedAce(pNewDacl, CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE,
+ MY_GENERIC_ALL_RIGHTS, pSid, cbSid)
+ && rtProcWinAddAccessAllowedAce(pNewDacl, NO_PROPAGATE_INHERIT_ACE, MY_WINSTATION_ALL_RIGHTS, pSid, cbSid))
+ {
+ /*
+ * Now mate the new DECL with the security descriptor and set it.
+ */
+ if (SetSecurityDescriptorDacl(pNewSecDesc, TRUE /*fDaclPresent*/, pNewDacl, FALSE /*fDaclDefaulted*/))
+ {
+ SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
+ if (SetUserObjectSecurity(hWinStation, &SecInfo, pNewSecDesc))
+ fRet = true;
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ RTMemTmpFree(pNewDacl);
+ }
+ else
+ AssertFailed();
+ RTMemTmpFree(pNewSecDesc);
+ RTMemTmpFree(pSecDesc);
+ }
+ return fRet;
+}
+
+
+/**
+ * Grants the given SID full access to the given desktop.
+ *
+ * @returns true on success, false on failure.
+ * @param hDesktop The desktop handle.
+ * @param pSid The SID.
+ */
+static bool rtProcWinAddSidToDesktop(HDESK hDesktop, PSID pSid)
+{
+ bool fRet = false;
+
+ /*
+ * Get the current DACL.
+ */
+ uint32_t cbSecDesc;
+ PACL pDacl;
+ ACL_SIZE_INFORMATION DaclSizeInfo;
+ BOOL fDaclPresent;
+ PSECURITY_DESCRIPTOR pSecDesc = rtProcWinGetUserObjDacl(hDesktop, &cbSecDesc, &pDacl, &fDaclPresent, &DaclSizeInfo);
+ if (pSecDesc)
+ {
+ /*
+ * Create a new DACL. This will contain one extra ACE.
+ */
+ PSECURITY_DESCRIPTOR pNewSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ if ( pNewSecDesc
+ && InitializeSecurityDescriptor(pNewSecDesc, SECURITY_DESCRIPTOR_REVISION))
+ {
+ uint32_t const cbSid = GetLengthSid(pSid);
+ uint32_t const cbNewDacl = DaclSizeInfo.AclBytesInUse + (sizeof(ACCESS_ALLOWED_ACE) + cbSid) * 1;
+ PACL pNewDacl = (PACL)RTMemTmpAlloc(cbNewDacl);
+ if ( pNewDacl
+ && InitializeAcl(pNewDacl, cbNewDacl, ACL_REVISION)
+ && rtProcWinCopyAces(pNewDacl, pDacl, fDaclPresent ? DaclSizeInfo.AceCount : 0))
+ {
+ /*
+ * Add the new SID ACE.
+ */
+ if (rtProcWinAddAccessAllowedAce(pNewDacl, 0 /*fAceFlags*/, MY_DESKTOP_ALL_RIGHTS, pSid, cbSid))
+ {
+ /*
+ * Now mate the new DECL with the security descriptor and set it.
+ */
+ if (SetSecurityDescriptorDacl(pNewSecDesc, TRUE /*fDaclPresent*/, pNewDacl, FALSE /*fDaclDefaulted*/))
+ {
+ SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
+ if (SetUserObjectSecurity(hDesktop, &SecInfo, pNewSecDesc))
+ fRet = true;
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ RTMemTmpFree(pNewDacl);
+ }
+ else
+ AssertFailed();
+ RTMemTmpFree(pNewSecDesc);
+ RTMemTmpFree(pSecDesc);
+ }
+ return fRet;
+}
+
+
+/**
+ * Preps the window station and desktop for the new app.
+ *
+ * EXPERIMENTAL. Thus no return code.
+ *
+ * @param hTokenToUse The access token of the new process.
+ * @param pStartupInfo The startup info (we'll change lpDesktop, maybe).
+ * @param phWinStationOld Where to return an window station handle to restore.
+ * Pass this to SetProcessWindowStation if not NULL.
+ */
+static void rtProcWinStationPrep(HANDLE hTokenToUse, STARTUPINFOW *pStartupInfo, HWINSTA *phWinStationOld)
+{
+ /** @todo Always mess with the interactive one? Maybe it's not there... */
+ *phWinStationOld = GetProcessWindowStation();
+ HWINSTA hWinStation0;
+ if (g_pfnOpenWindowStationW)
+ hWinStation0 = g_pfnOpenWindowStationW(L"winsta0", FALSE /*fInherit*/, READ_CONTROL | WRITE_DAC);
+ else
+ hWinStation0 = OpenWindowStationA("winsta0", FALSE /*fInherit*/, READ_CONTROL | WRITE_DAC); /* (for NT3.1) */
+ if (hWinStation0)
+ {
+ if (SetProcessWindowStation(hWinStation0))
+ {
+ HDESK hDesktop = OpenDesktop("default", 0 /*fFlags*/, FALSE /*fInherit*/,
+ READ_CONTROL | WRITE_DAC | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS);
+ if (hDesktop)
+ {
+ /*PSID pSid = rtProcWinGetTokenLogonSid(hTokenToUse); - Better to use the user SID. Avoid overflowing the ACL. */
+ PSID pSid = rtProcWinGetTokenUserSid(hTokenToUse, NULL /*prc*/);
+ if (pSid)
+ {
+ if ( rtProcWinAddSidToWinStation(hWinStation0, pSid)
+ && rtProcWinAddSidToDesktop(hDesktop, pSid))
+ {
+ pStartupInfo->lpDesktop = L"winsta0\\default";
+ }
+ RTMemFree(pSid);
+ }
+ CloseDesktop(hDesktop);
+ }
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ if (g_pfnCloseWindowStation)
+ g_pfnCloseWindowStation(hWinStation0);
+ }
+ else
+ AssertFailed();
+}
+
+
+/**
+ * Extracts the user name + domain from a given UPN (User Principal Name, "joedoe@example.com") or
+ * Down-Level Logon Name format ("example.com\\joedoe") string.
+ *
+ * @return IPRT status code.
+ * @param pwszString Pointer to string to extract the account info from.
+ * @param pAccountInfo Where to store the parsed account info.
+ * Must be free'd with rtProcWinFreeAccountInfo().
+ */
+static int rtProcWinParseAccountInfo(PRTUTF16 pwszString, PRTPROCWINACCOUNTINFO pAccountInfo)
+{
+ AssertPtrReturn(pwszString, VERR_INVALID_POINTER);
+ AssertPtrReturn(pAccountInfo, VERR_INVALID_POINTER);
+
+ /*
+ * Note: UPN handling is defined in RFC 822. We only implement very rudimentary parsing for the user
+ * name and domain fields though.
+ */
+ char *pszString;
+ int rc = RTUtf16ToUtf8(pwszString, &pszString);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ /* UPN or FQDN handling needed? */
+ /** @todo Add more validation here as needed. Regular expressions would be nice. */
+ char *pszDelim = strchr(pszString, '@');
+ if (pszDelim) /* UPN name? */
+ {
+ rc = RTStrToUtf16Ex(pszString, pszDelim - pszString, &pAccountInfo->pwszUserName, 0, NULL);
+ if (RT_FAILURE(rc))
+ break;
+
+ rc = RTStrToUtf16Ex(pszDelim + 1, RTSTR_MAX, &pAccountInfo->pwszDomain, 0, NULL);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else if (pszDelim = strchr(pszString, '\\')) /* FQDN name? */
+ {
+ rc = RTStrToUtf16Ex(pszString, pszDelim - pszString, &pAccountInfo->pwszDomain, 0, NULL);
+ if (RT_FAILURE(rc))
+ break;
+
+ rc = RTStrToUtf16Ex(pszDelim + 1, RTSTR_MAX, &pAccountInfo->pwszUserName, 0, NULL);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ } while (0);
+
+ RTStrFree(pszString);
+ }
+
+#ifdef DEBUG
+ LogRelFunc(("Name : %ls\n", pAccountInfo->pwszUserName));
+ LogRelFunc(("Domain: %ls\n", pAccountInfo->pwszDomain));
+#endif
+
+ if (RT_FAILURE(rc))
+ LogRelFunc(("Parsing \"%ls\" failed with rc=%Rrc\n", pwszString, rc));
+ return rc;
+}
+
+
+static void rtProcWinFreeAccountInfo(PRTPROCWINACCOUNTINFO pAccountInfo)
+{
+ if (!pAccountInfo)
+ return;
+
+ if (pAccountInfo->pwszUserName)
+ {
+ RTUtf16Free(pAccountInfo->pwszUserName);
+ pAccountInfo->pwszUserName = NULL;
+ }
+
+ if (pAccountInfo->pwszDomain)
+ {
+ RTUtf16Free(pAccountInfo->pwszDomain);
+ pAccountInfo->pwszDomain = NULL;
+ }
+}
+
+
+/**
+ * Tries to resolve the name of the SID.
+ *
+ * @returns IPRT status code.
+ * @param pSid The SID to resolve.
+ * @param ppwszName Where to return the name. Use RTUtf16Free to free.
+ */
+static int rtProcWinSidToName(PSID pSid, PRTUTF16 *ppwszName)
+{
+ *ppwszName = NULL;
+
+ /*
+ * Use large initial buffers here to try avoid having to repeat the call.
+ */
+ DWORD cwcAllocated = 512;
+ while (cwcAllocated < _32K)
+ {
+ PRTUTF16 pwszName = RTUtf16Alloc(cwcAllocated * sizeof(RTUTF16));
+ AssertReturn(pwszName, VERR_NO_UTF16_MEMORY);
+ PRTUTF16 pwszDomain = RTUtf16Alloc(cwcAllocated * sizeof(RTUTF16));
+ AssertReturnStmt(pwszDomain, RTUtf16Free(pwszName), VERR_NO_UTF16_MEMORY);
+
+ DWORD cwcName = cwcAllocated;
+ DWORD cwcDomain = cwcAllocated;
+ SID_NAME_USE SidNameUse = SidTypeUser;
+ if (LookupAccountSidW(NULL /*lpSystemName*/, pSid, pwszName, &cwcName, pwszDomain, &cwcDomain, &SidNameUse))
+ {
+ *ppwszName = pwszName;
+ RTUtf16Free(pwszDomain); /* may need this later. */
+ return VINF_SUCCESS;
+ }
+
+ DWORD const dwErr = GetLastError();
+ RTUtf16Free(pwszName);
+ RTUtf16Free(pwszDomain);
+ if (dwErr != ERROR_INSUFFICIENT_BUFFER)
+ return RTErrConvertFromWin32(dwErr);
+ cwcAllocated = RT_MAX(cwcName, cwcDomain) + 1;
+ }
+
+ return RTErrConvertFromWin32(ERROR_INSUFFICIENT_BUFFER);
+}
+
+
+/**
+ * Tries to resolve the user name for the token.
+ *
+ * @returns IPRT status code.
+ * @param hToken The token.
+ * @param ppwszUser Where to return the username. Use RTUtf16Free to free.
+ */
+static int rtProcWinTokenToUsername(HANDLE hToken, PRTUTF16 *ppwszUser)
+{
+ int rc = VINF_SUCCESS;
+ PSID pSid = rtProcWinGetTokenUserSid(hToken, &rc);
+ if (pSid)
+ {
+ rc = rtProcWinSidToName(pSid, ppwszUser);
+ RTMemFree(pSid);
+ }
+ else
+ *ppwszUser = NULL;
+ return rc;
+}
+
+
+/**
+ * Method \#2.
+ *
+ * @note pwszUser can be NULL when RTPROC_FLAGS_AS_IMPERSONATED_TOKEN is set.
+ */
+static int rtProcWinCreateAsUser2(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
+ RTENV hEnv, DWORD dwCreationFlags,
+ STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
+ uint32_t fFlags, const char *pszExec, uint32_t idDesiredSession,
+ HANDLE hUserToken)
+{
+ /*
+ * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
+ * we have to do the following:
+ * - Check the credentials supplied and get the user SID.
+ * - If valid get the correct Explorer/VBoxTray instance corresponding to that
+ * user. This of course is only possible if that user is logged in (over
+ * physical console or terminal services).
+ * - If we found the user's Explorer/VBoxTray app, use and modify the token to
+ * use it in order to allow the newly started process to access the user's
+ * desktop. If there's no Explorer/VBoxTray app we cannot display the started
+ * process (but run it without UI).
+ *
+ * The following restrictions apply:
+ * - A process only can show its UI when the user the process should run
+ * under is logged in (has a desktop).
+ * - We do not want to display a process of user A run on the desktop
+ * of user B on multi session systems.
+ *
+ * The following rights are needed in order to use LogonUserW and
+ * CreateProcessAsUserW, so the local policy has to be modified to:
+ * - SE_TCB_NAME = Act as part of the operating system
+ * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a (process) token object
+ * - SE_INCREASE_QUOTA_NAME = Increase quotas
+ *
+ * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
+ */
+ DWORD dwErr = NO_ERROR;
+ HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
+ int rc = VINF_SUCCESS;
+ if (fFlags & RTPROC_FLAGS_TOKEN_SUPPLIED)
+ hTokenLogon = hUserToken;
+ else if (fFlags & RTPROC_FLAGS_AS_IMPERSONATED_TOKEN)
+ rc = rtProcWinGetThreadTokenHandle(GetCurrentThread(), &hTokenLogon);
+ else if (pwszUser == NULL)
+ rc = rtProcWinGetProcessTokenHandle(GetCurrentProcess(), &hTokenLogon);
+ else
+ rc = rtProcWinUserLogon(pwszUser, pwszPassword, &hTokenLogon);
+ if (RT_SUCCESS(rc))
+ {
+ BOOL fRc;
+ bool fFound = false;
+ HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
+
+ /*
+ * If the SERVICE flag is specified, we do something rather ugly to
+ * make things work at all. We search for a known desktop process
+ * belonging to the user, grab its token and use it for launching
+ * the new process. That way the process will have desktop access.
+ */
+ if (fFlags & RTPROC_FLAGS_SERVICE)
+ {
+ /*
+ * For the token search we need a SID.
+ */
+ PSID pSid = rtProcWinGetTokenUserSid(hTokenLogon, &rc);
+
+ /*
+ * If we got a valid SID, search the running processes.
+ */
+ /*
+ * If we got a valid SID, search the running processes.
+ */
+ if (pSid)
+ {
+ if (IsValidSid(pSid))
+ {
+ /* Array of process names we want to look for. */
+ static const char * const s_papszProcNames[] =
+ {
+#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */
+ { "VBoxTray.exe" },
+# ifndef IN_GUEST
+ { "VirtualBox.exe" },
+# endif
+#endif
+ { "explorer.exe" },
+ NULL
+ };
+ fFound = rtProcWinFindTokenByProcess(s_papszProcNames, pSid, idDesiredSession, &hTokenUserDesktop);
+ dwErr = 0;
+ }
+ else
+ {
+ dwErr = GetLastError();
+ LogRelFunc(("SID is invalid: %ld\n", dwErr));
+ rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_3;
+ }
+
+ RTMemFree(pSid);
+ }
+ }
+ /* else: !RTPROC_FLAGS_SERVICE: Nothing to do here right now. */
+
+#if 0
+ /*
+ * If we make LogonUserW to return an impersonation token, enable this
+ * to convert it into a primary token.
+ */
+ if (!fFound && detect-impersonation-token)
+ {
+ HANDLE hNewToken;
+ if (DuplicateTokenEx(hTokenLogon, MAXIMUM_ALLOWED, NULL /*SecurityAttribs*/,
+ SecurityIdentification, TokenPrimary, &hNewToken))
+ {
+ CloseHandle(hTokenLogon);
+ hTokenLogon = hNewToken;
+ }
+ else
+ AssertMsgFailed(("%d\n", GetLastError()));
+ }
+#endif
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * If we didn't find a matching VBoxTray, just use the token we got
+ * above from LogonUserW(). This enables us to at least run processes
+ * with desktop interaction without UI.
+ */
+ HANDLE hTokenToUse = fFound ? hTokenUserDesktop : hTokenLogon;
+ if ( !(fFlags & RTPROC_FLAGS_PROFILE)
+ || (g_pfnUnloadUserProfile && g_pfnLoadUserProfileW) )
+ {
+ /*
+ * Load the profile, if requested. (Must be done prior to creating the enviornment.)
+ *
+ * Note! We don't have sufficient rights when impersonating a user, but we can
+ * ASSUME the user is logged on and has its profile loaded into HKEY_USERS already.
+ */
+ PROFILEINFOW ProfileInfo;
+ PRTUTF16 pwszUserFree = NULL;
+ RT_ZERO(ProfileInfo);
+ /** @todo r=bird: We probably don't need to load anything if pwszUser is NULL... */
+ if ((fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN)) == RTPROC_FLAGS_PROFILE)
+ {
+ if (!pwszUser)
+ {
+ Assert(fFlags & RTPROC_FLAGS_AS_IMPERSONATED_TOKEN);
+ rc = rtProcWinTokenToUsername(hTokenToUse, &pwszUserFree);
+ pwszUser = pwszUserFree;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ ProfileInfo.dwSize = sizeof(ProfileInfo);
+ ProfileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
+ ProfileInfo.lpUserName = pwszUser;
+ if (!g_pfnLoadUserProfileW(hTokenToUse, &ProfileInfo))
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the environment.
+ */
+ RTENV hEnvFinal;
+ rc = rtProcWinCreateEnvFromToken(hTokenToUse, hEnv, fFlags, &hEnvFinal);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszzBlock;
+ rc = RTEnvQueryUtf16Block(hEnvFinal, &pwszzBlock);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtProcWinFindExe(fFlags, hEnv, pszExec, ppwszExec);
+ if (RT_SUCCESS(rc))
+ {
+ HWINSTA hOldWinStation = NULL;
+ if ( !fFound
+ && g_enmWinVer <= kRTWinOSType_NT4) /** @todo test newer versions... */
+ rtProcWinStationPrep(hTokenToUse, pStartupInfo, &hOldWinStation);
+
+ /*
+ * Useful KB articles:
+ * http://support.microsoft.com/kb/165194/
+ * http://support.microsoft.com/kb/184802/
+ * http://support.microsoft.com/kb/327618/
+ */
+ if (g_pfnCreateProcessAsUserW)
+ {
+ fRc = g_pfnCreateProcessAsUserW(hTokenToUse,
+ *ppwszExec,
+ pwszCmdLine,
+ NULL, /* pProcessAttributes */
+ NULL, /* pThreadAttributes */
+ TRUE, /* fInheritHandles */
+ dwCreationFlags,
+ /** @todo Warn about exceeding 8192 bytes
+ * on XP and up. */
+ pwszzBlock, /* lpEnvironment */
+ NULL, /* pCurrentDirectory */
+ pStartupInfo,
+ pProcInfo);
+ if (fRc)
+ rc = VINF_SUCCESS;
+ else
+ {
+ dwErr = GetLastError();
+ if (dwErr == ERROR_PRIVILEGE_NOT_HELD)
+ rc = rtProcWinFigureWhichPrivilegeNotHeld2();
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ if (hOldWinStation)
+ SetProcessWindowStation(hOldWinStation);
+ }
+ RTEnvFreeUtf16Block(pwszzBlock);
+ }
+
+ if (hEnvFinal != hEnv)
+ RTEnvDestroy(hEnvFinal);
+ }
+
+ if ((fFlags & RTPROC_FLAGS_PROFILE) && ProfileInfo.hProfile)
+ {
+ fRc = g_pfnUnloadUserProfile(hTokenToUse, ProfileInfo.hProfile);
+#ifdef RT_STRICT
+ if (!fRc)
+ {
+ DWORD dwErr2 = GetLastError();
+ AssertMsgFailed(("Unloading user profile failed with error %u (%#x) - Are all handles closed? (dwErr=%u)",
+ dwErr2, dwErr2, dwErr));
+ }
+#endif
+ }
+ if (pwszUserFree)
+ RTUtf16Free(pwszUserFree);
+ }
+ }
+ else
+ rc = VERR_SYMBOL_NOT_FOUND;
+ } /* Account lookup succeeded? */
+
+ if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
+ CloseHandle(hTokenUserDesktop);
+ if ( !(fFlags & RTPROC_FLAGS_TOKEN_SUPPLIED)
+ && hTokenLogon != INVALID_HANDLE_VALUE)
+ CloseHandle(hTokenLogon);
+
+ if (rc == VERR_UNRESOLVED_ERROR)
+ LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
+ }
+
+ return rc;
+}
+
+
+/**
+ * Plants a standard handle into a child process on older windows versions.
+ *
+ * This is only needed when using CreateProcessWithLogonW on older windows
+ * versions. It would appear that newer versions of windows does this for us.
+ *
+ * @param hSrcHandle The source handle.
+ * @param hDstProcess The child process handle.
+ * @param offProcParamMember The offset to RTL_USER_PROCESS_PARAMETERS.
+ * @param ppvDstProcParamCache Where where cached the address of
+ * RTL_USER_PROCESS_PARAMETERS in the child.
+ */
+static void rtProcWinDupStdHandleIntoChild(HANDLE hSrcHandle, HANDLE hDstProcess, uint32_t offProcParamMember,
+ PVOID *ppvDstProcParamCache)
+{
+ if (hSrcHandle != NULL && hSrcHandle != INVALID_HANDLE_VALUE)
+ {
+ HANDLE hDstHandle;
+ if (DuplicateHandle(GetCurrentProcess(), hSrcHandle, hDstProcess, &hDstHandle,
+ 0 /*IgnoredDesiredAccess*/, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
+ {
+ if (hSrcHandle == hDstHandle)
+ return;
+
+ if (!*ppvDstProcParamCache)
+ {
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ ULONG cbIgn;
+ NTSTATUS rcNt = NtQueryInformationProcess(hDstProcess, ProcessBasicInformation,
+ &BasicInfo, sizeof(BasicInfo), &cbIgn);
+ if (NT_SUCCESS(rcNt))
+ {
+ SIZE_T cbCopied = 0;
+ if (!ReadProcessMemory(hDstProcess,
+ (char *)BasicInfo.PebBaseAddress + RT_UOFFSETOF(PEB_COMMON, ProcessParameters),
+ ppvDstProcParamCache, sizeof(*ppvDstProcParamCache), &cbCopied))
+ {
+ AssertMsgFailed(("PebBaseAddress=%p %d\n", BasicInfo.PebBaseAddress, GetLastError()));
+ *ppvDstProcParamCache = NULL;
+ }
+ }
+ else
+ AssertMsgFailed(("rcNt=%#x\n", rcNt));
+ }
+ if (*ppvDstProcParamCache)
+ {
+ if (WriteProcessMemory(hDstProcess, (char *)*ppvDstProcParamCache + offProcParamMember,
+ &hDstHandle, sizeof(hDstHandle), NULL))
+ return;
+ }
+
+ /*
+ * Close the handle.
+ */
+ HANDLE hSrcHandle2;
+ if (DuplicateHandle(hDstProcess, hDstHandle, GetCurrentProcess(), &hSrcHandle2,
+ 0 /*IgnoredDesiredAccess*/, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ CloseHandle(hSrcHandle2);
+ else
+ AssertMsgFailed(("hDstHandle=%p %u\n", hDstHandle, GetLastError()));
+ }
+ else
+ AssertMsg(GetLastError() == ERROR_INVALID_PARAMETER, ("%u\n", GetLastError()));
+ }
+}
+
+
+/**
+ * Method \#1.
+ *
+ * This method requires Windows 2000 or later. It may fail if the process is
+ * running under the SYSTEM account (like a service, ERROR_ACCESS_DENIED) on
+ * newer platforms (however, this works on W2K!).
+ */
+static int rtProcWinCreateAsUser1(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
+ RTENV hEnv, DWORD dwCreationFlags,
+ STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
+ uint32_t fFlags, const char *pszExec)
+{
+ /* The CreateProcessWithLogonW API was introduced with W2K and later. It uses a service
+ for launching the process. */
+ if (!g_pfnCreateProcessWithLogonW)
+ return VERR_SYMBOL_NOT_FOUND;
+
+ /*
+ * Create the environment block and find the executable first.
+ *
+ * We try to skip this when RTPROC_FLAGS_PROFILE is set so we can sidestep
+ * potential missing TCB privilege issues when calling UserLogonW. At least
+ * NT4 and W2K requires the trusted code base (TCB) privilege for logon use.
+ * Passing pwszzBlock=NULL and LOGON_WITH_PROFILE means the child process
+ * gets the environment specified by the user profile.
+ */
+ int rc;
+ PRTUTF16 pwszzBlock = NULL;
+
+ /* Eliminating the path search flags simplifies things a little. */
+ if ( (fFlags & RTPROC_FLAGS_SEARCH_PATH)
+ && (RTPathHasPath(pszExec) || RTPathExists(pszExec)))
+ fFlags &= ~RTPROC_FLAGS_SEARCH_PATH;
+
+ /*
+ * No profile is simple, as is a user specified environment (no change record).
+ */
+ if ( !(fFlags & RTPROC_FLAGS_PROFILE)
+ || ( !(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
+ && hEnv != RTENV_DEFAULT))
+ rc = rtProcWinCreateEnvBlockAndFindExe(fFlags, hEnv, pszExec, &pwszzBlock, ppwszExec);
+ /*
+ * Default profile environment without changes or path searching we leave
+ * to the service that implements the API.
+ */
+ else if ( hEnv == RTENV_DEFAULT
+ && !(fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_SEARCH_PATH)))
+ {
+ pwszzBlock = NULL;
+ rc = VINF_SUCCESS;
+ }
+ /*
+ * Otherwise, we need to get the user profile environment.
+ */
+ else
+ {
+ RTENV hEnvToUse = NIL_RTENV;
+ HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
+ rc = rtProcWinUserLogon(pwszUser, pwszPassword, &hTokenLogon);
+ if (RT_SUCCESS(rc))
+ {
+ /* CreateEnvFromToken docs says we should load the profile, though
+ we haven't observed any difference when not doing it. Maybe it's
+ only an issue with roaming profiles or something similar... */
+ PROFILEINFOW ProfileInfo;
+ RT_ZERO(ProfileInfo);
+ ProfileInfo.dwSize = sizeof(ProfileInfo);
+ ProfileInfo.lpUserName = pwszUser;
+ ProfileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
+
+ if (g_pfnLoadUserProfileW(hTokenLogon, &ProfileInfo))
+ {
+ /*
+ * Do what we need to do. Don't keep any temp environment object.
+ */
+ rc = rtProcWinCreateEnvFromToken(hTokenLogon, hEnv, fFlags, &hEnvToUse);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtProcWinFindExe(fFlags, hEnv, pszExec, ppwszExec);
+ if (RT_SUCCESS(rc))
+ rc = RTEnvQueryUtf16Block(hEnvToUse, &pwszzBlock);
+ if (hEnvToUse != hEnv)
+ RTEnvDestroy(hEnvToUse);
+ }
+
+ if (!g_pfnUnloadUserProfile(hTokenLogon, ProfileInfo.hProfile))
+ AssertFailed();
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ if (hTokenLogon != INVALID_HANDLE_VALUE)
+ CloseHandle(hTokenLogon);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the process.
+ */
+ Assert(!(dwCreationFlags & CREATE_SUSPENDED));
+ bool const fCreatedSuspended = g_enmWinVer < kRTWinOSType_XP;
+ BOOL fRc = g_pfnCreateProcessWithLogonW(pwszUser,
+ NULL, /* lpDomain*/
+ pwszPassword,
+ fFlags & RTPROC_FLAGS_PROFILE ? 1 /*LOGON_WITH_PROFILE*/ : 0,
+ *ppwszExec,
+ pwszCmdLine,
+ dwCreationFlags | (fCreatedSuspended ? CREATE_SUSPENDED : 0),
+ pwszzBlock,
+ NULL, /* pCurrentDirectory */
+ pStartupInfo,
+ pProcInfo);
+ if (fRc)
+ {
+ if (!fCreatedSuspended)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * Duplicate standard handles into the child process, we ignore failures here as it's
+ * legal to have bad standard handle values and we cannot dup console I/O handles.*
+ */
+ PVOID pvDstProcParamCache = NULL;
+ rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdInput, pProcInfo->hProcess,
+ RT_UOFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardInput), &pvDstProcParamCache);
+ rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdOutput, pProcInfo->hProcess,
+ RT_UOFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardOutput), &pvDstProcParamCache);
+ rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdError, pProcInfo->hProcess,
+ RT_UOFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardError), &pvDstProcParamCache);
+
+ if (ResumeThread(pProcInfo->hThread) != ~(DWORD)0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (RT_FAILURE(rc))
+ {
+ TerminateProcess(pProcInfo->hProcess, 127);
+ CloseHandle(pProcInfo->hThread);
+ CloseHandle(pProcInfo->hProcess);
+ }
+ }
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ if (rc == VERR_UNRESOLVED_ERROR)
+ LogRelFunc(("CreateProcessWithLogonW failed: dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
+ }
+ if (pwszzBlock)
+ RTEnvFreeUtf16Block(pwszzBlock);
+ }
+ return rc;
+}
+
+
+static int rtProcWinCreateAsUser(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
+ RTENV hEnv, DWORD dwCreationFlags,
+ STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
+ uint32_t fFlags, const char *pszExec, uint32_t idDesiredSession,
+ HANDLE hUserToken)
+{
+ /*
+ * If we run as a service CreateProcessWithLogon will fail, so don't even
+ * try it (because of Local System context). If we got an impersonated token
+ * we should use, we also have to have to skip over this approach.
+ * Note! This method is very slow on W2K.
+ */
+ if (!(fFlags & (RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN | RTPROC_FLAGS_TOKEN_SUPPLIED)))
+ {
+ AssertPtr(pwszUser);
+ int rc = rtProcWinCreateAsUser1(pwszUser, pwszPassword, ppwszExec, pwszCmdLine,
+ hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags, pszExec);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ return rtProcWinCreateAsUser2(pwszUser, pwszPassword, ppwszExec, pwszCmdLine, hEnv, dwCreationFlags,
+ pStartupInfo, pProcInfo, fFlags, pszExec, idDesiredSession, hUserToken);
+}
+
+
+/**
+ * RTPathTraverseList callback used by rtProcWinFindExe to locate the
+ * executable.
+ */
+static DECLCALLBACK(int) rtPathFindExec(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
+{
+ const char *pszExec = (const char *)pvUser1;
+ char *pszRealExec = (char *)pvUser2;
+ int rc = RTPathJoinEx(pszRealExec, RTPATH_MAX, pchPath, cchPath, pszExec, RTSTR_MAX, RTPATH_STR_F_STYLE_HOST);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (RTFileExists(pszRealExec))
+ return VINF_SUCCESS;
+ return VERR_TRY_AGAIN;
+}
+
+
+/**
+ * Locate the executable file if necessary.
+ *
+ * @returns IPRT status code.
+ * @param pszExec The UTF-8 executable string passed in by the user.
+ * @param fFlags The process creation flags pass in by the user.
+ * @param hEnv The environment to get the path variabel from.
+ * @param ppwszExec Pointer to the variable pointing to the UTF-16
+ * converted string. If we find something, the current
+ * pointer will be free (RTUtf16Free) and
+ * replaced by a new one.
+ */
+static int rtProcWinFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, PRTUTF16 *ppwszExec)
+{
+ /*
+ * Return immediately if we're not asked to search, or if the file has a
+ * path already or if it actually exists in the current directory.
+ */
+ if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH)
+ || RTPathHavePath(pszExec)
+ || RTPathExists(pszExec) )
+ return VINF_SUCCESS;
+
+ /*
+ * Search the Path or PATH variable for the file.
+ */
+ char *pszPath;
+ if (RTEnvExistEx(hEnv, "PATH"))
+ pszPath = RTEnvDupEx(hEnv, "PATH");
+ else if (RTEnvExistEx(hEnv, "Path"))
+ pszPath = RTEnvDupEx(hEnv, "Path");
+ else
+ return VERR_FILE_NOT_FOUND;
+
+ char szRealExec[RTPATH_MAX];
+ int rc = RTPathTraverseList(pszPath, ';', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
+ RTStrFree(pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Replace the executable string.
+ */
+ RTPathWinFree(*ppwszExec);
+ *ppwszExec = NULL;
+ rc = RTPathWinFromUtf8(ppwszExec, szRealExec, 0 /*fFlags*/);
+ }
+ else if (rc == VERR_END_OF_STRING)
+ rc = VERR_FILE_NOT_FOUND;
+ return rc;
+}
+
+
+/**
+ * Creates the UTF-16 environment block and, if necessary, find the executable.
+ *
+ * @returns IPRT status code.
+ * @param fFlags The process creation flags pass in by the user.
+ * @param hEnv The environment handle passed by the user.
+ * @param pszExec See rtProcWinFindExe.
+ * @param ppwszzBlock Where RTEnvQueryUtf16Block returns the block.
+ * @param ppwszExec See rtProcWinFindExe.
+ */
+static int rtProcWinCreateEnvBlockAndFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec,
+ PRTUTF16 *ppwszzBlock, PRTUTF16 *ppwszExec)
+{
+ int rc;
+
+ /*
+ * In most cases, we just need to convert the incoming enviornment to a
+ * UTF-16 environment block.
+ */
+ RTENV hEnvToUse = NIL_RTENV; /* (MSC maybe used uninitialized) */
+ if ( !(fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_ENV_CHANGE_RECORD))
+ || (hEnv == RTENV_DEFAULT && !(fFlags & RTPROC_FLAGS_PROFILE))
+ || (hEnv != RTENV_DEFAULT && !(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) )
+ {
+ hEnvToUse = hEnv;
+ rc = VINF_SUCCESS;
+ }
+ else if (fFlags & RTPROC_FLAGS_PROFILE)
+ {
+ /*
+ * We need to get the profile environment for the current user.
+ */
+ Assert((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT);
+ AssertReturn(g_pfnCreateEnvironmentBlock && g_pfnDestroyEnvironmentBlock, VERR_SYMBOL_NOT_FOUND);
+ AssertReturn(g_pfnLoadUserProfileW && g_pfnUnloadUserProfile, VERR_SYMBOL_NOT_FOUND);
+ HANDLE hToken;
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &hToken))
+ {
+ rc = rtProcWinCreateEnvFromToken(hToken, hEnv, fFlags, &hEnvToUse);
+ CloseHandle(hToken);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ /*
+ * Apply hEnv as a change record on top of the default environment.
+ */
+ Assert(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD);
+ rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTEnvApplyChanges(hEnvToUse, hEnv);
+ if (RT_FAILURE(rc))
+ RTEnvDestroy(hEnvToUse);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Query the UTF-16 environment block and locate the executable (if needed).
+ */
+ rc = RTEnvQueryUtf16Block(hEnvToUse, ppwszzBlock);
+ if (RT_SUCCESS(rc))
+ rc = rtProcWinFindExe(fFlags, hEnvToUse, pszExec, ppwszExec);
+
+ if (hEnvToUse != hEnv)
+ RTEnvDestroy(hEnvToUse);
+ }
+
+ return rc;
+}
+
+
+RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
+ PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
+ const char *pszPassword, void *pvExtraData, PRTPROCESS phProcess)
+{
+ /*
+ * Input validation
+ */
+ AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
+ AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
+ AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
+ AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
+ AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
+
+ /* Extra data: */
+ uint32_t idDesiredSession = UINT32_MAX;
+ if ( (fFlags & (RTPROC_FLAGS_DESIRED_SESSION_ID | RTPROC_FLAGS_SERVICE))
+ == (RTPROC_FLAGS_DESIRED_SESSION_ID | RTPROC_FLAGS_SERVICE))
+ {
+ AssertPtrReturn(pvExtraData, VERR_INVALID_POINTER);
+ idDesiredSession = *(uint32_t *)pvExtraData;
+ }
+ else
+ AssertReturn(!(fFlags & RTPROC_FLAGS_DESIRED_SESSION_ID), VERR_INVALID_FLAGS);
+
+ HANDLE hUserToken = NULL;
+ if (fFlags & RTPROC_FLAGS_TOKEN_SUPPLIED)
+ hUserToken = *(HANDLE *)pvExtraData;
+
+ /*
+ * Initialize the globals.
+ */
+ int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
+ AssertRCReturn(rc, rc);
+ if ( pszAsUser
+ || (fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN
+ | RTPROC_FLAGS_TOKEN_SUPPLIED)))
+ {
+ rc = RTOnce(&g_rtProcWinResolveOnce, rtProcWinResolveOnce, NULL);
+ AssertRCReturn(rc, rc);
+ }
+
+ /*
+ * Get the file descriptors for the handles we've been passed.
+ *
+ * It seems there is no point in trying to convince a child process's CRT
+ * that any of the standard file handles is non-TEXT. So, we don't...
+ */
+ STARTUPINFOW StartupInfo;
+ RT_ZERO(StartupInfo);
+ StartupInfo.cb = sizeof(StartupInfo);
+ StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+#if 1 /* The CRT should keep the standard handles up to date. */
+ StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+#else
+ StartupInfo.hStdInput = _get_osfhandle(0);
+ StartupInfo.hStdOutput = _get_osfhandle(1);
+ StartupInfo.hStdError = _get_osfhandle(2);
+#endif
+ /* If we want to have a hidden process (e.g. not visible to
+ * to the user) use the STARTUPINFO flags. */
+ if (fFlags & RTPROC_FLAGS_HIDDEN)
+ {
+ StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ StartupInfo.wShowWindow = SW_HIDE;
+ }
+
+ PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
+ HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
+ DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
+ HANDLE ahStdDups[3] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
+ for (int i = 0; i < 3; i++)
+ {
+ if (paHandles[i])
+ {
+ AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
+ switch (paHandles[i]->enmType)
+ {
+ case RTHANDLETYPE_FILE:
+ {
+ HANDLE hNativeFile = paHandles[i]->u.hFile != NIL_RTFILE
+ ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
+ : INVALID_HANDLE_VALUE;
+ if ( hNativeFile == *aphStds[i]
+ && g_enmWinVer == kRTWinOSType_NT310)
+ continue;
+ *aphStds[i] = hNativeFile;
+ break;
+ }
+
+ case RTHANDLETYPE_PIPE:
+ *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
+ ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
+ : INVALID_HANDLE_VALUE;
+ if ( g_enmWinVer == kRTWinOSType_NT310
+ && *aphStds[i] == INVALID_HANDLE_VALUE)
+ {
+ AssertMsgReturn(RTPipeGetCreationInheritability(paHandles[i]->u.hPipe), ("%Rrc %p\n", rc, *aphStds[i]),
+ VERR_INVALID_STATE);
+ continue;
+ }
+ break;
+
+ case RTHANDLETYPE_SOCKET:
+ *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
+ ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
+ : INVALID_HANDLE_VALUE;
+ break;
+
+ default:
+ AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
+ }
+
+ /* Get the inheritability of the handle. */
+ if (*aphStds[i] != INVALID_HANDLE_VALUE)
+ {
+ if (!g_pfnGetHandleInformation)
+ afInhStds[i] = 0; /* No handle info on NT 3.1, so ASSUME it is not inheritable. */
+ else if (!g_pfnGetHandleInformation(*aphStds[i], &afInhStds[i]))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailedReturn(("%Rrc aphStds[%d] => %p paHandles[%d]={%d,%p}\n",
+ rc, i, *aphStds[i], i, paHandles[i]->enmType, paHandles[i]->u.uInt),
+ rc);
+ }
+ }
+ }
+ }
+
+ /*
+ * Set the inheritability any handles we're handing the child.
+ *
+ * Note! On NT 3.1 there is no SetHandleInformation, so we have to duplicate
+ * the handles to make sure they are inherited by the child.
+ */
+ rc = VINF_SUCCESS;
+ for (int i = 0; i < 3; i++)
+ if ( (afInhStds[i] != 0xffffffff)
+ && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
+ {
+ if (!g_pfnSetHandleInformation)
+ {
+ if (DuplicateHandle(GetCurrentProcess(), *aphStds[i], GetCurrentProcess(), &ahStdDups[i],
+ i == 0 ? GENERIC_READ : GENERIC_WRITE, TRUE /*fInheritHandle*/, DUPLICATE_SAME_ACCESS))
+ *aphStds[i] = ahStdDups[i];
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailedBreak(("%Rrc aphStds[%u] => %p\n", rc, i, *aphStds[i]));
+ }
+ }
+ else if (!g_pfnSetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_INVALID_FUNCTION && g_enmWinVer == kRTWinOSType_NT310)
+ rc = VINF_SUCCESS;
+ else
+ AssertMsgFailedBreak(("%Rrc aphStds[%u] => %p\n", rc, i, *aphStds[i]));
+ }
+ }
+
+ /*
+ * Create the command line and convert the executable name.
+ */
+ PRTUTF16 pwszCmdLine = NULL; /* Shut up, MSC! */
+ if (RT_SUCCESS(rc))
+ rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs,
+ !(fFlags & RTPROC_FLAGS_UNQUOTED_ARGS)
+ ? RTGETOPTARGV_CNV_QUOTE_MS_CRT : RTGETOPTARGV_CNV_UNQUOTED);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszExec;
+ rc = RTPathWinFromUtf8(&pwszExec, pszExec, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get going...
+ */
+ PROCESS_INFORMATION ProcInfo;
+ RT_ZERO(ProcInfo);
+ DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ dwCreationFlags |= DETACHED_PROCESS;
+ if (fFlags & RTPROC_FLAGS_NO_WINDOW)
+ dwCreationFlags |= CREATE_NO_WINDOW;
+
+ /*
+ * Only use the normal CreateProcess stuff if we have no user name
+ * and we are not running from a (Windows) service. Otherwise use
+ * the more advanced version in rtProcWinCreateAsUser().
+ */
+ if ( pszAsUser == NULL
+ && !(fFlags & (RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN | RTPROC_FLAGS_TOKEN_SUPPLIED)))
+ {
+ /* Create the environment block first. */
+ PRTUTF16 pwszzBlock;
+ rc = rtProcWinCreateEnvBlockAndFindExe(fFlags, hEnv, pszExec, &pwszzBlock, &pwszExec);
+ if (RT_SUCCESS(rc))
+ {
+ if (CreateProcessW(pwszExec,
+ pwszCmdLine,
+ NULL, /* pProcessAttributes */
+ NULL, /* pThreadAttributes */
+ TRUE, /* fInheritHandles */
+ dwCreationFlags,
+ pwszzBlock,
+ NULL, /* pCurrentDirectory */
+ &StartupInfo,
+ &ProcInfo))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTEnvFreeUtf16Block(pwszzBlock);
+ }
+ }
+ else
+ {
+ /*
+ * Convert the additional parameters and use a helper
+ * function to do the actual work.
+ */
+ PRTUTF16 pwszUser = NULL;
+ if (pszAsUser)
+ rc = RTStrToUtf16(pszAsUser, &pwszUser);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszPassword;
+ rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtProcWinCreateAsUser(pwszUser, pwszPassword, &pwszExec, pwszCmdLine, hEnv, dwCreationFlags,
+ &StartupInfo, &ProcInfo, fFlags, pszExec, idDesiredSession,
+ hUserToken);
+
+ if (pwszPassword && *pwszPassword)
+ RTMemWipeThoroughly(pwszPassword, RTUtf16Len(pwszPassword), 5);
+ RTUtf16Free(pwszPassword);
+ }
+ RTUtf16Free(pwszUser);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ CloseHandle(ProcInfo.hThread);
+ if (phProcess)
+ {
+ /*
+ * Add the process to the child process list so RTProcWait can reuse and close
+ * the process handle, unless, of course, the caller has no intention waiting.
+ */
+ if (!(fFlags & RTPROC_FLAGS_NO_WAIT))
+ rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
+ else
+ CloseHandle(ProcInfo.hProcess);
+ *phProcess = ProcInfo.dwProcessId;
+ }
+ else
+ CloseHandle(ProcInfo.hProcess);
+ rc = VINF_SUCCESS;
+ }
+ RTPathWinFree(pwszExec);
+ }
+ RTUtf16Free(pwszCmdLine);
+ }
+
+ if (g_pfnSetHandleInformation)
+ {
+ /* Undo any handle inherit changes. */
+ for (int i = 0; i < 3; i++)
+ if ( (afInhStds[i] != 0xffffffff)
+ && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
+ {
+ if ( !g_pfnSetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0)
+ && ( GetLastError() != ERROR_INVALID_FUNCTION
+ || g_enmWinVer != kRTWinOSType_NT310) )
+ AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
+ }
+ }
+ else
+ {
+ /* Close handles duplicated for correct inheritance. */
+ for (int i = 0; i < 3; i++)
+ if (ahStdDups[i] != INVALID_HANDLE_VALUE)
+ CloseHandle(ahStdDups[i]);
+ }
+
+ return rc;
+}
+
+
+
+RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
+{
+ AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
+ int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Try find the process among the ones we've spawned, otherwise, attempt
+ * opening the specified process.
+ */
+ HANDLE hOpenedProc = NULL;
+ HANDLE hProcess = rtProcWinFindPid(Process);
+ if (hProcess == NULL)
+ {
+ hProcess = hOpenedProc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
+ if (hProcess == NULL)
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_INVALID_PARAMETER)
+ return VERR_PROCESS_NOT_FOUND;
+ return RTErrConvertFromWin32(dwErr);
+ }
+ }
+
+ /*
+ * Wait for it to terminate.
+ */
+ DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
+ DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
+ while (WaitRc == WAIT_IO_COMPLETION)
+ WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
+ switch (WaitRc)
+ {
+ /*
+ * It has terminated.
+ */
+ case WAIT_OBJECT_0:
+ {
+ DWORD dwExitCode;
+ if (GetExitCodeProcess(hProcess, &dwExitCode))
+ {
+ /** @todo the exit code can be special statuses. */
+ if (pProcStatus)
+ {
+ pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
+ pProcStatus->iStatus = (int)dwExitCode;
+ }
+ if (hOpenedProc == NULL)
+ rtProcWinRemovePid(Process);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+ }
+
+ /*
+ * It hasn't terminated just yet.
+ */
+ case WAIT_TIMEOUT:
+ rc = VERR_PROCESS_RUNNING;
+ break;
+
+ /*
+ * Something went wrong...
+ */
+ case WAIT_FAILED:
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+
+ case WAIT_ABANDONED:
+ AssertFailed();
+ rc = VERR_GENERAL_FAILURE;
+ break;
+
+ default:
+ AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+
+ if (hOpenedProc != NULL)
+ CloseHandle(hOpenedProc);
+ return rc;
+}
+
+
+RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
+{
+ /** @todo this isn't quite right. */
+ return RTProcWait(Process, fFlags, pProcStatus);
+}
+
+
+RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
+{
+ if (Process == NIL_RTPROCESS)
+ return VINF_SUCCESS;
+
+ int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Try find the process among the ones we've spawned, otherwise, attempt
+ * opening the specified process.
+ */
+ HANDLE hProcess = rtProcWinFindPid(Process);
+ if (hProcess != NULL)
+ {
+ if (!TerminateProcess(hProcess, 127))
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
+ if (hProcess != NULL)
+ {
+ BOOL fRc = TerminateProcess(hProcess, 127);
+ DWORD dwErr = GetLastError();
+ CloseHandle(hProcess);
+ if (!fRc)
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ }
+ return rc;
+}
+
+
+RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
+{
+ DWORD_PTR dwProcessAffinityMask = 0xffffffff;
+ DWORD_PTR dwSystemAffinityMask;
+
+ BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
+ Assert(fRc); NOREF(fRc);
+
+ return dwProcessAffinityMask;
+}
+
+
+RTR3DECL(int) RTProcQueryUsername(RTPROCESS hProcess, char *pszUser, size_t cbUser, size_t *pcbUser)
+{
+ AssertReturn( (pszUser && cbUser > 0)
+ || (!pszUser && !cbUser), VERR_INVALID_PARAMETER);
+ AssertReturn(pcbUser || pszUser, VERR_INVALID_PARAMETER);
+
+ int rc;
+ if ( hProcess == NIL_RTPROCESS
+ || hProcess == RTProcSelf())
+ {
+ RTUTF16 wszUsername[UNLEN + 1];
+ DWORD cwcUsername = RT_ELEMENTS(wszUsername);
+ if (GetUserNameW(&wszUsername[0], &cwcUsername))
+ {
+ if (pszUser)
+ {
+ rc = RTUtf16ToUtf8Ex(wszUsername, cwcUsername, &pszUser, cbUser, pcbUser);
+ if (pcbUser)
+ *pcbUser += 1;
+ }
+ else
+ {
+ *pcbUser = RTUtf16CalcUtf8Len(wszUsername) + 1;
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
+
+RTR3DECL(int) RTProcQueryUsernameA(RTPROCESS hProcess, char **ppszUser)
+{
+ AssertPtrReturn(ppszUser, VERR_INVALID_POINTER);
+ int rc;
+ if ( hProcess == NIL_RTPROCESS
+ || hProcess == RTProcSelf())
+ {
+ RTUTF16 wszUsername[UNLEN + 1];
+ DWORD cwcUsername = RT_ELEMENTS(wszUsername);
+ if (GetUserNameW(&wszUsername[0], &cwcUsername))
+ rc = RTUtf16ToUtf8(wszUsername, ppszUser);
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp b/src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp
new file mode 100644
index 00000000..f0f478f6
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp
@@ -0,0 +1,72 @@
+/* $Id: rtProcInitExePath-win.cpp $ */
+/** @file
+ * IPRT - rtProcInitName, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <iprt/win/windows.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include "internal/process.h"
+
+
+DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath)
+{
+ /*
+ * Query the image name from the dynamic linker, convert and return it.
+ */
+ WCHAR wsz[RTPATH_MAX];
+ HMODULE hExe = GetModuleHandle(NULL);
+ if (GetModuleFileNameW(hExe, wsz, RTPATH_MAX))
+ {
+ int rc = RTUtf16ToUtf8Ex(wsz, RTSTR_MAX, &pszPath, cchPath, NULL);
+ AssertRCReturn(rc, rc);
+ return VINF_SUCCESS;
+ }
+
+ DWORD err = GetLastError();
+ int rc = RTErrConvertFromWin32(err);
+ AssertMsgFailed(("%Rrc %d\n", rc, err));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/sched-win.cpp b/src/VBox/Runtime/r3/win/sched-win.cpp
new file mode 100644
index 00000000..7b8397e0
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/sched-win.cpp
@@ -0,0 +1,333 @@
+/* $Id: sched-win.cpp $ */
+/** @file
+ * IPRT - Scheduling, Win32.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @def WIN32_SCHED_ENABLED
+ * Enables the priority scheme. */
+#define WIN32_SCHED_ENABLED
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <iprt/win/windows.h>
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/sched.h"
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Configuration of one priority.
+ */
+typedef struct
+{
+ /** The priority. */
+ RTPROCPRIORITY enmPriority;
+ /** The name of this priority. */
+ const char *pszName;
+ /** The Win32 process priority class. If ANY_PROCESS_PRIORITY_CLASS the
+ * process priority class is left unchanged. */
+ DWORD dwProcessPriorityClass;
+ /** Array scheduler attributes corresponding to each of the thread types. */
+ struct
+ {
+ /** For sanity include the array index. */
+ RTTHREADTYPE enmType;
+ /** The Win32 thread priority. */
+ int iThreadPriority;
+ } aTypes[RTTHREADTYPE_END];
+} PROCPRIORITY;
+
+/** Matches any process priority class. */
+#define ANY_PROCESS_PRIORITY_CLASS (~0U)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Array of static priority configurations.
+ */
+static const PROCPRIORITY g_aPriorities[] =
+{
+ {
+ RTPROCPRIORITY_FLAT, "Flat", ANY_PROCESS_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_NORMAL }
+ }
+ },
+ {
+ RTPROCPRIORITY_LOW, "Low - Below Normal", BELOW_NORMAL_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ },
+ {
+ RTPROCPRIORITY_LOW, "Low", ANY_PROCESS_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_NORMAL }
+ }
+ },
+ {
+ RTPROCPRIORITY_NORMAL, "Normal - Normal", NORMAL_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ },
+ {
+ RTPROCPRIORITY_NORMAL, "Normal", ANY_PROCESS_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ },
+ {
+ RTPROCPRIORITY_HIGH, "High - High", HIGH_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ },
+ {
+ RTPROCPRIORITY_HIGH, "High - Above Normal", ABOVE_NORMAL_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ },
+ {
+ RTPROCPRIORITY_HIGH, "High", ANY_PROCESS_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ }
+};
+
+/**
+ * The dynamic default priority configuration.
+ *
+ * This can be recalulated at runtime depending on what the
+ * system allow us to do. Presently we don't do this as it's
+ * generally not a bit issue on Win32 hosts.
+ */
+static PROCPRIORITY g_aDefaultPriority =
+{
+ RTPROCPRIORITY_LOW, "Default", ANY_PROCESS_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+};
+
+
+/** Pointer to the current priority configuration. */
+static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority;
+
+
+/**
+ * Calculate the scheduling properties for all the threads in the default
+ * process priority, assuming the current thread have the type enmType.
+ *
+ * @returns iprt status code.
+ * @param enmType The thread type to be assumed for the current thread.
+ */
+DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END); RT_NOREF_PV(enmType);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority)
+{
+ Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST); RT_NOREF_PV(enmPriority);
+
+ if (enmPriority == RTPROCPRIORITY_DEFAULT)
+ {
+ g_pProcessPriority = &g_aDefaultPriority;
+ return VINF_SUCCESS;
+ }
+
+ for (size_t i = 0; i < RT_ELEMENTS(g_aPriorities); i++)
+ if ( g_aPriorities[i].enmPriority == enmPriority
+ && g_aPriorities[i].dwProcessPriorityClass == ANY_PROCESS_PRIORITY_CLASS)
+ {
+ g_pProcessPriority = &g_aPriorities[i];
+ return VINF_SUCCESS;
+ }
+
+ AssertFailedReturn(VERR_INTERNAL_ERROR);
+}
+
+
+/**
+ * Gets the win32 thread handle.
+ *
+ * @returns Valid win32 handle for the specified thread.
+ * @param pThread The thread.
+ */
+DECLINLINE(HANDLE) rtThreadNativeGetHandle(PRTTHREADINT pThread)
+{
+ if ((uintptr_t)pThread->Core.Key == GetCurrentThreadId())
+ return GetCurrentThread();
+ return (HANDLE)pThread->hThread;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ AssertMsg(g_pProcessPriority && g_pProcessPriority->aTypes[enmType].enmType == enmType,
+ ("enmType=%d entry=%d\n", enmType, g_pProcessPriority->aTypes[enmType].enmType));
+
+#ifdef WIN32_SCHED_ENABLED
+ HANDLE hThread = rtThreadNativeGetHandle(pThread);
+ if ( hThread == NULL /* No handle for alien threads. */
+ || SetThreadPriority(hThread, g_pProcessPriority->aTypes[enmType].iThreadPriority))
+ return VINF_SUCCESS;
+
+ DWORD dwLastError = GetLastError();
+ int rc = RTErrConvertFromWin32(dwLastError);
+ AssertMsgFailed(("SetThreadPriority(%p, %d) failed, dwLastError=%d rc=%Rrc\n",
+ rtThreadNativeGetHandle(pThread), g_pProcessPriority->aTypes[enmType].iThreadPriority, dwLastError, rc));
+ return rc;
+#else
+ return VINF_SUCCESS;
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/win/semevent-win.cpp b/src/VBox/Runtime/r3/win/semevent-win.cpp
new file mode 100644
index 00000000..be086941
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/semevent-win.cpp
@@ -0,0 +1,315 @@
+/* $Id: semevent-win.cpp $ */
+/** @file
+ * IPRT - Event Semaphore, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SEMAPHORE
+#include <iprt/win/windows.h>
+
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+#include "internal/mem.h"
+#include "internal/strict.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+struct RTSEMEVENTINTERNAL
+{
+ /** Magic value (RTSEMEVENT_MAGIC). */
+ uint32_t u32Magic;
+ /** The event handle. */
+ HANDLE hev;
+#ifdef RTSEMEVENT_STRICT
+ /** Signallers. */
+ RTLOCKVALRECSHRD Signallers;
+ /** Indicates that lock validation should be performed. */
+ bool volatile fEverHadSignallers;
+#endif
+ /** The creation flags. */
+ uint32_t fFlags;
+};
+
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+
+ struct RTSEMEVENTINTERNAL *pThis;
+ if (!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ pThis = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(*pThis));
+ else
+ pThis = (struct RTSEMEVENTINTERNAL *)rtMemBaseAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Create the semaphore.
+ * (Auto reset, not signaled, private event object.)
+ */
+ pThis->hev = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (pThis->hev != NULL) /* not INVALID_HANDLE_VALUE */
+ {
+ pThis->u32Magic = RTSEMEVENT_MAGIC;
+ pThis->fFlags = fFlags;
+#ifdef RTSEMEVENT_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemEventAnon = 0;
+ RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
+ "RTSemEvent-%u", ASMAtomicIncU32(&s_iSemEventAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
+ pszNameFmt, va);
+ va_end(va);
+ }
+ pThis->fEverHadSignallers = false;
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
+#endif
+
+ *phEventSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ DWORD dwErr = GetLastError();
+ if (!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ RTMemFree(pThis);
+ else
+ rtMemBaseFree(pThis);
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the handle and close the semaphore.
+ */
+ int rc = VINF_SUCCESS;
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC, RTSEMEVENT_MAGIC), VERR_INVALID_HANDLE);
+ if (CloseHandle(pThis->hev))
+ {
+#ifdef RTSEMEVENT_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->Signallers);
+#endif
+ if (!(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ RTMemFree(pThis);
+ else
+ rtMemBaseFree(pThis);
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ AssertMsgFailed(("Destroy hEventSem %p failed, lasterr=%u (%Rrc)\n", pThis, dwErr, rc));
+ /* Leak it. */
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMEVENT_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Signal the object.
+ */
+ if (SetEvent(pThis->hev))
+ return VINF_SUCCESS;
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("Signaling hEventSem %p failed, lasterr=%d\n", pThis, dwErr));
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+/** Goto avoidance. */
+DECL_FORCE_INLINE(int) rtSemEventWaitHandleStatus(struct RTSEMEVENTINTERNAL *pThis, DWORD rc)
+{
+ switch (rc)
+ {
+ case WAIT_OBJECT_0: return VINF_SUCCESS;
+ case WAIT_TIMEOUT: return VERR_TIMEOUT;
+ case WAIT_IO_COMPLETION: return VERR_INTERRUPTED;
+ case WAIT_ABANDONED: return VERR_SEM_OWNER_DIED;
+ default:
+ AssertMsgFailed(("%u\n", rc));
+ case WAIT_FAILED:
+ {
+ int rc2 = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("Wait on hEventSem %p failed, rc=%d lasterr=%d\n", pThis, rc, GetLastError()));
+ if (rc2)
+ return rc2;
+
+ AssertMsgFailed(("WaitForSingleObject(event) -> rc=%d while converted lasterr=%d\n", rc, rc2));
+ RT_NOREF_PV(pThis);
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+}
+
+
+#undef RTSemEventWaitNoResume
+RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Wait for condition.
+ */
+#ifdef RTSEMEVENT_STRICT
+ RTTHREAD hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)
+ ? RTThreadSelfAutoAdopt()
+ : RTThreadSelf();
+ if (pThis->fEverHadSignallers)
+ {
+ DWORD rc = WaitForSingleObjectEx(pThis->hev,
+ 0 /*Timeout*/,
+ TRUE /*fAlertable*/);
+ if (rc != WAIT_TIMEOUT || cMillies == 0)
+ return rtSemEventWaitHandleStatus(pThis, rc);
+ int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, NULL /*pSrcPos*/, false,
+ cMillies, RTTHREADSTATE_EVENT, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
+ DWORD rc = WaitForSingleObjectEx(pThis->hev,
+ cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies,
+ TRUE /*fAlertable*/);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
+ return rtSemEventWaitHandleStatus(pThis, rc);
+}
+
+
+RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
+
+ RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
+#else
+ RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/win/semeventmulti-win.cpp b/src/VBox/Runtime/r3/win/semeventmulti-win.cpp
new file mode 100644
index 00000000..ed37f656
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/semeventmulti-win.cpp
@@ -0,0 +1,389 @@
+/* $Id: semeventmulti-win.cpp $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphore, Windows.
+ *
+ * @remarks This file is identical to semevent-win.cpp except for the 2nd
+ * CreateEvent parameter, the reset function and the "Multi" infix.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SEMAPHORE
+#include <iprt/win/windows.h>
+
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+struct RTSEMEVENTMULTIINTERNAL
+{
+ /** Magic value (RTSEMEVENTMULTI_MAGIC). */
+ uint32_t u32Magic;
+ /** The event handle. */
+ HANDLE hev;
+#ifdef RTSEMEVENT_STRICT
+ /** Signallers. */
+ RTLOCKVALRECSHRD Signallers;
+ /** Indicates that lock validation should be performed. */
+ bool volatile fEverHadSignallers;
+#endif
+};
+
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ struct RTSEMEVENTMULTIINTERNAL *pThis = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Create the semaphore.
+ * (Manual reset, not signaled, private event object.)
+ */
+ pThis->hev = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (pThis->hev != NULL) /* not INVALID_HANDLE_VALUE */
+ {
+ pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
+#ifdef RTSEMEVENT_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemEventMultiAnon = 0;
+ RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
+ "RTSemEventMulti-%u", ASMAtomicIncU32(&s_iSemEventMultiAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
+ pszNameFmt, va);
+ va_end(va);
+ }
+ pThis->fEverHadSignallers = false;
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
+#endif
+
+ *phEventMultiSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ DWORD dwErr = GetLastError();
+ RTMemFree(pThis);
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENT) /* don't bitch */
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the handle and close the semaphore.
+ */
+ int rc = VINF_SUCCESS;
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTSEMEVENTMULTI_MAGIC, RTSEMEVENTMULTI_MAGIC), VERR_INVALID_HANDLE);
+ if (CloseHandle(pThis->hev))
+ {
+#ifdef RTSEMEVENT_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->Signallers);
+#endif
+ RTMemFree(pThis);
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ AssertMsgFailed(("Destroy hEventMultiSem %p failed, lasterr=%u (%Rrc)\n", pThis, dwErr, rc));
+ /* Leak it. */
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMEVENT_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Signal the object.
+ */
+ if (SetEvent(pThis->hev))
+ return VINF_SUCCESS;
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("Signaling hEventMultiSem %p failed, lasterr=%d\n", pThis, dwErr));
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Reset the object.
+ */
+ if (ResetEvent(pThis->hev))
+ return VINF_SUCCESS;
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("Resetting hEventMultiSem %p failed, lasterr=%d\n", pThis, dwErr));
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+/** Goto avoidance. */
+DECL_FORCE_INLINE(int)
+rtSemEventWaitHandleStatus(struct RTSEMEVENTMULTIINTERNAL *pThis, uint32_t fFlags, DWORD rc)
+{
+ switch (rc)
+ {
+ case WAIT_OBJECT_0: return VINF_SUCCESS;
+ case WAIT_TIMEOUT: return VERR_TIMEOUT;
+ case WAIT_IO_COMPLETION: return fFlags & RTSEMWAIT_FLAGS_RESUME ? VERR_TIMEOUT : VERR_INTERRUPTED;
+ case WAIT_ABANDONED: return VERR_SEM_OWNER_DIED;
+ default:
+ AssertMsgFailed(("%u\n", rc));
+ case WAIT_FAILED:
+ {
+ int rc2 = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("Wait on hEventMultiSem %p failed, rc=%d lasterr=%d\n", pThis, rc, GetLastError()));
+ if (rc2)
+ return rc2;
+
+ AssertMsgFailed(("WaitForSingleObject(event) -> rc=%d while converted lasterr=%d\n", rc, rc2));
+ RT_NOREF_PV(pThis);
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+}
+
+
+DECLINLINE(int) rtSemEventMultiWinWait(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the timeout to a millisecond count.
+ */
+ uint64_t uAbsDeadline;
+ DWORD dwMsTimeout;
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ {
+ dwMsTimeout = INFINITE;
+ uAbsDeadline = UINT64_MAX;
+ }
+ else
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_NANOSECS)
+ uTimeout = uTimeout < UINT64_MAX - UINT32_C(1000000) / 2
+ ? (uTimeout + UINT32_C(1000000) / 2) / UINT32_C(1000000)
+ : UINT64_MAX / UINT32_C(1000000);
+ if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE)
+ {
+ uAbsDeadline = uTimeout;
+ uint64_t u64Now = RTTimeSystemMilliTS();
+ if (u64Now < uTimeout)
+ uTimeout -= u64Now;
+ else
+ uTimeout = 0;
+ }
+ else if (fFlags & RTSEMWAIT_FLAGS_RESUME)
+ uAbsDeadline = RTTimeSystemMilliTS() + uTimeout;
+ else
+ uAbsDeadline = UINT64_MAX;
+
+ dwMsTimeout = uTimeout < UINT32_MAX
+ ? (DWORD)uTimeout
+ : INFINITE;
+ }
+
+ /*
+ * Do the wait.
+ */
+ DWORD rc;
+#ifdef RTSEMEVENT_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
+ if (pThis->fEverHadSignallers)
+ {
+ do
+ rc = WaitForSingleObjectEx(pThis->hev, 0 /*Timeout*/, TRUE /*fAlertable*/);
+ while (rc == WAIT_IO_COMPLETION && (fFlags & RTSEMWAIT_FLAGS_RESUME));
+ if (rc != WAIT_TIMEOUT || dwMsTimeout == 0)
+ return rtSemEventWaitHandleStatus(pThis, fFlags, rc);
+ int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
+ dwMsTimeout, RTTHREADSTATE_EVENT_MULTI, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+ RT_NOREF_PV(pSrcPos);
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true);
+ rc = WaitForSingleObjectEx(pThis->hev, dwMsTimeout, TRUE /*fAlertable*/);
+ if (rc == WAIT_IO_COMPLETION && (fFlags & RTSEMWAIT_FLAGS_RESUME))
+ {
+ while ( rc == WAIT_IO_COMPLETION
+ && RTTimeSystemMilliTS() < uAbsDeadline)
+ rc = WaitForSingleObjectEx(pThis->hev, dwMsTimeout, TRUE /*fAlertable*/);
+
+ }
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI);
+ return rtSemEventWaitHandleStatus(pThis, fFlags, rc);
+}
+
+
+
+#undef RTSemEventMultiWaitEx
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtSemEventMultiWinWait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemEventMultiWinWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemEventMultiWinWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+
+RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
+#else
+ RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/win/semmutex-win.cpp b/src/VBox/Runtime/r3/win/semmutex-win.cpp
new file mode 100644
index 00000000..34aa8bc5
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/semmutex-win.cpp
@@ -0,0 +1,356 @@
+/* $Id: semmutex-win.cpp $ */
+/** @file
+ * IPRT - Mutex Semaphores, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SEMAPHORE
+#include <iprt/win/windows.h>
+
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Posix internal representation of a Mutex semaphore. */
+struct RTSEMMUTEXINTERNAL
+{
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** Recursion count. */
+ uint32_t volatile cRecursions;
+ /** The owner thread. */
+ RTNATIVETHREAD volatile hNativeOwner;
+ /** The mutex handle. */
+ HANDLE hMtx;
+#ifdef RTSEMMUTEX_STRICT
+ /** Lock validator record associated with this mutex. */
+ RTLOCKVALRECEXCL ValidatorRec;
+#endif
+};
+
+
+
+#undef RTSemMutexCreate
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+}
+
+
+RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the semaphore.
+ */
+ int rc;
+ HANDLE hMtx = CreateMutex(NULL, FALSE, NULL);
+ if (hMtx)
+ {
+ RTSEMMUTEXINTERNAL *pThis = (RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+ pThis->hMtx = hMtx;
+ pThis->hNativeOwner = NIL_RTNATIVETHREAD;
+ pThis->cRecursions = 0;
+#ifdef RTSEMMUTEX_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iMutexAnon = 0;
+ RTLockValidatorRecExclInit(&pThis->ValidatorRec, hClass, uSubClass, pThis,
+ !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL),
+ "RTSemMutex-%u", ASMAtomicIncU32(&s_iMutexAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecExclInitV(&pThis->ValidatorRec, hClass, uSubClass, pThis,
+ !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL), pszNameFmt, va);
+ va_end(va);
+ }
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(uSubClass); RT_NOREF_PV(pszNameFmt);
+#endif
+ *phMutexSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ return rc;
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ if (pThis == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Close semaphore handle.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE);
+ HANDLE hMtx = pThis->hMtx;
+ ASMAtomicWritePtr(&pThis->hMtx, INVALID_HANDLE_VALUE);
+
+ int rc = VINF_SUCCESS;
+ if (!CloseHandle(hMtx))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("%p rc=%d lasterr=%d\n", pThis->hMtx, rc, GetLastError()));
+ }
+
+#ifdef RTSEMMUTEX_STRICT
+ RTLockValidatorRecExclDelete(&pThis->ValidatorRec);
+#endif
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTSemMutexSetSubClass(RTSEMMUTEX hMutexSem, uint32_t uSubClass)
+{
+#ifdef RTSEMMUTEX_STRICT
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+
+ return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorRec, uSubClass);
+#else
+ RT_NOREF_PV(hMutexSem); RT_NOREF_PV(uSubClass);
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+#endif
+}
+
+
+/**
+ * Internal worker for RTSemMutexRequestNoResume and it's debug companion.
+ *
+ * @returns Same as RTSEmMutexRequestNoResume
+ * @param hMutexSem The mutex handle.
+ * @param cMillies The number of milliseconds to wait.
+ * @param pSrcPos The source position of the caller.
+ */
+DECL_FORCE_INLINE(int) rtSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Check for recursive entry.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeOwner;
+ ASMAtomicReadHandle(&pThis->hNativeOwner, &hNativeOwner);
+ if (hNativeOwner == hNativeSelf)
+ {
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclRecursion(&pThis->ValidatorRec, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ ASMAtomicIncU32(&pThis->cRecursions);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Lock mutex semaphore.
+ */
+ RTTHREAD hThreadSelf = NIL_RTTHREAD;
+ if (cMillies > 0)
+ {
+#ifdef RTSEMMUTEX_STRICT
+ hThreadSelf = RTThreadSelfAutoAdopt();
+ int rc9 = RTLockValidatorRecExclCheckOrderAndBlocking(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true,
+ cMillies, RTTHREADSTATE_MUTEX, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#else
+ hThreadSelf = RTThreadSelf();
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_MUTEX, true);
+ RT_NOREF_PV(pSrcPos);
+#endif
+ }
+ DWORD rc = WaitForSingleObjectEx(pThis->hMtx,
+ cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies,
+ TRUE /*fAlertable*/);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX);
+ switch (rc)
+ {
+ case WAIT_OBJECT_0:
+#ifdef RTSEMMUTEX_STRICT
+ RTLockValidatorRecExclSetOwner(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true);
+#endif
+ ASMAtomicWriteHandle(&pThis->hNativeOwner, hNativeSelf);
+ ASMAtomicWriteU32(&pThis->cRecursions, 1);
+ return VINF_SUCCESS;
+
+ case WAIT_TIMEOUT: return VERR_TIMEOUT;
+ case WAIT_IO_COMPLETION: return VERR_INTERRUPTED;
+ case WAIT_ABANDONED: return VERR_SEM_OWNER_DIED;
+ default:
+ AssertMsgFailed(("%u\n", rc));
+ case WAIT_FAILED:
+ {
+ int rc2 = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("Wait on hMutexSem %p failed, rc=%d lasterr=%d\n", hMutexSem, rc, GetLastError()));
+ if (rc2 != VINF_SUCCESS)
+ return rc2;
+
+ AssertMsgFailed(("WaitForSingleObject(event) -> rc=%d while converted lasterr=%d\n", rc, rc2));
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+}
+
+
+#undef RTSemMutexRequestNoResume
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMMUTEX_STRICT
+ return rtSemMutexRequestNoResume(hMutexSem, cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemMutexRequestNoResume(hMutexSem, cMillies, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemMutexRequestNoResume(hMutexSem, cMillies, &SrcPos);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Check ownership and recursions.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeOwner;
+ ASMAtomicReadHandle(&pThis->hNativeOwner, &hNativeOwner);
+ if (RT_UNLIKELY(hNativeOwner != hNativeSelf))
+ {
+ AssertMsgFailed(("Not owner of mutex %p!! hNativeSelf=%RTntrd Owner=%RTntrd cRecursions=%d\n",
+ pThis, hNativeSelf, hNativeOwner, pThis->cRecursions));
+ return VERR_NOT_OWNER;
+ }
+ if (pThis->cRecursions > 1)
+ {
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclUnwind(&pThis->ValidatorRec);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ ASMAtomicDecU32(&pThis->cRecursions);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Unlock mutex semaphore.
+ */
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorRec, false);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ ASMAtomicWriteU32(&pThis->cRecursions, 0);
+ ASMAtomicWriteHandle(&pThis->hNativeOwner, NIL_RTNATIVETHREAD);
+
+ if (ReleaseMutex(pThis->hMtx))
+ return VINF_SUCCESS;
+
+ int rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("%p/%p, rc=%Rrc lasterr=%d\n", pThis, pThis->hMtx, rc, GetLastError()));
+ return rc;
+}
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false);
+
+ RTNATIVETHREAD hNativeOwner;
+ ASMAtomicReadHandle(&pThis->hNativeOwner, &hNativeOwner);
+ return hNativeOwner != NIL_RTNATIVETHREAD;
+}
+
diff --git a/src/VBox/Runtime/r3/win/serialport-win.cpp b/src/VBox/Runtime/r3/win/serialport-win.cpp
new file mode 100644
index 00000000..0f6f04c2
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/serialport-win.cpp
@@ -0,0 +1,1056 @@
+/* $Id: serialport-win.cpp $ */
+/** @file
+ * IPRT - Serial Port API, Windows Implementation.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/serialport.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/cdefs.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+
+#include <iprt/win/windows.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal serial port state.
+ */
+typedef struct RTSERIALPORTINTERNAL
+{
+ /** Magic value (RTSERIALPORT_MAGIC). */
+ uint32_t u32Magic;
+ /** Flags given while opening the serial port. */
+ uint32_t fOpenFlags;
+ /** The device handle. */
+ HANDLE hDev;
+ /** The overlapped write structure. */
+ OVERLAPPED OverlappedWrite;
+ /** The overlapped read structure. */
+ OVERLAPPED OverlappedRead;
+ /** The overlapped I/O structure when waiting on events. */
+ OVERLAPPED OverlappedEvt;
+ /** The event handle to wait on for the overlapped event operations of the device. */
+ HANDLE hEvtDev;
+ /** The event handle to wait on for the overlapped write operations of the device. */
+ HANDLE hEvtWrite;
+ /** The event handle to wait on for the overlapped read operations of the device. */
+ HANDLE hEvtRead;
+ /** The event handle to wait on for waking up waiting threads externally. */
+ HANDLE hEvtIntr;
+ /** Events currently waited for. */
+ uint32_t fEvtMask;
+ /** Event mask as received by WaitCommEvent(). */
+ DWORD dwEventMask;
+ /** Flag whether a write is currently pending. */
+ bool fWritePending;
+ /** Event query pending. */
+ bool fEvtQueryPending;
+ /** Bounce buffer for writes. */
+ uint8_t *pbBounceBuf;
+ /** Amount of used buffer space. */
+ size_t cbBounceBufUsed;
+ /** Amount of allocated buffer space. */
+ size_t cbBounceBufAlloc;
+ /** The current active port config. */
+ DCB PortCfg;
+} RTSERIALPORTINTERNAL;
+/** Pointer to the internal serial port state. */
+typedef RTSERIALPORTINTERNAL *PRTSERIALPORTINTERNAL;
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The pipe buffer size we prefer. */
+#define RTSERIALPORT_NT_SIZE _32K
+
+
+
+/*********************************************************************************************************************************
+* Global variables *
+*********************************************************************************************************************************/
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Updatest the current event mask to wait for.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param fEvtMask The new event mask to change to.
+ */
+static int rtSerialPortWinUpdateEvtMask(PRTSERIALPORTINTERNAL pThis, uint32_t fEvtMask)
+{
+ DWORD dwEvtMask = EV_ERR;
+
+ if (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX)
+ dwEvtMask |= EV_RXCHAR;
+ if (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX)
+ dwEvtMask |= EV_TXEMPTY;
+ if (fEvtMask & RTSERIALPORT_EVT_F_BREAK_DETECTED)
+ dwEvtMask |= EV_BREAK;
+ if (fEvtMask & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED)
+ dwEvtMask |= EV_CTS | EV_DSR | EV_RING | EV_RLSD;
+
+ int rc = VINF_SUCCESS;
+ if (!SetCommMask(pThis->hDev, dwEvtMask))
+ rc = RTErrConvertFromWin32(GetLastError());
+ else
+ pThis->fEvtMask = fEvtMask;
+
+ return rc;
+}
+
+
+/**
+ * Tries to set the default config on the given serial port.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ */
+static int rtSerialPortSetDefaultCfg(PRTSERIALPORTINTERNAL pThis)
+{
+ if (!PurgeComm(pThis->hDev, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR))
+ return RTErrConvertFromWin32(GetLastError());
+
+ pThis->PortCfg.DCBlength = sizeof(pThis->PortCfg);
+ if (!GetCommState(pThis->hDev, &pThis->PortCfg))
+ return RTErrConvertFromWin32(GetLastError());
+
+ pThis->PortCfg.BaudRate = CBR_9600;
+ pThis->PortCfg.fBinary = TRUE;
+ pThis->PortCfg.fParity = TRUE;
+ pThis->PortCfg.fDtrControl = DTR_CONTROL_DISABLE;
+ pThis->PortCfg.ByteSize = 8;
+ pThis->PortCfg.Parity = NOPARITY;
+ pThis->PortCfg.fOutxCtsFlow = FALSE;
+ pThis->PortCfg.fOutxDsrFlow = FALSE;
+ pThis->PortCfg.fDsrSensitivity = FALSE;
+ pThis->PortCfg.fTXContinueOnXoff = TRUE;
+ pThis->PortCfg.fOutX = FALSE;
+ pThis->PortCfg.fInX = FALSE;
+ pThis->PortCfg.fErrorChar = FALSE;
+ pThis->PortCfg.fNull = FALSE;
+ pThis->PortCfg.fRtsControl = RTS_CONTROL_DISABLE;
+ pThis->PortCfg.fAbortOnError = FALSE;
+ pThis->PortCfg.wReserved = 0;
+ pThis->PortCfg.XonLim = 5;
+ pThis->PortCfg.XoffLim = 5;
+
+ int rc = VINF_SUCCESS;
+ if (!SetCommState(pThis->hDev, &pThis->PortCfg))
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set timeouts for non blocking mode.
+ * See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_commtimeouts .
+ */
+ COMMTIMEOUTS ComTimeouts;
+ RT_ZERO(ComTimeouts);
+ ComTimeouts.ReadIntervalTimeout = MAXDWORD;
+ if (!SetCommTimeouts(pThis->hDev, &ComTimeouts))
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+
+ return rc;
+}
+
+
+/**
+ * Common worker for handling I/O completion.
+ *
+ * This is used by RTSerialPortClose, RTSerialPortWrite and RTPipeSerialPortNB.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ */
+static int rtSerialPortWriteCheckCompletion(PRTSERIALPORTINTERNAL pThis)
+{
+ int rc = VINF_SUCCESS;
+ DWORD dwRc = WaitForSingleObject(pThis->OverlappedWrite.hEvent, 0);
+ if (dwRc == WAIT_OBJECT_0)
+ {
+ DWORD cbWritten = 0;
+ if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedWrite, &cbWritten, TRUE))
+ {
+ for (;;)
+ {
+ if (cbWritten >= pThis->cbBounceBufUsed)
+ {
+ pThis->fWritePending = false;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* resubmit the remainder of the buffer - can this actually happen? */
+ memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
+ rc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(rc == TRUE);
+ if (!WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
+ &cbWritten, &pThis->OverlappedWrite))
+ {
+ if (GetLastError() == ERROR_IO_PENDING)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fWritePending = false;
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ break;
+ }
+ Assert(cbWritten > 0);
+ }
+ }
+ else
+ {
+ pThis->fWritePending = false;
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else if (dwRc == WAIT_TIMEOUT)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fWritePending = false;
+ if (dwRc == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ return rc;
+}
+
+
+/**
+ * Processes the received Windows comm events and converts them to our format.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ * @param dwEventMask Event mask received.
+ * @param pfEvtsRecv Where to store the converted events.
+ */
+static int rtSerialPortEvtFlagsProcess(PRTSERIALPORTINTERNAL pThis, DWORD dwEventMask, uint32_t *pfEvtsRecv)
+{
+ int rc = VINF_SUCCESS;
+
+ if (dwEventMask & EV_RXCHAR)
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
+ if (dwEventMask & EV_TXEMPTY)
+ {
+ if (pThis->fWritePending)
+ {
+ rc = rtSerialPortWriteCheckCompletion(pThis);
+ if (rc == VINF_SUCCESS)
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
+ else
+ rc = VINF_SUCCESS;
+ }
+ else
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
+ }
+ if (dwEventMask & EV_BREAK)
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_BREAK_DETECTED;
+ if (dwEventMask & (EV_CTS | EV_DSR | EV_RING | EV_RLSD))
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
+
+ return rc;
+}
+
+
+/**
+ * The internal comm event wait worker.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ * @param msTimeout The timeout to wait for.
+ */
+static int rtSerialPortEvtWaitWorker(PRTSERIALPORTINTERNAL pThis, RTMSINTERVAL msTimeout)
+{
+ int rc = VINF_SUCCESS;
+ HANDLE ahWait[2];
+ ahWait[0] = pThis->hEvtDev;
+ ahWait[1] = pThis->hEvtIntr;
+
+ DWORD dwRet = WaitForMultipleObjects(2, ahWait, FALSE, msTimeout == RT_INDEFINITE_WAIT ? INFINITE : msTimeout);
+ if (dwRet == WAIT_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else if (dwRet == WAIT_FAILED)
+ rc = RTErrConvertFromWin32(GetLastError());
+ else if (dwRet == WAIT_OBJECT_0)
+ rc = VINF_SUCCESS;
+ else
+ {
+ Assert(dwRet == WAIT_OBJECT_0 + 1);
+ rc = VERR_INTERRUPTED;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags)
+{
+ AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER);
+ AssertReturn(RT_VALID_PTR(pszPortAddress) && *pszPortAddress != '\0', VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE),
+ VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSERIALPORT_MAGIC;
+ pThis->fOpenFlags = fFlags;
+ pThis->fEvtMask = 0;
+ pThis->fWritePending = false;
+ pThis->fEvtQueryPending = false;
+ pThis->pbBounceBuf = NULL;
+ pThis->cbBounceBufUsed = 0;
+ pThis->cbBounceBufAlloc = 0;
+ RT_ZERO(pThis->OverlappedEvt);
+ RT_ZERO(pThis->OverlappedWrite);
+ RT_ZERO(pThis->OverlappedRead);
+ pThis->hEvtDev = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (pThis->hEvtDev)
+ {
+ pThis->OverlappedEvt.hEvent = pThis->hEvtDev,
+ pThis->hEvtIntr = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (pThis->hEvtIntr)
+ {
+ pThis->hEvtWrite = CreateEvent(NULL, TRUE, TRUE, NULL);
+ if (pThis->hEvtWrite)
+ {
+ pThis->OverlappedWrite.hEvent = pThis->hEvtWrite;
+ pThis->hEvtRead = CreateEvent(NULL, TRUE, TRUE, NULL);
+ if (pThis->hEvtRead)
+ {
+ pThis->OverlappedRead.hEvent = pThis->hEvtRead;
+ DWORD fWinFlags = 0;
+
+ if (fFlags & RTSERIALPORT_OPEN_F_WRITE)
+ fWinFlags |= GENERIC_WRITE;
+ if (fFlags & RTSERIALPORT_OPEN_F_READ)
+ fWinFlags |= GENERIC_READ;
+
+ pThis->hDev = CreateFile(pszPortAddress,
+ fWinFlags,
+ 0, /* Must be opened with exclusive access. */
+ NULL, /* No SECURITY_ATTRIBUTES structure. */
+ OPEN_EXISTING, /* Must use OPEN_EXISTING. */
+ FILE_FLAG_OVERLAPPED, /* Overlapped I/O. */
+ NULL);
+ if (pThis->hDev)
+ {
+ rc = rtSerialPortSetDefaultCfg(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ *phSerialPort = pThis;
+ return rc;
+ }
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ CloseHandle(pThis->hEvtRead);
+ }
+
+ CloseHandle(pThis->hEvtWrite);
+ }
+
+ CloseHandle(pThis->hEvtIntr);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ CloseHandle(pThis->hEvtDev);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ if (pThis == NIL_RTSERIALPORT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
+
+ if (pThis->fWritePending)
+ rtSerialPortWriteCheckCompletion(pThis);
+
+ CloseHandle(pThis->hDev);
+ CloseHandle(pThis->hEvtDev);
+ CloseHandle(pThis->hEvtWrite);
+ CloseHandle(pThis->hEvtRead);
+ CloseHandle(pThis->hEvtIntr);
+ pThis->hDev = NULL;
+ pThis->hEvtDev = NULL;
+ pThis->hEvtWrite = NULL;
+ pThis->hEvtRead = NULL;
+ pThis->hEvtIntr = NULL;
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1);
+
+ return (RTHCINTPTR)pThis->hDev;
+}
+
+
+RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
+
+ /*
+ * Kick of an overlapped read.
+ */
+ int rc = VINF_SUCCESS;
+ uint8_t *pbBuf = (uint8_t *)pvBuf;
+
+ while ( cbToRead > 0
+ && RT_SUCCESS(rc))
+ {
+ BOOL fSucc = ResetEvent(pThis->OverlappedRead.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
+ DWORD cbRead = 0;
+ if (ReadFile(pThis->hDev, pbBuf,
+ cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
+ &cbRead, &pThis->OverlappedRead))
+ {
+ if (pcbRead)
+ {
+ *pcbRead = cbRead;
+ break;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ DWORD dwWait = WaitForSingleObject(pThis->OverlappedRead.hEvent, INFINITE);
+ if (dwWait == WAIT_OBJECT_0)
+ {
+ if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedRead, &cbRead, TRUE /*fWait*/))
+ {
+ if (pcbRead)
+ {
+ *pcbRead = cbRead;
+ break;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ Assert(dwWait == WAIT_FAILED);
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ if (RT_SUCCESS(rc))
+ {
+ cbToRead -= cbRead;
+ pbBuf += cbRead;
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
+
+ *pcbRead = 0;
+
+ /* Check whether there is data waiting in the input queue. */
+ int rc = VINF_SUCCESS;
+ COMSTAT ComStat; RT_ZERO(ComStat);
+ if (ClearCommError(pThis->hDev, NULL, &ComStat))
+ {
+ if (ComStat.cbInQue > 0)
+ {
+ DWORD dwToRead = RT_MIN(ComStat.cbInQue, (DWORD)cbToRead);
+ /* Kick of an overlapped read. It should return immediately */
+ BOOL fSucc = ResetEvent(pThis->OverlappedRead.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
+ DWORD cbRead = 0;
+ if ( cbToRead == 0
+ || ReadFile(pThis->hDev, pvBuf, dwToRead,
+ &cbRead, &pThis->OverlappedRead))
+ *pcbRead = cbRead;
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ /* This shouldn't actually happen, so turn this into a synchronous read. */
+ if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedRead, &cbRead, TRUE /*fWait*/))
+ *pcbRead = cbRead;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
+
+ /* If I/O is pending, check if it has completed. */
+ int rc = VINF_SUCCESS;
+ if (pThis->fWritePending)
+ rc = rtSerialPortWriteCheckCompletion(pThis);
+ if (rc == VINF_SUCCESS)
+ {
+ const uint8_t *pbBuf = (const uint8_t *)pvBuf;
+
+ while ( cbToWrite > 0
+ && RT_SUCCESS(rc))
+ {
+ BOOL fSucc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
+ DWORD cbWritten = 0;
+ if (WriteFile(pThis->hDev, pbBuf,
+ cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0,
+ &cbWritten, &pThis->OverlappedWrite))
+ {
+ if (pcbWritten)
+ {
+ *pcbWritten = cbWritten;
+ break;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ DWORD dwWait = WaitForSingleObject(pThis->OverlappedWrite.hEvent, INFINITE);
+ if (dwWait == WAIT_OBJECT_0)
+ {
+ if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedWrite, &cbWritten, TRUE /*fWait*/))
+ {
+ if (pcbWritten)
+ {
+ *pcbWritten = cbWritten;
+ break;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ Assert(dwWait == WAIT_FAILED);
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ if (RT_SUCCESS(rc))
+ {
+ cbToWrite -= cbWritten;
+ pbBuf += cbWritten;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
+
+ /* If I/O is pending, check if it has completed. */
+ int rc = VINF_SUCCESS;
+ if (pThis->fWritePending)
+ rc = rtSerialPortWriteCheckCompletion(pThis);
+ if (rc == VINF_SUCCESS)
+ {
+ Assert(!pThis->fWritePending);
+
+ /* Do the bounce buffering. */
+ if ( pThis->cbBounceBufAlloc < cbToWrite
+ && pThis->cbBounceBufAlloc < RTSERIALPORT_NT_SIZE)
+ {
+ if (cbToWrite > RTSERIALPORT_NT_SIZE)
+ cbToWrite = RTSERIALPORT_NT_SIZE;
+ void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
+ if (pv)
+ {
+ pThis->pbBounceBuf = (uint8_t *)pv;
+ pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else if (cbToWrite > RTSERIALPORT_NT_SIZE)
+ cbToWrite = RTSERIALPORT_NT_SIZE;
+ if (RT_SUCCESS(rc) && cbToWrite)
+ {
+ memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
+ pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
+
+ /* Submit the write. */
+ rc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(rc == TRUE);
+ DWORD cbWritten = 0;
+ if (WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
+ &cbWritten, &pThis->OverlappedWrite))
+ {
+ *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ *pcbWritten = cbToWrite;
+ pThis->fWritePending = true;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (RT_SUCCESS(rc))
+ *pcbWritten = 0;
+ }
+ else if (RT_SUCCESS(rc))
+ *pcbWritten = 0;
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ pCfg->uBaudRate = pThis->PortCfg.BaudRate;
+ switch (pThis->PortCfg.Parity)
+ {
+ case NOPARITY:
+ pCfg->enmParity = RTSERIALPORTPARITY_NONE;
+ break;
+ case EVENPARITY:
+ pCfg->enmParity = RTSERIALPORTPARITY_EVEN;
+ break;
+ case ODDPARITY:
+ pCfg->enmParity = RTSERIALPORTPARITY_ODD;
+ break;
+ case MARKPARITY:
+ pCfg->enmParity = RTSERIALPORTPARITY_MARK;
+ break;
+ case SPACEPARITY:
+ pCfg->enmParity = RTSERIALPORTPARITY_SPACE;
+ break;
+ default:
+ AssertFailed();
+ return VERR_INTERNAL_ERROR;
+ }
+
+ switch (pThis->PortCfg.ByteSize)
+ {
+ case 5:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
+ break;
+ case 6:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
+ break;
+ case 7:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
+ break;
+ case 8:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
+ break;
+ default:
+ AssertFailed();
+ return VERR_INTERNAL_ERROR;
+ }
+
+ switch (pThis->PortCfg.StopBits)
+ {
+ case ONESTOPBIT:
+ pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
+ break;
+ case ONE5STOPBITS:
+ pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
+ break;
+ case TWOSTOPBITS:
+ pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
+ break;
+ default:
+ AssertFailed();
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ RT_NOREF(pErrInfo);
+
+ DCB DcbNew;
+ memcpy(&DcbNew, &pThis->PortCfg, sizeof(DcbNew));
+ DcbNew.BaudRate = pCfg->uBaudRate;
+
+ switch (pCfg->enmParity)
+ {
+ case RTSERIALPORTPARITY_NONE:
+ DcbNew.Parity = NOPARITY;
+ break;
+ case RTSERIALPORTPARITY_EVEN:
+ DcbNew.Parity = EVENPARITY;
+ break;
+ case RTSERIALPORTPARITY_ODD:
+ DcbNew.Parity = ODDPARITY;
+ break;
+ case RTSERIALPORTPARITY_MARK:
+ DcbNew.Parity = MARKPARITY;
+ break;
+ case RTSERIALPORTPARITY_SPACE:
+ DcbNew.Parity = SPACEPARITY;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ switch (pCfg->enmDataBitCount)
+ {
+ case RTSERIALPORTDATABITS_5BITS:
+ DcbNew.ByteSize = 5;
+ break;
+ case RTSERIALPORTDATABITS_6BITS:
+ DcbNew.ByteSize = 6;
+ break;
+ case RTSERIALPORTDATABITS_7BITS:
+ DcbNew.ByteSize = 7;
+ break;
+ case RTSERIALPORTDATABITS_8BITS:
+ DcbNew.ByteSize = 8;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ switch (pCfg->enmStopBitCount)
+ {
+ case RTSERIALPORTSTOPBITS_ONE:
+ DcbNew.StopBits = ONESTOPBIT;
+ break;
+ case RTSERIALPORTSTOPBITS_ONEPOINTFIVE:
+ AssertReturn(pCfg->enmDataBitCount == RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER);
+ DcbNew.StopBits = ONE5STOPBITS;
+ break;
+ case RTSERIALPORTSTOPBITS_TWO:
+ AssertReturn(pCfg->enmDataBitCount != RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER);
+ DcbNew.StopBits = TWOSTOPBITS;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ int rc = VINF_SUCCESS;
+ if (!SetCommState(pThis->hDev, &DcbNew))
+ rc = RTErrConvertFromWin32(GetLastError());
+ else
+ memcpy(&pThis->PortCfg, &DcbNew, sizeof(DcbNew));
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv,
+ RTMSINTERVAL msTimeout)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
+
+ *pfEvtsRecv = 0;
+
+ int rc = VINF_SUCCESS;
+ if (fEvtMask != pThis->fEvtMask)
+ {
+ rc = rtSerialPortWinUpdateEvtMask(pThis, fEvtMask);
+ if ( RT_SUCCESS(rc)
+ && pThis->fEvtQueryPending)
+ {
+ /*
+ * Setting a new event mask lets the WaitCommEvent() call finish immediately,
+ * so clean up and process any events here.
+ */
+ rc = rtSerialPortEvtWaitWorker(pThis, 1);
+ AssertRC(rc);
+
+ if (pThis->dwEventMask != 0)
+ {
+ pThis->fEvtQueryPending = false;
+ return rtSerialPortEvtFlagsProcess(pThis, pThis->dwEventMask, pfEvtsRecv);
+ }
+ }
+ }
+
+ /*
+ * EV_RXCHAR is triggered only if a byte is received after the event mask is set,
+ * not if there is already something in the input buffer. Thatswhy we check the input
+ * buffer for any stored data and the output buffer whether it is empty and return
+ * the appropriate flags.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ COMSTAT ComStat; RT_ZERO(ComStat);
+ if (!ClearCommError(pThis->hDev, NULL, &ComStat))
+ return RTErrConvertFromWin32(GetLastError());
+
+ /* Check whether data is already waiting in the input buffer. */
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX)
+ && ComStat.cbInQue > 0)
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
+
+ /* Check whether the output buffer is empty. */
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX)
+ && ComStat.cbOutQue == 0)
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
+
+ /* Return if there is at least one event. */
+ if (*pfEvtsRecv != 0)
+ return VINF_SUCCESS;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Set up a new event wait if there is none pending. */
+ if (!pThis->fEvtQueryPending)
+ {
+ RT_ZERO(pThis->OverlappedEvt);
+ pThis->OverlappedEvt.hEvent = pThis->hEvtDev;
+
+ pThis->dwEventMask = 0;
+ pThis->fEvtQueryPending = true;
+ if (!WaitCommEvent(pThis->hDev, &pThis->dwEventMask, &pThis->OverlappedEvt))
+ {
+ DWORD dwRet = GetLastError();
+ if (dwRet == ERROR_IO_PENDING)
+ rc = VINF_SUCCESS;
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ pThis->fEvtQueryPending = false;
+ }
+ }
+ else
+ pThis->fEvtQueryPending = false;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && pThis->fEvtQueryPending)
+ rc = rtSerialPortEvtWaitWorker(pThis, msTimeout);
+
+ if (RT_SUCCESS(rc))
+ {
+ pThis->fEvtQueryPending = false;
+ rc = rtSerialPortEvtFlagsProcess(pThis, pThis->dwEventMask, pfEvtsRecv);
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ if (!SetEvent(pThis->hEvtIntr))
+ return RTErrConvertFromWin32(GetLastError());
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ BOOL fSucc = FALSE;
+ if (fSet)
+ fSucc = SetCommBreak(pThis->hDev);
+ else
+ fSucc = ClearCommBreak(pThis->hDev);
+
+ int rc = VINF_SUCCESS;
+ if (!fSucc)
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ BOOL fSucc = TRUE;
+ if (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
+ fSucc = EscapeCommFunction(pThis->hDev, SETDTR);
+ if ( fSucc
+ && (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS))
+ fSucc = EscapeCommFunction(pThis->hDev, SETRTS);
+
+ if ( fSucc
+ && (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
+ fSucc = EscapeCommFunction(pThis->hDev, CLRDTR);
+ if ( fSucc
+ && (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS))
+ fSucc = EscapeCommFunction(pThis->hDev, CLRRTS);
+
+ int rc = VINF_SUCCESS;
+ if (!fSucc)
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
+
+ *pfStsLines = 0;
+
+ int rc = VINF_SUCCESS;
+ DWORD fStsLinesQueried = 0;
+
+ /* Get the new state */
+ if (GetCommModemStatus(pThis->hDev, &fStsLinesQueried))
+ {
+ *pfStsLines |= (fStsLinesQueried & MS_RLSD_ON) ? RTSERIALPORT_STS_LINE_DCD : 0;
+ *pfStsLines |= (fStsLinesQueried & MS_RING_ON) ? RTSERIALPORT_STS_LINE_RI : 0;
+ *pfStsLines |= (fStsLinesQueried & MS_DSR_ON) ? RTSERIALPORT_STS_LINE_DSR : 0;
+ *pfStsLines |= (fStsLinesQueried & MS_CTS_ON) ? RTSERIALPORT_STS_LINE_CTS : 0;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/shmem-win.cpp b/src/VBox/Runtime/r3/win/shmem-win.cpp
new file mode 100644
index 00000000..b97fc796
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/shmem-win.cpp
@@ -0,0 +1,473 @@
+/* $Id: shmem-win.cpp $ */
+/** @file
+ * IPRT - Named shared memory object, Windows Implementation.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/shmem.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/cdefs.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include "internal/magics.h"
+#include "internal/path.h"
+#include "internal-r3-win.h" /* For g_enmWinVer + kRTWinOSType_XXX */
+
+/*
+ * Define values ourselves in case the compiling host is too old.
+ * See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createfilemappinga
+ * for when these were introduced.
+ */
+#ifndef PAGE_EXECUTE_READ
+# define PAGE_EXECUTE_READ 0x20
+#endif
+#ifndef PAGE_EXECUTE_READWRITE
+# define PAGE_EXECUTE_READWRITE 0x40
+#endif
+#ifndef PAGE_EXECUTE_WRITECOPY
+# define PAGE_EXECUTE_WRITECOPY 0x80
+#endif
+#ifndef FILE_MAP_EXECUTE
+# define FILE_MAP_EXECUTE 0x20
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Shared memory object mapping descriptor.
+ */
+typedef struct RTSHMEMMAPPINGDESC
+{
+ /** Number of references held to this mapping, 0 if the descriptor is free. */
+ volatile uint32_t cMappings;
+ /** Pointer to the region mapping. */
+ void *pvMapping;
+ /** Start offset */
+ size_t offRegion;
+ /** Size of the region. */
+ size_t cbRegion;
+ /** Access flags for this region .*/
+ uint32_t fFlags;
+} RTSHMEMMAPPINGDESC;
+/** Pointer to a shared memory object mapping descriptor. */
+typedef RTSHMEMMAPPINGDESC *PRTSHMEMMAPPINGDESC;
+/** Pointer to a constant shared memory object mapping descriptor. */
+typedef const RTSHMEMMAPPINGDESC *PCRTSHMEMMAPPINGDESC;
+
+
+/**
+ * Internal shared memory object state.
+ */
+typedef struct RTSHMEMINT
+{
+ /** Magic value (RTSHMEM_MAGIC). */
+ uint32_t u32Magic;
+ /** Flag whether this instance created the named shared memory object. */
+ bool fCreate;
+ /** Handle to the underlying mapping object. */
+ HANDLE hShmObj;
+ /** Size of the mapping object in bytes. */
+ size_t cbMax;
+ /** Overall number of mappings active for this shared memory object. */
+ volatile uint32_t cMappings;
+ /** Maximum number of mapping descriptors allocated. */
+ uint32_t cMappingDescsMax;
+ /** Number of mapping descriptors used. */
+ volatile uint32_t cMappingDescsUsed;
+ /** Array of mapping descriptors - variable in size. */
+ RTSHMEMMAPPINGDESC aMappingDescs[1];
+} RTSHMEMINT;
+/** Pointer to the internal shared memory object state. */
+typedef RTSHMEMINT *PRTSHMEMINT;
+
+
+
+
+/**
+ * Returns a mapping descriptor matching the given region properties or NULL if none was found.
+ *
+ * @returns Pointer to the matching mapping descriptor or NULL if not found.
+ * @param pThis Pointer to the shared memory object instance.
+ * @param offRegion Offset into the shared memory object to start mapping at.
+ * @param cbRegion Size of the region to map.
+ * @param fFlags Desired properties of the mapped region, combination of RTSHMEM_MAP_F_* defines.
+ */
+DECLINLINE(PRTSHMEMMAPPINGDESC) rtShMemMappingDescFindByProp(PRTSHMEMINT pThis, size_t offRegion, size_t cbRegion, uint32_t fFlags)
+{
+ for (uint32_t i = 0; i < pThis->cMappingDescsMax; i++)
+ {
+ if ( pThis->aMappingDescs[i].offRegion == offRegion
+ && pThis->aMappingDescs[i].cbRegion == cbRegion
+ && pThis->aMappingDescs[i].fFlags == fFlags)
+ return &pThis->aMappingDescs[i];
+ }
+
+ return NULL;
+}
+
+
+RTDECL(int) RTShMemOpen(PRTSHMEM phShMem, const char *pszName, uint32_t fFlags, size_t cbMax, uint32_t cMappingsHint)
+{
+ AssertPtrReturn(phShMem, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSHMEM_O_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(cMappingsHint < 64, VERR_OUT_OF_RANGE);
+ AssertReturn(cbMax > 0 || !(fFlags & RTSHMEM_O_F_CREATE), VERR_NOT_SUPPORTED);
+
+ if (fFlags & RTSHMEM_O_F_TRUNCATE)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * The executable access was introduced with Windows XP SP2 and Windows Server 2003 SP1,
+ * PAGE_EXECUTE_WRITECOPY was not available until Windows Vista SP1.
+ * Allow execute mappings only starting from Windows 7 to keep the checks simple here (lazy coder).
+ */
+ if ( (fFlags & RTSHMEM_O_F_MAYBE_EXEC)
+ && g_enmWinVer < kRTWinOSType_7)
+ return VERR_NOT_SUPPORTED;
+
+ cMappingsHint = cMappingsHint == 0 ? 5 : cMappingsHint;
+ int rc = VINF_SUCCESS;
+ PRTSHMEMINT pThis = (PRTSHMEMINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTSHMEMINT, aMappingDescs[cMappingsHint]));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->u32Magic = RTSHMEM_MAGIC;
+ /*pThis->fCreate = false; */
+ /*pThis->cMappings = 0; */
+ pThis->cMappingDescsMax = cMappingsHint;
+ /*pThis->cMappingDescsUsed = 0; */
+ /* Construct the filename, always use the local namespace, global requires special privileges. */
+ char szName[RTPATH_MAX];
+ ssize_t cch = RTStrPrintf2(&szName[0], sizeof(szName), "Local\\%s", pszName);
+ if (cch > 0)
+ {
+ PRTUTF16 pwszName = NULL;
+ rc = RTStrToUtf16Ex(&szName[0], RTSTR_MAX, &pwszName, 0, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (fFlags & RTSHMEM_O_F_CREATE)
+ {
+#if HC_ARCH_BITS == 64
+ DWORD dwSzMaxHigh = cbMax >> 32;
+#elif HC_ARCH_BITS == 32
+ DWORD dwSzMaxHigh = 0;
+#else
+# error "Port me"
+#endif
+ DWORD dwSzMaxLow = cbMax & UINT32_C(0xffffffff);
+ DWORD fProt = 0;
+
+ if (fFlags & RTSHMEM_O_F_MAYBE_EXEC)
+ {
+ if ((fFlags & RTSHMEM_O_F_READWRITE) == RTSHMEM_O_F_READ)
+ fProt |= PAGE_EXECUTE_READ;
+ else
+ fProt |= PAGE_EXECUTE_READWRITE;
+ }
+ else
+ {
+ if ((fFlags & RTSHMEM_O_F_READWRITE) == RTSHMEM_O_F_READ)
+ fProt |= PAGE_READONLY;
+ else
+ fProt |= PAGE_READWRITE;
+ }
+ pThis->hShmObj = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, fProt,
+ dwSzMaxHigh, dwSzMaxLow, pwszName);
+ }
+ else
+ {
+ DWORD fProt = SECTION_QUERY;
+ if (fFlags & RTSHMEM_O_F_MAYBE_EXEC)
+ fProt |= FILE_MAP_EXECUTE;
+ if (fFlags & RTSHMEM_O_F_READ)
+ fProt |= FILE_MAP_READ;
+ if (fFlags & RTSHMEM_O_F_WRITE)
+ fProt |= FILE_MAP_WRITE;
+
+ pThis->hShmObj = OpenFileMappingW(fProt, FALSE, pwszName);
+ }
+ RTUtf16Free(pwszName);
+ if (pThis->hShmObj != NULL)
+ {
+ *phShMem = pThis;
+ return VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemClose(RTSHMEM hShMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->cMappings, VERR_INVALID_STATE);
+
+ int rc = VINF_SUCCESS;
+ if (CloseHandle(pThis->hShmObj))
+ {
+ pThis->u32Magic = RTSHMEM_MAGIC_DEAD;
+ RTMemFree(pThis);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemDelete(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(*pszName != '\0', VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(uint32_t) RTShMemRefCount(RTSHMEM hShMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, 0);
+
+ return pThis->cMappings;
+}
+
+
+RTDECL(int) RTShMemSetSize(RTSHMEM hShMem, size_t cbMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->cMappings, VERR_INVALID_STATE);
+ AssertReturn(cbMem, VERR_NOT_SUPPORTED);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTShMemQuerySize(RTSHMEM hShMem, size_t *pcbMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pcbMem, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ SECTION_BASIC_INFORMATION SecInf;
+ SIZE_T cbRet;
+ NTSTATUS rcNt = NtQuerySection(pThis->hShmObj, SectionBasicInformation, &SecInf, sizeof(SecInf), &cbRet);
+ if (NT_SUCCESS(rcNt))
+ {
+ AssertReturn(cbRet == sizeof(SecInf), VERR_INTERNAL_ERROR);
+#if HC_ARCH_BITS == 32
+ AssertReturn(SecInf.MaximumSize.HighPart == 0, VERR_INTERNAL_ERROR_2);
+ *pcbMem = SecInf.MaximumSize.LowPart;
+#elif HC_ARCH_BITS == 64
+ *pcbMem = SecInf.MaximumSize.QuadPart;
+#else
+# error "Port me"
+#endif
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemMapRegion(RTSHMEM hShMem, size_t offRegion, size_t cbRegion, uint32_t fFlags, void **ppv)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(ppv, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSHMEM_MAP_F_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /* See comment in RTShMemOpen(). */
+ if ( (fFlags & RTSHMEM_MAP_F_EXEC)
+ && g_enmWinVer < kRTWinOSType_7)
+ return VERR_NOT_SUPPORTED;
+
+ /* Try to find a mapping with compatible parameters first. */
+ PRTSHMEMMAPPINGDESC pMappingDesc = NULL;
+ for (uint32_t iTry = 0; iTry < 10; iTry++)
+ {
+ pMappingDesc = rtShMemMappingDescFindByProp(pThis, offRegion, cbRegion, fFlags);
+ if (!pMappingDesc)
+ break;
+
+ /* Increase the mapping count and check that the region is still accessible by us. */
+ if ( ASMAtomicIncU32(&pMappingDesc->cMappings) > 1
+ && pMappingDesc->offRegion == offRegion
+ && pMappingDesc->cbRegion == cbRegion
+ && pMappingDesc->fFlags == fFlags)
+ break;
+ /* Mapping was freed inbetween, next round. */
+ }
+
+ int rc = VINF_SUCCESS;
+ if (!pMappingDesc)
+ {
+ /* Find an empty region descriptor and map the region. */
+ for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++)
+ {
+ if (!pThis->aMappingDescs[i].cMappings)
+ {
+ pMappingDesc = &pThis->aMappingDescs[i];
+
+ /* Try to grab this one. */
+ if (ASMAtomicIncU32(&pMappingDesc->cMappings) == 1)
+ break;
+
+ /* Somebody raced us, drop reference and continue. */
+ ASMAtomicDecU32(&pMappingDesc->cMappings);
+ pMappingDesc = NULL;
+ }
+ }
+
+ if (RT_LIKELY(pMappingDesc))
+ {
+ /* Try to map it. */
+ DWORD fProt = 0;
+ DWORD offLow = offRegion & UINT32_C(0xffffffff);
+#if HC_ARCH_BITS == 64
+ DWORD offHigh = offRegion >> 32;
+#elif HC_ARCH_BITS == 32
+ DWORD offHigh = 0;
+#else
+# error "Port me"
+#endif
+ if (fFlags & RTSHMEM_MAP_F_READ)
+ fProt |= FILE_MAP_READ;
+ if (fFlags & RTSHMEM_MAP_F_WRITE)
+ fProt |= FILE_MAP_WRITE;
+ if (fFlags & RTSHMEM_MAP_F_EXEC)
+ fProt |= FILE_MAP_EXECUTE;
+ if (fFlags & RTSHMEM_MAP_F_COW)
+ fProt |= FILE_MAP_COPY;
+
+ void *pv = MapViewOfFile(pThis->hShmObj, fProt, offHigh, offLow, cbRegion);
+ if (pv != NULL)
+ {
+ pMappingDesc->pvMapping = pv;
+ pMappingDesc->offRegion = offRegion;
+ pMappingDesc->cbRegion = cbRegion;
+ pMappingDesc->fFlags = fFlags;
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ ASMAtomicDecU32(&pMappingDesc->cMappings);
+ }
+ }
+ else
+ rc = VERR_SHMEM_MAXIMUM_MAPPINGS_REACHED;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppv = pMappingDesc->pvMapping;
+ ASMAtomicIncU32(&pThis->cMappings);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemUnmapRegion(RTSHMEM hShMem, void *pv)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pv, VERR_INVALID_PARAMETER);
+
+ /* Find the mapping descriptor by the given region address. */
+ PRTSHMEMMAPPINGDESC pMappingDesc = NULL;
+ for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++)
+ {
+ if (pThis->aMappingDescs[i].pvMapping == pv)
+ {
+ pMappingDesc = &pThis->aMappingDescs[i];
+ break;
+ }
+ }
+
+ AssertPtrReturn(pMappingDesc, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ if (!ASMAtomicDecU32(&pMappingDesc->cMappings))
+ {
+ /* Last mapping of this region was unmapped, so do the real unmapping now. */
+ if (UnmapViewOfFile(pv))
+ {
+ ASMAtomicDecU32(&pThis->cMappingDescsUsed);
+ ASMAtomicDecU32(&pThis->cMappings);
+ }
+ else
+ {
+ ASMAtomicIncU32(&pMappingDesc->cMappings);
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/symlink-win.cpp b/src/VBox/Runtime/r3/win/symlink-win.cpp
new file mode 100644
index 00000000..a0c5f1e2
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/symlink-win.cpp
@@ -0,0 +1,367 @@
+/* $Id: symlink-win.cpp $ */
+/** @file
+ * IPRT - Symbolic Links, Windows.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYMLINK
+#include <iprt/win/windows.h>
+
+#include <iprt/symlink.h>
+#include "internal-r3-win.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include "internal/path.h"
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct MY_REPARSE_DATA_BUFFER
+{
+ ULONG ReparseTag;
+#define MY_IO_REPARSE_TAG_SYMLINK 0xa000000c
+#define MY_IO_REPARSE_TAG_MOUNT_POINT 0xa0000003
+
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union
+ {
+ struct
+ {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+#define MY_SYMLINK_FLAG_RELATIVE 1
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct
+ {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct
+ {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+} MY_REPARSE_DATA_BUFFER;
+#define MY_FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+
+RTDECL(bool) RTSymlinkExists(const char *pszSymlink)
+{
+ bool fRc = false;
+ RTFSOBJINFO ObjInfo;
+ int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode);
+
+ LogFlow(("RTSymlinkExists(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
+ return fRc;
+}
+
+
+RTDECL(bool) RTSymlinkIsDangling(const char *pszSymlink)
+{
+ bool fRc = false;
+ RTFSOBJINFO ObjInfo;
+ int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ {
+ fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode);
+ if (fRc)
+ {
+ rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ fRc = !RT_SUCCESS_NP(rc);
+ }
+ }
+
+ LogFlow(("RTSymlinkIsDangling(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
+ return fRc;
+}
+
+
+RTDECL(int) RTSymlinkCreate(const char *pszSymlink, const char *pszTarget, RTSYMLINKTYPE enmType, uint32_t fCreate)
+{
+ /*
+ * Validate the input.
+ */
+ AssertReturn(enmType > RTSYMLINKTYPE_INVALID && enmType < RTSYMLINKTYPE_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszSymlink, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszTarget, VERR_INVALID_POINTER);
+ RT_NOREF_PV(fCreate);
+
+ /*
+ * Resolve the API.
+ */
+ typedef BOOLEAN (WINAPI *PFNCREATESYMBOLICLINKW)(LPCWSTR, LPCWSTR, DWORD);
+ static PFNCREATESYMBOLICLINKW s_pfnCreateSymbolicLinkW = NULL;
+ static bool s_fTried = FALSE;
+ if (!s_fTried)
+ {
+ PFNCREATESYMBOLICLINKW pfn = (PFNCREATESYMBOLICLINKW)GetProcAddress(g_hModKernel32, "CreateSymbolicLinkW");
+ if (pfn)
+ s_pfnCreateSymbolicLinkW = pfn;
+ s_fTried = true;
+ }
+ if (!s_pfnCreateSymbolicLinkW)
+ {
+ LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns VERR_NOT_SUPPORTED - Windows API not found\n",
+ pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Convert the paths.
+ */
+ PRTUTF16 pwszNativeSymlink;
+ int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszNativeTarget;
+ rc = RTPathWinFromUtf8(&pwszNativeTarget, pszTarget, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /* The link target path must use backslashes to work reliably. */
+ RTUTF16 wc;
+ PRTUTF16 pwsz = pwszNativeTarget;
+ while ((wc = *pwsz) != '\0')
+ {
+ if (wc == '/')
+ *pwsz = '\\';
+ pwsz++;
+ }
+
+ /*
+ * Massage the target path, determin the link type.
+ */
+ size_t cchTarget = strlen(pszTarget);
+ size_t cchVolSpecTarget = rtPathVolumeSpecLen(pszTarget);
+#if 0 /* looks like this isn't needed after all. That makes everything much simper :-) */
+ if ( cchTarget > RT_MIN(cchVolSpecTarget, 1)
+ && RTPATH_IS_SLASH(pszTarget[cchTarget - 1]))
+ {
+ size_t cwcNativeTarget = RTUtf16Len(pwszNativeTarget);
+ size_t offFromEnd = 1;
+ while ( offFromEnd < cchTarget
+ && cchTarget - offFromEnd >= cchVolSpecTarget
+ && RTPATH_IS_SLASH(pszTarget[cchTarget - offFromEnd]))
+ {
+ Assert(offFromEnd < cwcNativeTarget);
+ pwszNativeTarget[cwcNativeTarget - offFromEnd] = 0;
+ offFromEnd++;
+ }
+ }
+#endif
+
+ if (enmType == RTSYMLINKTYPE_UNKNOWN)
+ {
+ if ( cchTarget > cchVolSpecTarget
+ && RTPATH_IS_SLASH(pszTarget[cchTarget - 1]))
+ enmType = RTSYMLINKTYPE_DIR;
+ else if (cchVolSpecTarget)
+ {
+ /** @todo this is subject to sharing violations. */
+ DWORD dwAttr = GetFileAttributesW(pwszNativeTarget);
+ if ( dwAttr != INVALID_FILE_ATTRIBUTES
+ && (dwAttr & FILE_ATTRIBUTE_DIRECTORY))
+ enmType = RTSYMLINKTYPE_DIR;
+ }
+ else
+ {
+ /** @todo Join the symlink directory with the target and
+ * look up the attributes on that. -lazy bird. */
+ }
+ }
+
+ /*
+ * Create the link.
+ */
+ if (s_pfnCreateSymbolicLinkW(pwszNativeSymlink, pwszNativeTarget, enmType == RTSYMLINKTYPE_DIR))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTPathWinFree(pwszNativeTarget);
+ }
+ RTPathWinFree(pwszNativeSymlink);
+ }
+
+ LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns %Rrc\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTSymlinkDelete(const char *pszSymlink, uint32_t fDelete)
+{
+ RT_NOREF_PV(fDelete);
+
+ /*
+ * Convert the path.
+ */
+ PRTUTF16 pwszNativeSymlink;
+ int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We have to use different APIs depending on whether this is a
+ * directory or file link. This means we're subject to one more race
+ * than on posix at the moment. We could probably avoid this though,
+ * if we wanted to go talk with the native API layer below Win32...
+ */
+ DWORD dwAttr = GetFileAttributesW(pwszNativeSymlink);
+ if (dwAttr != INVALID_FILE_ATTRIBUTES)
+ {
+ if (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT)
+ {
+ BOOL fRc;
+ if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
+ fRc = RemoveDirectoryW(pwszNativeSymlink);
+ else
+ fRc = DeleteFileW(pwszNativeSymlink);
+ if (fRc)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_NOT_SYMLINK;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTPathWinFree(pwszNativeSymlink);
+ }
+
+ LogFlow(("RTSymlinkDelete(%p={%s}, %#x): returns %Rrc\n", pszSymlink, pszSymlink, fDelete, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead)
+{
+ RT_NOREF_PV(fRead);
+
+ char *pszMyTarget;
+ int rc = RTSymlinkReadA(pszSymlink, &pszMyTarget);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrCopy(pszTarget, cbTarget, pszMyTarget);
+ RTStrFree(pszMyTarget);
+ }
+ LogFlow(("RTSymlinkRead(%p={%s}): returns %Rrc\n", pszSymlink, pszSymlink, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTSymlinkReadA(const char *pszSymlink, char **ppszTarget)
+{
+ AssertPtr(ppszTarget);
+ PRTUTF16 pwszNativeSymlink;
+ int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hSymlink = CreateFileW(pwszNativeSymlink,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hSymlink != INVALID_HANDLE_VALUE)
+ {
+ DWORD cbReturned = 0;
+ union
+ {
+ MY_REPARSE_DATA_BUFFER Buf;
+ uint8_t abBuf[16*_1K + sizeof(WCHAR)];
+ } u;
+ if (DeviceIoControl(hSymlink,
+ MY_FSCTL_GET_REPARSE_POINT,
+ NULL /*pInBuffer */,
+ 0 /*cbInBuffer */,
+ &u.Buf,
+ sizeof(u) - sizeof(WCHAR),
+ &cbReturned,
+ NULL /*pOverlapped*/))
+ {
+ if (u.Buf.ReparseTag == MY_IO_REPARSE_TAG_SYMLINK)
+ {
+ PWCHAR pwszTarget = &u.Buf.SymbolicLinkReparseBuffer.PathBuffer[0];
+ pwszTarget += u.Buf.SymbolicLinkReparseBuffer.SubstituteNameOffset / 2;
+ pwszTarget[u.Buf.SymbolicLinkReparseBuffer.SubstituteNameLength / 2] = 0;
+ if ( !(u.Buf.SymbolicLinkReparseBuffer.Flags & MY_SYMLINK_FLAG_RELATIVE)
+ && pwszTarget[0] == '\\'
+ && pwszTarget[1] == '?'
+ && pwszTarget[2] == '?'
+ && pwszTarget[3] == '\\'
+ && pwszTarget[4] != 0
+ )
+ pwszTarget += 4;
+ rc = RTUtf16ToUtf8(pwszTarget, ppszTarget);
+ }
+ else
+ rc = VERR_NOT_SYMLINK;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ CloseHandle(hSymlink);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTPathWinFree(pwszNativeSymlink);
+ }
+
+ if (RT_SUCCESS(rc))
+ LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc *ppszTarget=%p:{%s}\n", pszSymlink, pszSymlink, ppszTarget, rc, *ppszTarget, *ppszTarget));
+ else
+ LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc\n", pszSymlink, pszSymlink, ppszTarget, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp b/src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp
new file mode 100644
index 00000000..30088667
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp
@@ -0,0 +1,68 @@
+/* $Id: system-get-nt-xxx-win.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryOSInfo, generic stub.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/win/windows.h>
+
+#include "internal-r3-win.h"
+#include <iprt/system.h>
+#include <iprt/assert.h>
+
+
+RTDECL(uint32_t) RTSystemGetNtBuildNo(void)
+{
+ Assert(g_WinOsInfoEx.dwOSVersionInfoSize > 0);
+ return g_WinOsInfoEx.dwBuildNumber;
+}
+
+
+RTDECL(uint64_t) RTSystemGetNtVersion(void)
+{
+ Assert(g_WinOsInfoEx.dwOSVersionInfoSize > 0);
+ return RTSYSTEM_MAKE_NT_VERSION(g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion, g_WinOsInfoEx.dwBuildNumber);
+}
+
+
+RTDECL(uint8_t) RTSystemGetNtProductType(void)
+{
+ Assert(g_WinOsInfoEx.dwOSVersionInfoSize > 0);
+ return g_WinOsInfoEx.wProductType; /* It's a byte, not a word as 'w' normally indicates. (Baka Maikurosofuto!) */
+}
+
diff --git a/src/VBox/Runtime/r3/win/thread-win.cpp b/src/VBox/Runtime/r3/win/thread-win.cpp
new file mode 100644
index 00000000..80bd3879
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/thread-win.cpp
@@ -0,0 +1,588 @@
+/* $Id: thread-win.cpp $ */
+/** @file
+ * IPRT - Threads, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <iprt/nt/nt-and-windows.h>
+
+#ifndef IPRT_NO_CRT
+# include <errno.h>
+# include <process.h>
+#endif
+
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/assert.h>
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include "internal/thread.h"
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** SetThreadDescription */
+typedef HRESULT (WINAPI *PFNSETTHREADDESCRIPTION)(HANDLE hThread, WCHAR *pwszName); /* Since W10 1607 */
+
+/** CoInitializeEx */
+typedef HRESULT (WINAPI *PFNCOINITIALIZEEX)(LPVOID, DWORD);
+/** CoUninitialize */
+typedef void (WINAPI *PFNCOUNINITIALIZE)(void);
+/** OleUninitialize */
+typedef void (WINAPI *PFNOLEUNINITIALIZE)(void);
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The TLS index allocated for storing the RTTHREADINT pointer. */
+static DWORD g_dwSelfTLS = TLS_OUT_OF_INDEXES;
+/** Pointer to SetThreadDescription (KERNEL32.DLL) if available. */
+static PFNSETTHREADDESCRIPTION g_pfnSetThreadDescription = NULL;
+
+/** Pointer to CoInitializeEx (OLE32.DLL / combase.dll) if available. */
+static PFNCOINITIALIZEEX volatile g_pfnCoInitializeEx = NULL;
+/** Pointer to CoUninitialize (OLE32.DLL / combase.dll) if available. */
+static PFNCOUNINITIALIZE volatile g_pfnCoUninitialize = NULL;
+/** Pointer to OleUninitialize (OLE32.DLL / combase.dll) if available. */
+static PFNOLEUNINITIALIZE volatile g_pfnOleUninitialize = NULL;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtThreadWinTellDebuggerThreadName(uint32_t idThread, const char *pszName);
+DECLINLINE(void) rtThreadWinSetThreadName(PRTTHREADINT pThread, DWORD idThread);
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ g_dwSelfTLS = TlsAlloc();
+ if (g_dwSelfTLS == TLS_OUT_OF_INDEXES)
+ return VERR_NO_TLS_FOR_SELF;
+
+ g_pfnSetThreadDescription = (PFNSETTHREADDESCRIPTION)GetProcAddress(g_hModKernel32, "SetThreadDescription");
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeReInitObtrusive(void)
+{
+ /* nothing to do here. */
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDetach(void)
+{
+ /*
+ * Deal with alien threads.
+ */
+ PRTTHREADINT pThread = (PRTTHREADINT)TlsGetValue(g_dwSelfTLS);
+ if ( pThread
+ && (pThread->fIntFlags & RTTHREADINT_FLAGS_ALIEN))
+ {
+ rtThreadTerminate(pThread, 0);
+ TlsSetValue(g_dwSelfTLS, NULL);
+ }
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ if (pThread == (PRTTHREADINT)TlsGetValue(g_dwSelfTLS))
+ TlsSetValue(g_dwSelfTLS, NULL);
+
+ if ((HANDLE)pThread->hThread != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle((HANDLE)pThread->hThread);
+ pThread->hThread = (uintptr_t)INVALID_HANDLE_VALUE;
+ }
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ if (!TlsSetValue(g_dwSelfTLS, pThread))
+ return VERR_FAILED_TO_SET_SELF_TLS;
+ rtThreadWinSetThreadName(pThread, GetCurrentThreadId());
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeInformDebugger(PRTTHREADINT pThread)
+{
+ rtThreadWinTellDebuggerThreadName((uint32_t)(uintptr_t)pThread->Core.Key, pThread->szName);
+}
+
+
+/**
+ * Communicates the thread name to the debugger, if we're begin debugged that
+ * is.
+ *
+ * See http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for debugger
+ * interface details.
+ *
+ * @param idThread The thread ID. UINT32_MAX for current thread.
+ * @param pszName The name.
+ */
+static void rtThreadWinTellDebuggerThreadName(uint32_t idThread, const char *pszName)
+{
+ struct
+ {
+ uint32_t uType;
+ const char *pszName;
+ uint32_t idThread;
+ uint32_t fFlags;
+ } Pkg = { 0x1000, pszName, idThread, 0 };
+ __try
+ {
+ RaiseException(0x406d1388, 0, sizeof(Pkg)/sizeof(ULONG_PTR), (ULONG_PTR *)&Pkg);
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {
+
+ }
+}
+
+
+/**
+ * Sets the thread name as best as we can.
+ */
+DECLINLINE(void) rtThreadWinSetThreadName(PRTTHREADINT pThread, DWORD idThread)
+{
+ if (g_pfnIsDebuggerPresent && g_pfnIsDebuggerPresent())
+ rtThreadWinTellDebuggerThreadName(idThread, &pThread->szName[0]);
+
+ /* The SetThreadDescription API introduced in windows 10 1607 / server 2016
+ allows setting the thread name while the debugger isn't attached. Works
+ with WinDbgX, VisualStudio 2017 v15.6+, and presumeably some recent windbg
+ version. */
+ if (g_pfnSetThreadDescription)
+ {
+ /* The name should be ASCII, so we just need to expand 'char' to 'WCHAR'. */
+ WCHAR wszName[RTTHREAD_NAME_LEN];
+ for (size_t i = 0; i < RTTHREAD_NAME_LEN; i++)
+ wszName[i] = pThread->szName[i];
+
+ HRESULT hrc = g_pfnSetThreadDescription(GetCurrentThread(), wszName);
+ Assert(SUCCEEDED(hrc)); RT_NOREF(hrc);
+ }
+}
+
+
+/**
+ * Bitch about dangling COM and OLE references, dispose of them
+ * afterwards so we don't end up deadlocked somewhere below
+ * OLE32!DllMain.
+ */
+static void rtThreadNativeUninitComAndOle(void)
+{
+#if 1 /* experimental code */
+ /*
+ * Read the counters.
+ */
+ struct MySOleTlsData
+ {
+ void *apvReserved0[2]; /**< x86=0x00 W7/64=0x00 */
+ DWORD adwReserved0[3]; /**< x86=0x08 W7/64=0x10 */
+ void *apvReserved1[1]; /**< x86=0x14 W7/64=0x20 */
+ DWORD cComInits; /**< x86=0x18 W7/64=0x28 */
+ DWORD cOleInits; /**< x86=0x1c W7/64=0x2c */
+ DWORD dwReserved1; /**< x86=0x20 W7/64=0x30 */
+ void *apvReserved2[4]; /**< x86=0x24 W7/64=0x38 */
+ DWORD adwReserved2[1]; /**< x86=0x34 W7/64=0x58 */
+ void *pvCurrentCtx; /**< x86=0x38 W7/64=0x60 */
+ IUnknown *pCallState; /**< x86=0x3c W7/64=0x68 */
+ } *pOleTlsData = NULL; /* outside the try/except for debugging */
+ DWORD cComInits = 0;
+ DWORD cOleInits = 0;
+ __try
+ {
+ void *pvTeb = NtCurrentTeb();
+# ifdef RT_ARCH_AMD64
+ pOleTlsData = *(struct MySOleTlsData **)((uintptr_t)pvTeb + 0x1758); /*TEB.ReservedForOle*/
+# elif RT_ARCH_X86
+ pOleTlsData = *(struct MySOleTlsData **)((uintptr_t)pvTeb + 0x0f80); /*TEB.ReservedForOle*/
+# else
+# error "Port me!"
+# endif
+ if (pOleTlsData)
+ {
+ cComInits = pOleTlsData->cComInits;
+ cOleInits = pOleTlsData->cOleInits;
+ }
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ AssertFailedReturnVoid();
+ }
+
+ /*
+ * Assert sanity. If any of these breaks, the structure layout above is
+ * probably not correct any longer.
+ */
+ AssertMsgReturnVoid(cComInits < 1000, ("%u (%#x)\n", cComInits, cComInits));
+ AssertMsgReturnVoid(cOleInits < 1000, ("%u (%#x)\n", cOleInits, cOleInits));
+ AssertMsgReturnVoid(cComInits >= cOleInits, ("cComInits=%#x cOleInits=%#x\n", cComInits, cOleInits));
+
+ /*
+ * Do the uninitializing.
+ */
+ if (cComInits)
+ {
+ AssertMsgFailed(("cComInits=%u (%#x) cOleInits=%u (%#x) - dangling COM/OLE inits!\n",
+ cComInits, cComInits, cOleInits, cOleInits));
+
+ PFNOLEUNINITIALIZE pfnOleUninitialize = g_pfnOleUninitialize;
+ PFNCOUNINITIALIZE pfnCoUninitialize = g_pfnCoUninitialize;
+ if (pfnCoUninitialize && pfnOleUninitialize)
+ { /* likely */ }
+ else
+ {
+ HMODULE hOle32 = GetModuleHandle("ole32.dll");
+ AssertReturnVoid(hOle32 != NULL);
+
+ pfnOleUninitialize = (PFNOLEUNINITIALIZE)GetProcAddress(hOle32, "OleUninitialize");
+ AssertReturnVoid(pfnOleUninitialize);
+
+ pfnCoUninitialize = (PFNCOUNINITIALIZE)GetProcAddress(hOle32, "CoUninitialize");
+ AssertReturnVoid(pfnCoUninitialize);
+ }
+
+ while (cOleInits-- > 0)
+ {
+ pfnOleUninitialize();
+ cComInits--;
+ }
+
+ while (cComInits-- > 0)
+ pfnCoUninitialize();
+ }
+#endif
+}
+
+
+/**
+ * Implements the RTTHREADFLAGS_COM_MTA and RTTHREADFLAGS_COM_STA flags.
+ *
+ * @returns true if COM uninitialization should be done, false if not.
+ * @param fFlags The thread flags.
+ */
+static bool rtThreadNativeWinCoInitialize(unsigned fFlags)
+{
+ /*
+ * Resolve the ole32 init and uninit functions dynamically.
+ */
+ PFNCOINITIALIZEEX pfnCoInitializeEx = g_pfnCoInitializeEx;
+ PFNCOUNINITIALIZE pfnCoUninitialize = g_pfnCoUninitialize;
+ if (pfnCoInitializeEx && pfnCoUninitialize)
+ { /* likely */ }
+ else
+ {
+ RTLDRMOD hModOle32 = NIL_RTLDRMOD;
+ int rc = RTLdrLoadSystem("ole32.dll", true /*fNoUnload*/, &hModOle32);
+ AssertRCReturn(rc, false);
+
+ PFNOLEUNINITIALIZE pfnOleUninitialize;
+ pfnOleUninitialize = (PFNOLEUNINITIALIZE)RTLdrGetFunction(hModOle32, "OleUninitialize");
+ pfnCoUninitialize = (PFNCOUNINITIALIZE )RTLdrGetFunction(hModOle32, "CoUninitialize");
+ pfnCoInitializeEx = (PFNCOINITIALIZEEX )RTLdrGetFunction(hModOle32, "CoInitializeEx");
+
+ RTLdrClose(hModOle32);
+ AssertReturn(pfnCoInitializeEx && pfnCoUninitialize, false);
+
+ if (pfnOleUninitialize && !g_pfnOleUninitialize)
+ g_pfnOleUninitialize = pfnOleUninitialize;
+ g_pfnCoInitializeEx = pfnCoInitializeEx;
+ g_pfnCoUninitialize = pfnCoUninitialize;
+ }
+
+ /*
+ * Do the initializating.
+ */
+ DWORD fComInit;
+ if (fFlags & RTTHREADFLAGS_COM_MTA)
+ fComInit = COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY | COINIT_DISABLE_OLE1DDE;
+ else
+ fComInit = COINIT_APARTMENTTHREADED | COINIT_SPEED_OVER_MEMORY;
+ HRESULT hrc = pfnCoInitializeEx(NULL, fComInit);
+ AssertMsg(SUCCEEDED(hrc), ("%Rhrc fComInit=%#x\n", hrc, fComInit));
+ return SUCCEEDED(hrc);
+}
+
+
+/**
+ * Wrapper which unpacks the param stuff and calls thread function.
+ */
+#ifndef IPRT_NO_CRT
+static unsigned __stdcall rtThreadNativeMain(void *pvArgs) RT_NOTHROW_DEF
+#else
+static DWORD __stdcall rtThreadNativeMain(void *pvArgs) RT_NOTHROW_DEF
+#endif
+{
+ DWORD dwThreadId = GetCurrentThreadId();
+ PRTTHREADINT pThread = (PRTTHREADINT)pvArgs;
+
+ if (!TlsSetValue(g_dwSelfTLS, pThread))
+ AssertReleaseMsgFailed(("failed to set self TLS. lasterr=%d thread '%s'\n", GetLastError(), pThread->szName));
+ rtThreadWinSetThreadName(pThread, dwThreadId);
+
+ bool fUninitCom = (pThread->fFlags & (RTTHREADFLAGS_COM_MTA | RTTHREADFLAGS_COM_STA)) != 0;
+ if (fUninitCom)
+ fUninitCom = rtThreadNativeWinCoInitialize(pThread->fFlags);
+
+ int rc = rtThreadMain(pThread, dwThreadId, &pThread->szName[0]);
+
+ TlsSetValue(g_dwSelfTLS, NULL); /* rtThreadMain already released the structure. */
+
+ if (fUninitCom && g_pfnCoUninitialize)
+ g_pfnCoUninitialize();
+
+ rtThreadNativeUninitComAndOle();
+#ifndef IPRT_NO_CRT
+ _endthreadex(rc);
+ return rc; /* not reached */
+#else
+ for (;;)
+ ExitThread(rc);
+#endif
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread)
+{
+ AssertReturn(pThread->cbStack < ~(unsigned)0, VERR_INVALID_PARAMETER);
+
+ /*
+ * If a stack size is given, make sure it's not a multiple of 64KB so that we
+ * get one or more pages for overflow protection. (ASSUMES 64KB alloc align.)
+ */
+ unsigned cbStack = (unsigned)pThread->cbStack;
+ if (cbStack > 0 && RT_ALIGN_T(cbStack, _64K, unsigned) == cbStack)
+ cbStack += PAGE_SIZE;
+
+ /*
+ * Create the thread.
+ */
+ pThread->hThread = (uintptr_t)INVALID_HANDLE_VALUE;
+#ifndef IPRT_NO_CRT
+ unsigned uThreadId = 0;
+ uintptr_t hThread = _beginthreadex(NULL /*pSecAttrs*/, cbStack, rtThreadNativeMain, pThread, 0 /*fFlags*/, &uThreadId);
+ if (hThread != 0 && hThread != ~0U)
+ {
+ pThread->hThread = hThread;
+ *pNativeThread = uThreadId;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+#else
+ DWORD idThread = 0;
+ HANDLE hThread = CreateThread(NULL /*pSecAttrs*/, cbStack, rtThreadNativeMain, pThread, 0 /*fFlags*/, &idThread);
+ if (hThread != NULL)
+ {
+ pThread->hThread = (uintptr_t)hThread;
+ *pNativeThread = idThread;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+#endif
+}
+
+
+DECLHIDDEN(bool) rtThreadNativeIsAliveKludge(PRTTHREADINT pThread)
+{
+ PPEB_COMMON pPeb = NtCurrentPeb();
+ if (!pPeb || !pPeb->Ldr || !pPeb->Ldr->ShutdownInProgress)
+ return true;
+ DWORD rcWait = WaitForSingleObject((HANDLE)pThread->hThread, 0);
+ return rcWait != WAIT_OBJECT_0;
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ PRTTHREADINT pThread = (PRTTHREADINT)TlsGetValue(g_dwSelfTLS);
+ /** @todo import alien threads ? */
+ return pThread;
+}
+
+
+#if 0 /* noone is using this ... */
+/**
+ * Returns the processor number the current thread was running on during this call
+ *
+ * @returns processor nr
+ */
+static int rtThreadGetCurrentProcessorNumber(void)
+{
+ static bool fInitialized = false;
+ static DWORD (WINAPI *pfnGetCurrentProcessorNumber)(void) = NULL;
+ if (!fInitialized)
+ {
+ HMODULE hmodKernel32 = GetModuleHandle("kernel32.dll");
+ if (hmodKernel32)
+ pfnGetCurrentProcessorNumber = (DWORD (WINAPI*)(void))GetProcAddress(hmodKernel32, "GetCurrentProcessorNumber");
+ fInitialized = true;
+ }
+ if (pfnGetCurrentProcessorNumber)
+ return pfnGetCurrentProcessorNumber();
+ return -1;
+}
+#endif
+
+
+RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet)
+{
+ /* The affinity functionality was added in NT 3.50, so we resolve the APIs
+ dynamically to be able to run on NT 3.1. */
+ if (g_pfnSetThreadAffinityMask)
+ {
+ DWORD_PTR fNewMask = pCpuSet ? RTCpuSetToU64(pCpuSet) : ~(DWORD_PTR)0;
+ DWORD_PTR dwRet = g_pfnSetThreadAffinityMask(GetCurrentThread(), fNewMask);
+ if (dwRet)
+ return VINF_SUCCESS;
+
+ int iLastError = GetLastError();
+ AssertMsgFailed(("SetThreadAffinityMask failed, LastError=%d\n", iLastError));
+ return RTErrConvertFromWin32(iLastError);
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet)
+{
+ /* The affinity functionality was added in NT 3.50, so we resolve the APIs
+ dynamically to be able to run on NT 3.1. */
+ if ( g_pfnSetThreadAffinityMask
+ && g_pfnGetProcessAffinityMask)
+ {
+ /*
+ * Haven't found no query api, but the set api returns the old mask, so let's use that.
+ */
+ DWORD_PTR dwIgnored;
+ DWORD_PTR dwProcAff = 0;
+ if (g_pfnGetProcessAffinityMask(GetCurrentProcess(), &dwProcAff, &dwIgnored))
+ {
+ HANDLE hThread = GetCurrentThread();
+ DWORD_PTR dwRet = g_pfnSetThreadAffinityMask(hThread, dwProcAff);
+ if (dwRet)
+ {
+ DWORD_PTR dwSet = g_pfnSetThreadAffinityMask(hThread, dwRet);
+ Assert(dwSet == dwProcAff); NOREF(dwRet);
+
+ RTCpuSetFromU64(pCpuSet, (uint64_t)dwSet);
+ return VINF_SUCCESS;
+ }
+ }
+
+ int iLastError = GetLastError();
+ AssertMsgFailed(("SetThreadAffinityMask or GetProcessAffinityMask failed, LastError=%d\n", iLastError));
+ return RTErrConvertFromWin32(iLastError);
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTR3DECL(int) RTThreadGetExecutionTimeMilli(uint64_t *pKernelTime, uint64_t *pUserTime)
+{
+ uint64_t u64CreationTime, u64ExitTime, u64KernelTime, u64UserTime;
+
+ if (GetThreadTimes(GetCurrentThread(), (LPFILETIME)&u64CreationTime, (LPFILETIME)&u64ExitTime, (LPFILETIME)&u64KernelTime, (LPFILETIME)&u64UserTime))
+ {
+ *pKernelTime = u64KernelTime / 10000; /* GetThreadTimes returns time in 100 ns units */
+ *pUserTime = u64UserTime / 10000; /* GetThreadTimes returns time in 100 ns units */
+ return VINF_SUCCESS;
+ }
+
+ int iLastError = GetLastError();
+ AssertMsgFailed(("GetThreadTimes failed, LastError=%d\n", iLastError));
+ return RTErrConvertFromWin32(iLastError);
+}
+
+
+/**
+ * Gets the native thread handle for a IPRT thread.
+ *
+ * @returns The thread handle. INVALID_HANDLE_VALUE on failure.
+ * @param hThread The IPRT thread handle.
+ *
+ * @note Windows only.
+ * @note Only valid after parent returns from the thread creation call.
+ */
+RTDECL(uintptr_t) RTThreadGetNativeHandle(RTTHREAD hThread)
+{
+ PRTTHREADINT pThread = rtThreadGet(hThread);
+ if (pThread)
+ {
+ uintptr_t hHandle = pThread->hThread;
+ rtThreadRelease(pThread);
+ return hHandle;
+ }
+ return (uintptr_t)INVALID_HANDLE_VALUE;
+}
+RT_EXPORT_SYMBOL(RTThreadGetNativeHandle);
+
+
+RTDECL(int) RTThreadPoke(RTTHREAD hThread)
+{
+ AssertReturn(hThread != RTThreadSelf(), VERR_INVALID_PARAMETER);
+ if (g_pfnNtAlertThread)
+ {
+ PRTTHREADINT pThread = rtThreadGet(hThread);
+ AssertReturn(pThread, VERR_INVALID_HANDLE);
+
+ NTSTATUS rcNt = g_pfnNtAlertThread((HANDLE)pThread->hThread);
+
+ rtThreadRelease(pThread);
+ if (NT_SUCCESS(rcNt))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(rcNt);
+ }
+ return VERR_NOT_IMPLEMENTED;
+}
+
diff --git a/src/VBox/Runtime/r3/win/thread2-win.cpp b/src/VBox/Runtime/r3/win/thread2-win.cpp
new file mode 100644
index 00000000..f9874c96
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/thread2-win.cpp
@@ -0,0 +1,84 @@
+/* $Id: thread2-win.cpp $ */
+/** @file
+ * IPRT - Threads part 2, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <iprt/win/windows.h>
+
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include "internal/thread.h"
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ return (RTNATIVETHREAD)GetCurrentThreadId();
+}
+
+
+RTR3DECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ LogFlow(("RTThreadSleep: cMillies=%d\n", cMillies));
+ Sleep(cMillies);
+ LogFlow(("RTThreadSleep: returning %Rrc (cMillies=%d)\n", VINF_SUCCESS, cMillies));
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies)
+{
+ Sleep(cMillies);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(bool) RTThreadYield(void)
+{
+ uint64_t u64TS = ASMReadTSC();
+ Sleep(0);
+ u64TS = ASMReadTSC() - u64TS;
+ bool fRc = u64TS > 1500;
+ LogFlow(("RTThreadYield: returning %d (%llu ticks)\n", fRc, u64TS));
+ return fRc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/time-win.cpp b/src/VBox/Runtime/r3/win/time-win.cpp
new file mode 100644
index 00000000..09e42d8e
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/time-win.cpp
@@ -0,0 +1,228 @@
+/* $Id: time-win.cpp $ */
+/** @file
+ * IPRT - Time, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/time.h"
+#include "internal-r3-win.h"
+
+/*
+ * Note! The selected time source be the exact same one as we use in kernel land!
+ */
+//#define USE_TICK_COUNT
+//#define USE_PERFORMANCE_COUNTER
+//# define USE_FILE_TIME
+//#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+# define USE_INTERRUPT_TIME
+//#else
+//# define USE_TICK_COUNT
+//#endif
+
+
+
+DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void)
+{
+#if defined USE_TICK_COUNT
+ /*
+ * This would work if it didn't flip over every 49 (or so) days.
+ */
+ return (uint64_t)GetTickCount() * RT_NS_1MS_64;
+
+#elif defined USE_PERFORMANCE_COUNTER
+ /*
+ * Slow and not derived from InterruptTime.
+ */
+ static LARGE_INTEGER llFreq;
+ static unsigned uMult;
+ if (!llFreq.QuadPart)
+ {
+ if (!QueryPerformanceFrequency(&llFreq))
+ return (uint64_t)GetTickCount() * RT_NS_1MS_64;
+ llFreq.QuadPart /= 1000;
+ uMult = 1000000; /* no math genius, but this seemed to help avoiding floating point. */
+ }
+
+ LARGE_INTEGER ll;
+ if (QueryPerformanceCounter(&ll))
+ return (ll.QuadPart * uMult) / llFreq.QuadPart;
+ return (uint64_t)GetTickCount() * RT_NS_1MS_64;
+
+#elif defined USE_FILE_TIME
+ /*
+ * This is SystemTime not InterruptTime.
+ */
+ uint64_t u64; /* manual say larger integer, should be safe to assume it's the same. */
+ GetSystemTimeAsFileTime((LPFILETIME)&u64);
+ return u64 * 100;
+
+#elif defined USE_INTERRUPT_TIME
+ /*
+ * Use interrupt time if we can (not possible on NT 3.1).
+ * Note! We cannot entirely depend on g_enmWinVer here as we're likely to
+ * get called before IPRT is initialized. Ditto g_hModNtDll.
+ */
+ static PFNRTLGETINTERRUPTTIMEPRECISE s_pfnRtlGetInterruptTimePrecise = NULL;
+ static int volatile s_iCanUseUserSharedData = -1;
+ int iCanUseUserSharedData = s_iCanUseUserSharedData;
+ if (iCanUseUserSharedData != -1)
+ { /* likely */ }
+ else
+ {
+ /* We may be called before g_enmWinVer has been initialized. */
+ if (g_enmWinVer != kRTWinOSType_UNKNOWN)
+ iCanUseUserSharedData = g_enmWinVer > kRTWinOSType_NT310;
+ else
+ {
+ DWORD dwVer = GetVersion();
+ iCanUseUserSharedData = (dwVer & 0xff) != 3 || ((dwVer >> 8) & 0xff) >= 50;
+ }
+ if (iCanUseUserSharedData != 0)
+ {
+ FARPROC pfn = GetProcAddress(g_hModNtDll ? g_hModNtDll : GetModuleHandleW(L"ntdll"), "RtlGetInterruptTimePrecise");
+ if (pfn != NULL)
+ {
+ ASMAtomicWritePtr(&s_pfnRtlGetInterruptTimePrecise, pfn);
+ iCanUseUserSharedData = 42;
+ }
+ }
+ s_iCanUseUserSharedData = iCanUseUserSharedData;
+ }
+
+ if (iCanUseUserSharedData != 0)
+ {
+ LARGE_INTEGER Time;
+ if (iCanUseUserSharedData == 42)
+ {
+ uint64_t iIgnored;
+ Time.QuadPart = s_pfnRtlGetInterruptTimePrecise(&iIgnored);
+ }
+ else
+ {
+ PKUSER_SHARED_DATA pUserSharedData = (PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA;
+ do
+ {
+ Time.HighPart = pUserSharedData->InterruptTime.High1Time;
+ Time.LowPart = pUserSharedData->InterruptTime.LowPart;
+ } while (pUserSharedData->InterruptTime.High2Time != Time.HighPart);
+ }
+
+ return (uint64_t)Time.QuadPart * 100;
+ }
+
+ return (uint64_t)GetTickCount() * RT_NS_1MS_64;
+
+#else
+# error "Must select a method bright guy!"
+#endif
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ uint64_t u64;
+ AssertCompile(sizeof(u64) == sizeof(FILETIME));
+ if (g_pfnGetSystemTimeAsFileTime)
+ g_pfnGetSystemTimeAsFileTime((LPFILETIME)&u64);
+ else
+ {
+ SYSTEMTIME SysTime = {0};
+ GetSystemTime(&SysTime);
+ BOOL fRet = SystemTimeToFileTime(&SysTime, (LPFILETIME)&u64);
+ Assert(fRet); RT_NOREF(fRet);
+ }
+ return RTTimeSpecSetNtTime(pTime, u64);
+
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeLocalNow(PRTTIMESPEC pTime)
+{
+ uint64_t u64Local;
+ if (g_pfnGetSystemTimeAsFileTime)
+ {
+ uint64_t u64;
+ AssertCompile(sizeof(u64) == sizeof(FILETIME));
+ g_pfnGetSystemTimeAsFileTime((LPFILETIME)&u64);
+ if (!FileTimeToLocalFileTime((FILETIME const *)&u64, (LPFILETIME)&u64Local))
+ u64Local = u64;
+ }
+ else
+ {
+ SYSTEMTIME SysTime = {0};
+ GetLocalTime(&SysTime);
+ BOOL fRet = SystemTimeToFileTime(&SysTime, (LPFILETIME)&u64Local);
+ Assert(fRet); RT_NOREF(fRet);
+ }
+ return RTTimeSpecSetNtTime(pTime, u64Local);
+}
+
+
+RTDECL(int64_t) RTTimeLocalDeltaNano(void)
+{
+ /*
+ * UTC = local + Tzi.Bias;
+ * The bias is given in minutes.
+ */
+ TIME_ZONE_INFORMATION Tzi;
+ Tzi.Bias = 0;
+ if (GetTimeZoneInformation(&Tzi) != TIME_ZONE_ID_INVALID)
+ return -(int64_t)Tzi.Bias * 60 * RT_NS_1SEC_64;
+ return 0;
+}
+
diff --git a/src/VBox/Runtime/r3/win/time2-win.cpp b/src/VBox/Runtime/r3/win/time2-win.cpp
new file mode 100644
index 00000000..d4f0db06
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/time2-win.cpp
@@ -0,0 +1,164 @@
+/* $Id: time2-win.cpp $ */
+/** @file
+ * IPRT - Time, Windows.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include <iprt/win/windows.h>
+
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/time.h"
+
+#include "internal-r3-win.h"
+
+
+RTDECL(int) RTTimeSet(PCRTTIMESPEC pTime)
+{
+ FILETIME FileTime;
+ SYSTEMTIME SysTime;
+ if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTime, &FileTime), &SysTime))
+ {
+ if (SetSystemTime(&SysTime))
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTDECL(PRTTIME) RTTimeLocalExplode(PRTTIME pTime, PCRTTIMESPEC pTimeSpec)
+{
+ RTTIMESPEC LocalTime;
+ if (g_pfnSystemTimeToTzSpecificLocalTime)
+ {
+ /*
+ * FileTimeToLocalFileTime does not do the right thing, so we'll have
+ * to convert to system time and SystemTimeToTzSpecificLocalTime instead.
+ *
+ * Note! FileTimeToSystemTime drops resoultion down to milliseconds, thus
+ * we have to do the offUTC calculation using milliseconds and adjust
+ * u32Nanosecons by sub milliseconds digits.
+ */
+ SYSTEMTIME SystemTimeIn;
+ FILETIME FileTime;
+ if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTimeSpec, &FileTime), &SystemTimeIn))
+ {
+ SYSTEMTIME SystemTimeOut;
+ if (g_pfnSystemTimeToTzSpecificLocalTime(NULL /* use current TZI */, &SystemTimeIn, &SystemTimeOut))
+ {
+ if (SystemTimeToFileTime(&SystemTimeOut, &FileTime))
+ {
+ RTTimeSpecSetNtFileTime(&LocalTime, &FileTime);
+ pTime = RTTimeExplode(pTime, &LocalTime);
+ if (pTime)
+ {
+ pTime->fFlags = (pTime->fFlags & ~RTTIME_FLAGS_TYPE_MASK) | RTTIME_FLAGS_TYPE_LOCAL;
+ pTime->offUTC = (RTTimeSpecGetMilli(&LocalTime) - RTTimeSpecGetMilli(pTimeSpec)) / RT_MS_1MIN;
+ pTime->u32Nanosecond += RTTimeSpecGetNano(pTimeSpec) % RT_NS_1MS;
+ }
+ return pTime;
+ }
+ }
+ }
+ }
+
+ /*
+ * The fallback is to use the current offset.
+ * (A better fallback would be to use the offset of the same time of the year.)
+ */
+ LocalTime = *pTimeSpec;
+ int64_t cNsUtcOffset = RTTimeLocalDeltaNano();
+ RTTimeSpecAddNano(&LocalTime, cNsUtcOffset);
+ pTime = RTTimeExplode(pTime, &LocalTime);
+ if (pTime)
+ {
+ pTime->fFlags = (pTime->fFlags & ~RTTIME_FLAGS_TYPE_MASK) | RTTIME_FLAGS_TYPE_LOCAL;
+ pTime->offUTC = cNsUtcOffset / RT_NS_1MIN;
+ }
+ return pTime;
+}
+
+
+/**
+ * Gets the delta between UTC and local time at the given time.
+ *
+ * @code
+ * RTTIMESPEC LocalTime;
+ * RTTimeNow(&LocalTime);
+ * RTTimeSpecAddNano(&LocalTime, RTTimeLocalDeltaNanoFor(&LocalTime));
+ * @endcode
+ *
+ * @param pTimeSpec The time spec giving the time to get the delta for.
+ * @returns Returns the nanosecond delta between UTC and local time.
+ */
+RTDECL(int64_t) RTTimeLocalDeltaNanoFor(PCRTTIMESPEC pTimeSpec)
+{
+ RTTIMESPEC LocalTime;
+ if (g_pfnSystemTimeToTzSpecificLocalTime)
+ {
+ /*
+ * FileTimeToLocalFileTime does not do the right thing, so we'll have
+ * to convert to system time and SystemTimeToTzSpecificLocalTime instead.
+ *
+ * Note! FileTimeToSystemTime drops resoultion down to milliseconds, thus
+ * we have to do the offUTC calculation using milliseconds and adjust
+ * u32Nanosecons by sub milliseconds digits.
+ */
+ SYSTEMTIME SystemTimeIn;
+ FILETIME FileTime;
+ if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTimeSpec, &FileTime), &SystemTimeIn))
+ {
+ SYSTEMTIME SystemTimeOut;
+ if (g_pfnSystemTimeToTzSpecificLocalTime(NULL /* use current TZI */, &SystemTimeIn, &SystemTimeOut))
+ {
+ if (SystemTimeToFileTime(&SystemTimeOut, &FileTime))
+ {
+ RTTimeSpecSetNtFileTime(&LocalTime, &FileTime);
+
+ return (RTTimeSpecGetMilli(&LocalTime) - RTTimeSpecGetMilli(pTimeSpec)) * RT_NS_1MS;
+ }
+ }
+ }
+ }
+
+ return RTTimeLocalDeltaNano();
+}
+
diff --git a/src/VBox/Runtime/r3/win/timer-win.cpp b/src/VBox/Runtime/r3/win/timer-win.cpp
new file mode 100644
index 00000000..f8d4a520
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/timer-win.cpp
@@ -0,0 +1,520 @@
+/* $Id: timer-win.cpp $ */
+/** @file
+ * IPRT - Timer.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIMER
+#define _WIN32_WINNT 0x0500
+#include <iprt/win/windows.h>
+
+#include <iprt/timer.h>
+#ifdef USE_CATCH_UP
+# include <iprt/time.h>
+#endif
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/asm.h>
+#include <iprt/semaphore.h>
+#include <iprt/err.h>
+#include "internal/magics.h"
+#include "internal-r3-win.h"
+
+
+/** Define the flag for creating a manual reset timer if not available in the SDK we are compiling with. */
+#ifndef CREATE_WAITABLE_TIMER_MANUAL_RESET
+# define CREATE_WAITABLE_TIMER_MANUAL_RESET 0x00000001
+#endif
+/** Define the flag for high resolution timers, available since Windows 10 RS4 if not available. */
+#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
+# define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002
+#endif
+
+
+RT_C_DECLS_BEGIN
+/* from sysinternals. */
+NTSYSAPI LONG NTAPI NtSetTimerResolution(IN ULONG DesiredResolution, IN BOOLEAN SetResolution, OUT PULONG CurrentResolution);
+NTSYSAPI LONG NTAPI NtQueryTimerResolution(OUT PULONG MaximumResolution, OUT PULONG MinimumResolution, OUT PULONG CurrentResolution);
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal representation of a timer handle.
+ */
+typedef struct RTTIMER
+{
+ /** Magic.
+ * This is RTTIMER_MAGIC, but changes to something else before the timer
+ * is destroyed to indicate clearly that thread should exit. */
+ uint32_t volatile u32Magic;
+ /** Flag indicating the timer is suspended. */
+ bool volatile fSuspended;
+ /** Flag indicating that the timer has been destroyed. */
+ bool volatile fDestroyed;
+ /** User argument. */
+ void *pvUser;
+ /** Callback. */
+ PFNRTTIMER pfnTimer;
+ /** The current tick. */
+ uint64_t iTick;
+ /** The timer interval. 0 if one-shot. */
+ uint64_t u64NanoInterval;
+ /** The first shot interval. 0 if ASAP. */
+ uint64_t volatile u64NanoFirst;
+ /** Time handle. */
+ HANDLE hTimer;
+ /** USE_CATCH_UP: ns time of the next tick.
+ * !USE_CATCH_UP: -uMilliesInterval * 10000 */
+ LARGE_INTEGER llNext;
+ /** The thread handle of the timer thread. */
+ RTTHREAD Thread;
+ /** Event semaphore on which the thread is blocked. */
+ RTSEMEVENT Event;
+ /** The error/status of the timer.
+ * Initially -1, set to 0 when the timer have been successfully started, and
+ * to errno on failure in starting the timer. */
+ volatile int iError;
+} RTTIMER;
+
+
+
+/**
+ * Timer thread.
+ */
+static DECLCALLBACK(int) rttimerCallback(RTTHREAD hThreadSelf, void *pvArg)
+{
+ PRTTIMER pTimer = (PRTTIMER)(void *)pvArg;
+ Assert(pTimer->u32Magic == RTTIMER_MAGIC);
+
+ /*
+ * Bounce our priority up quite a bit.
+ */
+ if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
+ {
+ int rc = GetLastError();
+ AssertMsgFailed(("Failed to set priority class lasterror %d.\n", rc));
+ pTimer->iError = RTErrConvertFromWin32(rc);
+ RTThreadUserSignal(hThreadSelf);
+ return rc;
+ }
+
+ /*
+ * The work loop.
+ */
+ RTThreadUserSignal(hThreadSelf);
+
+ while ( !pTimer->fDestroyed
+ && pTimer->u32Magic == RTTIMER_MAGIC)
+ {
+ /*
+ * Wait for a start or destroy event.
+ */
+ if (pTimer->fSuspended)
+ {
+ int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)
+ {
+ AssertRC(rc);
+ if (pTimer->fDestroyed)
+ continue;
+ RTThreadSleep(1000); /* Don't cause trouble! */
+ }
+ if ( pTimer->fSuspended
+ || pTimer->fDestroyed)
+ continue;
+ }
+
+ /*
+ * Start the waitable timer.
+ */
+ pTimer->llNext.QuadPart = -(int64_t)pTimer->u64NanoInterval / 100;
+ LARGE_INTEGER ll;
+ if (pTimer->u64NanoFirst)
+ {
+ GetSystemTimeAsFileTime((LPFILETIME)&ll);
+ ll.QuadPart += pTimer->u64NanoFirst / 100;
+ pTimer->u64NanoFirst = 0;
+ }
+ else
+ ll.QuadPart = -(int64_t)pTimer->u64NanoInterval / 100;
+ if (!SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE))
+ {
+ ASMAtomicXchgBool(&pTimer->fSuspended, true);
+ int rc = GetLastError();
+ AssertMsgFailed(("Failed to set timer, lasterr %d.\n", rc));
+ pTimer->iError = RTErrConvertFromWin32(rc);
+ RTThreadUserSignal(hThreadSelf);
+ continue; /* back to suspended mode. */
+ }
+ pTimer->iError = 0;
+ RTThreadUserSignal(hThreadSelf);
+
+ /*
+ * Timer Service Loop.
+ */
+ do
+ {
+ int rc = WaitForSingleObjectEx(pTimer->hTimer, INFINITE, FALSE);
+ if (pTimer->u32Magic != RTTIMER_MAGIC)
+ break;
+ if (rc == WAIT_OBJECT_0)
+ {
+ /*
+ * Callback the handler.
+ */
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
+
+ /*
+ * Rearm the timer handler.
+ */
+ ll = pTimer->llNext;
+ BOOL fRc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE);
+ AssertMsg(fRc || pTimer->u32Magic != RTTIMER_MAGIC, ("last error %d\n", GetLastError())); NOREF(fRc);
+ }
+ else
+ {
+ /*
+ * We failed during wait, so just signal the destructor and exit.
+ */
+ int rc2 = GetLastError();
+ RTThreadUserSignal(hThreadSelf);
+ AssertMsgFailed(("Wait on hTimer failed, rc=%d lasterr=%d\n", rc, rc2)); NOREF(rc2);
+ return -1;
+ }
+ } while (RT_LIKELY( !pTimer->fSuspended
+ && !pTimer->fDestroyed
+ && pTimer->u32Magic == RTTIMER_MAGIC));
+
+ /*
+ * Disable the timer.
+ */
+ int rc = CancelWaitableTimer (pTimer->hTimer); RT_NOREF(rc);
+ AssertMsg(rc, ("CancelWaitableTimer lasterr=%d\n", GetLastError()));
+
+ /*
+ * ACK any pending suspend request.
+ */
+ if (!pTimer->fDestroyed)
+ {
+ pTimer->iError = 0;
+ RTThreadUserSignal(hThreadSelf);
+ }
+ }
+
+ /*
+ * Exit.
+ */
+ pTimer->iError = 0;
+ RTThreadUserSignal(hThreadSelf);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Tries to set the NT timer resolution to a value matching the given timer interval.
+ *
+ * @returns IPRT status code.
+ * @param u64NanoInterval The timer interval in nano seconds.
+ */
+static int rtTimerNtSetTimerResolution(uint64_t u64NanoInterval)
+{
+ /*
+ * On windows we'll have to set the timer resolution before
+ * we start the timer.
+ */
+ ULONG ulMax = UINT32_MAX;
+ ULONG ulMin = UINT32_MAX;
+ ULONG ulCur = UINT32_MAX;
+ ULONG ulReq = (ULONG)(u64NanoInterval / 100);
+ NtQueryTimerResolution(&ulMax, &ulMin, &ulCur);
+ Log(("NtQueryTimerResolution -> ulMax=%lu00ns ulMin=%lu00ns ulCur=%lu00ns\n", ulMax, ulMin, ulCur));
+ if (ulCur > ulMin && ulCur > ulReq)
+ {
+ ulReq = RT_MIN(ulMin, ulReq);
+ if (NtSetTimerResolution(ulReq, TRUE, &ulCur) >= 0)
+ Log(("Changed timer resolution to %lu*100ns.\n", ulReq));
+ else if (NtSetTimerResolution(10000, TRUE, &ulCur) >= 0)
+ Log(("Changed timer resolution to 1ms.\n"));
+ else if (NtSetTimerResolution(20000, TRUE, &ulCur) >= 0)
+ Log(("Changed timer resolution to 2ms.\n"));
+ else if (NtSetTimerResolution(40000, TRUE, &ulCur) >= 0)
+ Log(("Changed timer resolution to 4ms.\n"));
+ else if (ulMin <= 50000 && NtSetTimerResolution(ulMin, TRUE, &ulCur) >= 0)
+ Log(("Changed timer resolution to %lu *100ns.\n", ulMin));
+ else
+ {
+ AssertMsgFailed(("Failed to configure timer resolution!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
+{
+ /*
+ * We don't support the fancy MP features.
+ */
+ if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Create new timer.
+ */
+ int rc = VERR_IPE_UNINITIALIZED_STATUS;
+ PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
+ if (pTimer)
+ {
+ pTimer->u32Magic = RTTIMER_MAGIC;
+ pTimer->fSuspended = true;
+ pTimer->fDestroyed = false;
+ pTimer->Thread = NIL_RTTHREAD;
+ pTimer->pfnTimer = pfnTimer;
+ pTimer->pvUser = pvUser;
+ pTimer->u64NanoInterval = u64NanoInterval;
+
+ rc = RTSemEventCreate(&pTimer->Event);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create Win32 waitable timer.
+ * We will first try the undocumented CREATE_WAITABLE_TIMER_HIGH_RESOLUTION which
+ * exists since some Windows 10 version (RS4). If this fails we resort to the old
+ * method of setting the timer resolution before creating a timer which will probably
+ * not give us the accuracy for intervals below the system tick resolution.
+ */
+ pTimer->iError = 0;
+ if (g_pfnCreateWaitableTimerExW)
+ pTimer->hTimer = g_pfnCreateWaitableTimerExW(NULL, NULL,
+ CREATE_WAITABLE_TIMER_MANUAL_RESET | CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
+ TIMER_ALL_ACCESS);
+ if (!pTimer->hTimer)
+ {
+ rc = rtTimerNtSetTimerResolution(u64NanoInterval);
+ if (RT_SUCCESS(rc))
+ pTimer->hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
+ }
+
+ if (pTimer->hTimer)
+ {
+ /*
+ * Kick off the timer thread.
+ */
+ rc = RTThreadCreate(&pTimer->Thread, rttimerCallback, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Wait for the timer to successfully create the timer
+ * If we don't get a response in 10 secs, then we assume we're screwed.
+ */
+ rc = RTThreadUserWait(pTimer->Thread, 10000);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pTimer->iError;
+ if (RT_SUCCESS(rc))
+ {
+ *ppTimer = pTimer;
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* bail out */
+ ASMAtomicXchgBool(&pTimer->fDestroyed, true);
+ ASMAtomicXchgU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
+ RTThreadWait(pTimer->Thread, 45*1000, NULL);
+ CancelWaitableTimer(pTimer->hTimer);
+ }
+ CloseHandle(pTimer->hTimer);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTSemEventDestroy(pTimer->Event);
+ pTimer->Event = NIL_RTSEMEVENT;
+ }
+
+ RTMemFree(pTimer);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
+{
+ /* NULL is ok. */
+ if (!pTimer)
+ return VINF_SUCCESS;
+
+ int rc = VINF_SUCCESS;
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
+
+ /*
+ * Signal that we want the thread to exit.
+ */
+ ASMAtomicWriteBool(&pTimer->fDestroyed, true);
+ ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
+
+ /*
+ * Suspend the timer if it's running.
+ */
+ if (!pTimer->fSuspended)
+ {
+ LARGE_INTEGER ll = {0};
+ ll.LowPart = 100;
+ rc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE);
+ AssertMsg(rc, ("CancelWaitableTimer lasterr=%d\n", GetLastError()));
+ }
+
+ rc = RTSemEventSignal(pTimer->Event);
+ AssertRC(rc);
+
+ /*
+ * Wait for the thread to exit.
+ * And if it don't wanna exit, we'll get kill it.
+ */
+ rc = RTThreadWait(pTimer->Thread, 30 * 1000, NULL);
+ if (RT_FAILURE(rc))
+ TerminateThread((HANDLE)RTThreadGetNative(pTimer->Thread), UINT32_MAX);
+
+ /*
+ * Free resource.
+ */
+ rc = CloseHandle(pTimer->hTimer);
+ AssertMsg(rc, ("CloseHandle lasterr=%d\n", GetLastError()));
+
+ RTSemEventDestroy(pTimer->Event);
+ pTimer->Event = NIL_RTSEMEVENT;
+
+ RTMemFree(pTimer);
+ return rc;
+}
+
+
+RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
+
+ RTThreadUserReset(pTimer->Thread);
+
+ /*
+ * Already running?
+ */
+ if (!ASMAtomicXchgBool(&pTimer->fSuspended, false))
+ return VERR_TIMER_ACTIVE;
+ LogFlow(("RTTimerStart: pTimer=%p u64First=%llu u64NanoInterval=%llu\n", pTimer, u64First, pTimer->u64NanoInterval));
+
+ /*
+ * Tell the thread to start servicing the timer.
+ * Wait for it to ACK the request to avoid reset races.
+ */
+ ASMAtomicUoWriteU64(&pTimer->u64NanoFirst, u64First);
+ ASMAtomicUoWriteU64(&pTimer->iTick, 0);
+ int rc = RTSemEventSignal(pTimer->Event);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadUserWait(pTimer->Thread, 45*1000);
+ AssertRC(rc);
+ RTThreadUserReset(pTimer->Thread);
+ }
+ else
+ AssertRC(rc);
+
+ if (RT_FAILURE(rc))
+ ASMAtomicXchgBool(&pTimer->fSuspended, true);
+ return rc;
+}
+
+
+RTDECL(int) RTTimerStop(PRTTIMER pTimer)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+
+ RTThreadUserReset(pTimer->Thread);
+
+ /*
+ * Already running?
+ */
+ if (ASMAtomicXchgBool(&pTimer->fSuspended, true))
+ return VERR_TIMER_SUSPENDED;
+ LogFlow(("RTTimerStop: pTimer=%p\n", pTimer));
+
+ /*
+ * Tell the thread to stop servicing the timer.
+ */
+ int rc = VINF_SUCCESS;
+ if (RTThreadSelf() != pTimer->Thread)
+ {
+ LARGE_INTEGER ll = {0};
+ ll.LowPart = 100;
+ rc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE);
+ AssertMsg(rc, ("SetWaitableTimer lasterr=%d\n", GetLastError()));
+ rc = RTThreadUserWait(pTimer->Thread, 45*1000);
+ AssertRC(rc);
+ RTThreadUserReset(pTimer->Thread);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
+{
+ AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+ NOREF(u64NanoInterval);
+ return VERR_NOT_SUPPORTED;
+}
diff --git a/src/VBox/Runtime/r3/win/tls-win.cpp b/src/VBox/Runtime/r3/win/tls-win.cpp
new file mode 100644
index 00000000..5145a266
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/tls-win.cpp
@@ -0,0 +1,230 @@
+/* $Id: tls-win.cpp $ */
+/** @file
+ * IPRT - Thread Local Storage (TLS), Win32.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <iprt/win/windows.h>
+
+#include <iprt/thread.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTTLSWINDTOR
+{
+ RTLISTNODE ListEntry;
+ DWORD iTls;
+ PFNRTTLSDTOR pfnDestructor;
+} RTTLSWINDTOR;
+typedef RTTLSWINDTOR *PRTTLSWINDTOR;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Init once for the list and critical section. */
+static RTONCE g_Once = RTONCE_INITIALIZER;
+/** Critical section protecting the TLS destructor list. */
+static RTCRITSECTRW g_CritSect;
+/** List of TLS destrictors (RTTLSWINDTOR). */
+static RTLISTANCHOR g_TlsDtorHead;
+/** Number of desturctors in the list (helps putting of initialization). */
+static uint32_t volatile g_cTlsDtors = 0;
+
+
+/**
+ * @callback_method_impl{FNRTONCE}
+ */
+static DECLCALLBACK(int32_t) rtTlsWinInitLock(void *pvUser)
+{
+ RT_NOREF(pvUser);
+ RTListInit(&g_TlsDtorHead);
+ return RTCritSectRwInit(&g_CritSect);
+}
+
+
+RTR3DECL(RTTLS) RTTlsAlloc(void)
+{
+ AssertCompile(sizeof(RTTLS) >= sizeof(DWORD));
+ DWORD iTls = TlsAlloc();
+ return iTls != TLS_OUT_OF_INDEXES ? (RTTLS)iTls : NIL_RTTLS;
+}
+
+
+RTR3DECL(int) RTTlsAllocEx(PRTTLS piTls, PFNRTTLSDTOR pfnDestructor)
+{
+ int rc;
+ if (!pfnDestructor)
+ {
+ DWORD iTls = TlsAlloc();
+ if (iTls != TLS_OUT_OF_INDEXES)
+ {
+ Assert((RTTLS)iTls != NIL_RTTLS);
+ *piTls = (RTTLS)iTls;
+ Assert((DWORD)*piTls == iTls);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ rc = RTOnce(&g_Once, rtTlsWinInitLock, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ PRTTLSWINDTOR pDtor = (PRTTLSWINDTOR)RTMemAlloc(sizeof(*pDtor));
+ if (pDtor)
+ {
+ DWORD iTls = TlsAlloc();
+ if (iTls != TLS_OUT_OF_INDEXES)
+ {
+ Assert((RTTLS)iTls != NIL_RTTLS);
+ *piTls = (RTTLS)iTls;
+ Assert((DWORD)*piTls == iTls);
+
+ /*
+ * Add the destructor to the list. We keep it sorted.
+ */
+ pDtor->iTls = iTls;
+ pDtor->pfnDestructor = pfnDestructor;
+ RTCritSectRwEnterExcl(&g_CritSect);
+ RTListAppend(&g_TlsDtorHead, &pDtor->ListEntry);
+ ASMAtomicIncU32(&g_cTlsDtors);
+ RTCritSectRwLeaveExcl(&g_CritSect);
+
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTTlsFree(RTTLS iTls)
+{
+ if (iTls == NIL_RTTLS)
+ return VINF_SUCCESS;
+ if (TlsFree((DWORD)iTls))
+ {
+ if (ASMAtomicReadU32(&g_cTlsDtors) > 0)
+ {
+ RTCritSectRwEnterExcl(&g_CritSect);
+ PRTTLSWINDTOR pDtor;
+ RTListForEach(&g_TlsDtorHead, pDtor, RTTLSWINDTOR, ListEntry)
+ {
+ if (pDtor->iTls == (DWORD)iTls)
+ {
+ RTListNodeRemove(&pDtor->ListEntry);
+ ASMAtomicDecU32(&g_cTlsDtors);
+ RTMemFree(pDtor);
+ break;
+ }
+ }
+ RTCritSectRwLeaveExcl(&g_CritSect);
+ }
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTR3DECL(void *) RTTlsGet(RTTLS iTls)
+{
+ return TlsGetValue((DWORD)iTls);
+}
+
+
+RTR3DECL(int) RTTlsGetEx(RTTLS iTls, void **ppvValue)
+{
+ void *pv = TlsGetValue((DWORD)iTls);
+ if (pv)
+ {
+ *ppvValue = pv;
+ return VINF_SUCCESS;
+ }
+
+ /* TlsGetValue always updates last error */
+ *ppvValue = NULL;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTR3DECL(int) RTTlsSet(RTTLS iTls, void *pvValue)
+{
+ if (TlsSetValue((DWORD)iTls, pvValue))
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+/**
+ * Called by dllmain-win.cpp when a thread detaches.
+ */
+DECLHIDDEN(void) rtThreadWinTlsDestruction(void)
+{
+ if (ASMAtomicReadU32(&g_cTlsDtors) > 0)
+ {
+ RTCritSectRwEnterShared(&g_CritSect);
+ PRTTLSWINDTOR pDtor;
+ RTListForEach(&g_TlsDtorHead, pDtor, RTTLSWINDTOR, ListEntry)
+ {
+ void *pvValue = TlsGetValue(pDtor->iTls);
+ if (pvValue != NULL)
+ {
+ pDtor->pfnDestructor(pvValue);
+ TlsSetValue(pDtor->iTls, NULL);
+ }
+ }
+ RTCritSectRwLeaveShared(&g_CritSect);
+ }
+}
+
diff --git a/src/VBox/Runtime/r3/win/tpm-win.cpp b/src/VBox/Runtime/r3/win/tpm-win.cpp
new file mode 100644
index 00000000..3ee0e46b
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/tpm-win.cpp
@@ -0,0 +1,307 @@
+/* $Id: tpm-win.cpp $ */
+/** @file
+ * IPRT - Trusted Platform Module (TPM) access, Windows variant.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/tpm.h>
+
+#include <iprt/assertcompile.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+
+#include "internal-r3-win.h"
+
+#include <iprt/win/windows.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* tbs.dll: */
+typedef struct TBS_CONTEXT_PARAMS2
+{
+ UINT32 version;
+ union
+ {
+ struct
+ {
+ UINT32 requestRaw: 1;
+ UINT32 includeTpm12: 1;
+ UINT32 includeTpm20: 1;
+ } Fields;
+
+ UINT32 asUINT32;
+ } u;
+} TBS_CONTEXT_PARAMS2;
+
+typedef struct TBS_DEVICE_INFO
+{
+ UINT32 structVersion;
+ UINT32 tpmVersion;
+ UINT32 tpmInterfaceType;
+ UINT32 tpmImpRevision;
+} TBS_DEVICE_INFO;
+
+#define TPM_VERSION_12 1
+#define TPM_VERSION_20 2
+
+#define TBS_SUCCESS S_OK
+#define TBS_COMMAND_PRIORITY_NORMAL 200
+
+typedef UINT32 TBS_RESULT;
+typedef void *TBS_HCONTEXT;
+typedef TBS_RESULT (WINAPI *PFNTBSI_CONTEXT_CREATE)(const TBS_CONTEXT_PARAMS2 *, TBS_HCONTEXT *);
+typedef TBS_RESULT (WINAPI *PFNTBSI_CONTEXT_CLOSE)(TBS_HCONTEXT);
+typedef TBS_RESULT (WINAPI *PFNTBSI_GET_DEVICE_INFO)(UINT32, TBS_DEVICE_INFO *);
+typedef TBS_RESULT (WINAPI *PFNTBSI_CANCEL_COMMANDS)(TBS_HCONTEXT);
+typedef TBS_RESULT (WINAPI *PFNTBSI_SUBMIT_COMMANDS)(TBS_HCONTEXT, UINT32, UINT32, const BYTE *, UINT32, PBYTE, PUINT32);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal TPM instance data.
+ */
+typedef struct RTTPMINT
+{
+ /** Handle to the TPM context. */
+ TBS_HCONTEXT hCtx;
+ /** The deduced TPM version. */
+ RTTPMVERSION enmTpmVers;
+} RTTPMINT;
+/** Pointer to the internal TPM instance data. */
+typedef RTTPMINT *PRTTPMINT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Init once structure. */
+static RTONCE g_rtTpmWinInitOnce = RTONCE_INITIALIZER;
+/* tbs.dll: */
+static PFNTBSI_CONTEXT_CREATE g_pfnTbsiContextCreate = NULL;
+static PFNTBSI_CONTEXT_CLOSE g_pfnTbsiContextClose = NULL;
+static PFNTBSI_GET_DEVICE_INFO g_pfnTbsiGetDeviceInfo = NULL;
+static PFNTBSI_CANCEL_COMMANDS g_pfnTbsiCancelCommands = NULL;
+static PFNTBSI_SUBMIT_COMMANDS g_pfnTbsiSubmitCommands = NULL;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Initialize the globals.
+ *
+ * @returns IPRT status code.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int32_t) rtTpmWinInitOnce(void *pvUser)
+{
+ RT_NOREF(pvUser);
+ RTLDRMOD hMod;
+
+ int rc = RTLdrLoadSystem("tbs.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "Tbsi_Context_Create", (void **)&g_pfnTbsiContextCreate);
+ if (RT_FAILURE(rc)) return rc;
+
+ rc = RTLdrGetSymbol(hMod, "Tbsip_Context_Close", (void **)&g_pfnTbsiContextClose);
+ if (RT_FAILURE(rc)) return rc;
+
+ rc = RTLdrGetSymbol(hMod, "Tbsip_Cancel_Commands", (void **)&g_pfnTbsiCancelCommands);
+ if (RT_FAILURE(rc)) return rc;
+
+ rc = RTLdrGetSymbol(hMod, "Tbsip_Submit_Command", (void **)&g_pfnTbsiSubmitCommands);
+ if (RT_FAILURE(rc)) return rc;
+
+ rc = RTLdrGetSymbol(hMod, "Tbsi_GetDeviceInfo", (void **)&g_pfnTbsiGetDeviceInfo);
+ if (RT_FAILURE(rc)) { g_pfnTbsiGetDeviceInfo = NULL; Assert(g_enmWinVer < kRTWinOSType_8); }
+
+ RTLdrClose(hMod);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTTpmOpen(PRTTPM phTpm, uint32_t idTpm)
+{
+ AssertPtrReturn(phTpm, VERR_INVALID_POINTER);
+ if (idTpm == RTTPM_ID_DEFAULT)
+ idTpm = 0;
+
+ AssertReturn(idTpm == 0, VERR_NOT_SUPPORTED);
+
+ /*
+ * Initialize the globals.
+ */
+ int rc = RTOnce(&g_rtTpmWinInitOnce, rtTpmWinInitOnce, NULL);
+ AssertRCReturn(rc, rc);
+
+ PRTTPMINT pThis = (PRTTPMINT)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ TBS_CONTEXT_PARAMS2 CtxParams; RT_ZERO(CtxParams);
+
+ CtxParams.version = TPM_VERSION_12;
+ if (g_pfnTbsiGetDeviceInfo)
+ {
+ /* TPM2 support is available starting with Win8 which has Tbsi_GetDeviceInfo available. */
+ TBS_DEVICE_INFO DevInfo; RT_ZERO(DevInfo);
+
+ DevInfo.structVersion = TPM_VERSION_20;
+ TBS_RESULT rcTbs = g_pfnTbsiGetDeviceInfo(sizeof(DevInfo), &DevInfo);
+ if (rcTbs == TBS_SUCCESS)
+ {
+ CtxParams.version = TPM_VERSION_20;
+ if (DevInfo.tpmVersion == TPM_VERSION_20)
+ {
+ pThis->enmTpmVers = RTTPMVERSION_2_0;
+ CtxParams.u.Fields.includeTpm20 = 1;
+ }
+ else
+ {
+ Assert(DevInfo.tpmVersion == TPM_VERSION_12);
+ pThis->enmTpmVers = RTTPMVERSION_1_2;
+ CtxParams.u.Fields.includeTpm12 = 1;
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else
+ pThis->enmTpmVers = RTTPMVERSION_1_2;
+
+ if (RT_SUCCESS(rc))
+ {
+ TBS_RESULT rcTbs = g_pfnTbsiContextCreate(&CtxParams, &pThis->hCtx);
+ if (rcTbs == TBS_SUCCESS)
+ {
+ *phTpm = pThis;
+ return VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTTpmClose(RTTPM hTpm)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ TBS_RESULT rcTbs = g_pfnTbsiContextClose(pThis->hCtx);
+ Assert(rcTbs == TBS_SUCCESS); RT_NOREF(rcTbs);
+
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTTPMVERSION) RTTpmGetVersion(RTTPM hTpm)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, RTTPMVERSION_INVALID);
+ return pThis->enmTpmVers;
+}
+
+
+RTDECL(uint32_t) RTTpmGetLocalityMax(RTTPM hTpm)
+{
+ RT_NOREF(hTpm);
+ return 0; /* Only TPM locality 0 is supported. */
+}
+
+
+RTDECL(int) RTTpmReqCancel(RTTPM hTpm)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ TBS_RESULT rcTbs = g_pfnTbsiCancelCommands(pThis->hCtx);
+ if (rcTbs != TBS_SUCCESS)
+ return VERR_DEV_IO_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTpmReqExec(RTTPM hTpm, uint8_t bLoc, const void *pvReq, size_t cbReq,
+ void *pvResp, size_t cbRespMax, size_t *pcbResp)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvReq, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvResp, VERR_INVALID_POINTER);
+ AssertReturn(cbReq && cbRespMax, VERR_INVALID_PARAMETER);
+ AssertReturn(cbReq == (UINT32)cbReq && cbRespMax == (UINT32)cbRespMax, VERR_BUFFER_OVERFLOW);
+ AssertReturn(bLoc == 0, VERR_NOT_SUPPORTED); /* TBS doesn't support another locality than 0. */
+
+ UINT32 cbResult = (UINT32)cbRespMax;
+ TBS_RESULT rcTbs = g_pfnTbsiSubmitCommands(pThis->hCtx, 0 /*Locality*/, TBS_COMMAND_PRIORITY_NORMAL,
+ (const BYTE *)pvReq, (UINT32)cbReq, (BYTE *)pvResp, &cbResult);
+ if (rcTbs == TBS_SUCCESS)
+ {
+ if (pcbResp)
+ *pcbResp = cbResult;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_DEV_IO_ERROR;
+}
+
diff --git a/src/VBox/Runtime/r3/win/utf16locale-win.cpp b/src/VBox/Runtime/r3/win/utf16locale-win.cpp
new file mode 100644
index 00000000..d282572a
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/utf16locale-win.cpp
@@ -0,0 +1,58 @@
+/* $Id: utf16locale-win.cpp $ */
+/** @file
+ * IPRT - UTF-16 Locale Specific Manipulation, Win32.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_UTF16
+#include <iprt/win/windows.h>
+
+#include <iprt/utf16.h>
+
+
+RTDECL(int) RTUtf16LocaleICmp(PCRTUTF16 pusz1, PCRTUTF16 pusz2)
+{
+ if (pusz1 == pusz2)
+ return 0;
+ if (pusz1 == NULL)
+ return -1;
+ if (pusz2 == NULL)
+ return 1;
+
+ return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pusz1, -1, pusz2, -1) - 2;
+}
+
diff --git a/src/VBox/Runtime/r3/win/utf8-win.cpp b/src/VBox/Runtime/r3/win/utf8-win.cpp
new file mode 100644
index 00000000..7defa137
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/utf8-win.cpp
@@ -0,0 +1,204 @@
+/* $Id: utf8-win.cpp $ */
+/** @file
+ * IPRT - UTF8 helpers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_UTF8
+#include <iprt/win/windows.h>
+
+#include <iprt/string.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/utf16.h>
+
+
+
+RTR3DECL(int) RTStrUtf8ToCurrentCPTag(char **ppszString, const char *pszString, const char *pszTag)
+{
+ return RTStrUtf8ToCurrentCPExTag(ppszString, pszString, RTSTR_MAX, pszTag);
+}
+
+
+RTR3DECL(int) RTStrUtf8ToCurrentCPExTag(char **ppszString, const char *pszString, size_t cchString, const char *pszTag)
+{
+ Assert(ppszString);
+ Assert(pszString);
+ *ppszString = NULL;
+
+ /*
+ * If the ANSI codepage (CP_ACP) is UTF-8, no translation is needed.
+ * Same goes for empty strings.
+ */
+ if ( cchString == 0
+ || *pszString == '\0')
+ return RTStrDupNExTag(ppszString, pszString, 0, pszTag);
+ if (GetACP() == CP_UTF8)
+ {
+ int rc = RTStrValidateEncodingEx(pszString, cchString, 0);
+ AssertRCReturn(rc, rc);
+ return RTStrDupNExTag(ppszString, pszString, cchString, pszTag);
+ }
+
+ /*
+ * Convert to wide char first.
+ */
+ PRTUTF16 pwszString = NULL;
+ int rc = RTStrToUtf16Ex(pszString, cchString, &pwszString, 0, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * First calc result string length.
+ */
+ int cbResult = WideCharToMultiByte(CP_ACP, 0, pwszString, -1, NULL, 0, NULL, NULL);
+ if (cbResult > 0)
+ {
+ /*
+ * Alloc space for result buffer.
+ */
+ LPSTR lpString = (LPSTR)RTMemTmpAllocTag(cbResult, pszTag);
+ if (lpString)
+ {
+ /*
+ * Do the translation.
+ */
+ if (WideCharToMultiByte(CP_ACP, 0, pwszString, -1, lpString, cbResult, NULL, NULL) > 0)
+ {
+ /* ok */
+ *ppszString = lpString;
+ RTMemTmpFree(pwszString);
+ return VINF_SUCCESS;
+ }
+
+ /* translation error */
+ int iLastErr = GetLastError();
+ AssertMsgFailed(("Unicode to ACP translation failed. lasterr=%d\n", iLastErr));
+ rc = RTErrConvertFromWin32(iLastErr);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ RTMemTmpFree(lpString);
+ }
+ else
+ {
+ /* translation error */
+ int iLastErr = GetLastError();
+ AssertMsgFailed(("Unicode to ACP translation failed lasterr=%d\n", iLastErr));
+ rc = RTErrConvertFromWin32(iLastErr);
+ }
+ RTMemTmpFree(pwszString);
+ return rc;
+}
+
+static int rtStrCPToUtf8Tag(char **ppszString, const char *pszString, uint32_t uCodePage, const char *pszTag)
+{
+ Assert(ppszString);
+ Assert(pszString);
+ *ppszString = NULL;
+
+ /*
+ * If the ANSI codepage (CP_ACP) is UTF-8, no translation is needed.
+ * Same goes for empty strings.
+ */
+ if (*pszString == '\0')
+ return RTStrDupExTag(ppszString, pszString, pszTag);
+ if (GetACP() == CP_UTF8)
+ {
+ int rc = RTStrValidateEncoding(pszString);
+ AssertRCReturn(rc, rc);
+ return RTStrDupExTag(ppszString, pszString, pszTag);
+ }
+
+ /** @todo is there a quicker way? Currently: ACP -> UTF-16 -> UTF-8 */
+
+ /*
+ * First calc result string length.
+ */
+ int rc;
+ int cwc = MultiByteToWideChar((UINT)uCodePage, 0, pszString, -1, NULL, 0);
+ if (cwc > 0)
+ {
+ /*
+ * Alloc space for result buffer.
+ */
+ PRTUTF16 pwszString = (PRTUTF16)RTMemTmpAlloc(cwc * sizeof(RTUTF16));
+ if (pwszString)
+ {
+ /*
+ * Do the translation.
+ */
+ if (MultiByteToWideChar((UINT)uCodePage, 0, pszString, -1, pwszString, cwc) > 0)
+ {
+ /*
+ * Now we got UTF-16, convert it to UTF-8
+ */
+ rc = RTUtf16ToUtf8(pwszString, ppszString);
+ RTMemTmpFree(pwszString);
+ return rc;
+ }
+ RTMemTmpFree(pwszString);
+ /* translation error */
+ int iLastErr = GetLastError();
+ AssertMsgFailed(("ACP to Unicode translation failed. lasterr=%d\n", iLastErr));
+ rc = RTErrConvertFromWin32(iLastErr);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ else
+ {
+ /* translation error */
+ int iLastErr = GetLastError();
+ AssertMsgFailed(("Unicode to ACP translation failed lasterr=%d\n", iLastErr));
+ rc = RTErrConvertFromWin32(iLastErr);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrCurrentCPToUtf8Tag(char **ppszString, const char *pszString, const char *pszTag)
+{
+ return rtStrCPToUtf8Tag(ppszString, pszString, CP_ACP, pszTag);
+}
+
+
+RTR3DECL(int) RTStrConsoleCPToUtf8Tag(char **ppszString, const char *pszString, const char *pszTag)
+{
+ return rtStrCPToUtf8Tag(ppszString, pszString, GetConsoleCP(), pszTag);
+}
diff --git a/src/VBox/Runtime/r3/win/uuid-win.cpp b/src/VBox/Runtime/r3/win/uuid-win.cpp
new file mode 100644
index 00000000..eebeb0c0
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/uuid-win.cpp
@@ -0,0 +1,195 @@
+/* $Id: uuid-win.cpp $ */
+/** @file
+ * IPRT - UUID, Windows implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_UUID
+#include <iprt/win/windows.h>
+
+#include <iprt/uuid.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTUuidClear(PRTUUID pUuid)
+{
+ /* check params */
+ AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
+
+ return RTErrConvertFromWin32(UuidCreateNil((UUID *)pUuid));
+}
+
+
+RTDECL(bool) RTUuidIsNull(PCRTUUID pUuid)
+{
+ /* check params */
+ AssertPtrReturn(pUuid, true);
+
+ RPC_STATUS status;
+ return !!UuidIsNil((UUID *)pUuid, &status);
+}
+
+
+RTDECL(int) RTUuidCompare(PCRTUUID pUuid1, PCRTUUID pUuid2)
+{
+ /*
+ * Special cases.
+ */
+ if (pUuid1 == pUuid2)
+ return 0;
+ if (!pUuid1)
+ return RTUuidIsNull(pUuid2) ? 0 : -1;
+ if (!pUuid2)
+ return RTUuidIsNull(pUuid1) ? 0 : 1;
+ AssertPtrReturn(pUuid1, -1);
+ AssertPtrReturn(pUuid2, 1);
+
+ /*
+ * Hand the rest to the Windows API.
+ */
+ RPC_STATUS status;
+ return UuidCompare((UUID *)pUuid1, (UUID *)pUuid2, &status);
+}
+
+
+RTDECL(int) RTUuidCompareStr(PCRTUUID pUuid1, const char *pszString2)
+{
+ /* check params */
+ AssertPtrReturn(pUuid1, -1);
+ AssertPtrReturn(pszString2, 1);
+
+ /*
+ * Try convert the string to a UUID and then compare the two.
+ */
+ RTUUID Uuid2;
+ int rc = RTUuidFromStr(&Uuid2, pszString2);
+ AssertRCReturn(rc, 1);
+
+ return RTUuidCompare(pUuid1, &Uuid2);
+}
+
+
+RTDECL(int) RTUuidCompare2Strs(const char *pszString1, const char *pszString2)
+{
+ RTUUID Uuid1;
+ RTUUID Uuid2;
+ int rc;
+
+ /* check params */
+ AssertPtrReturn(pszString1, -1);
+ AssertPtrReturn(pszString2, 1);
+
+ /*
+ * Try convert the strings to UUIDs and then compare them.
+ */
+ rc = RTUuidFromStr(&Uuid1, pszString1);
+ AssertRCReturn(rc, -1);
+
+ rc = RTUuidFromStr(&Uuid2, pszString2);
+ AssertRCReturn(rc, 1);
+
+ return RTUuidCompare(&Uuid1, &Uuid2);
+}
+
+
+RTDECL(int) RTUuidToStr(PCRTUUID pUuid, char *pszString, size_t cchString)
+{
+ /* check params */
+ AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszString, VERR_INVALID_POINTER);
+ AssertReturn(cchString >= RTUUID_STR_LENGTH, VERR_INVALID_PARAMETER);
+
+ /*
+ * Try convert it.
+ *
+ * The API allocates a new string buffer for us, so we can do our own
+ * buffer overflow handling.
+ */
+ RPC_STATUS Status;
+ unsigned char *pszTmpStr = NULL;
+#ifdef RPC_UNICODE_SUPPORTED
+ /* always use ASCII version! */
+ Status = UuidToStringA((UUID *)pUuid, &pszTmpStr);
+#else
+ Status = UuidToString((UUID *)pUuid, &pszTmpStr);
+#endif
+ if (Status != RPC_S_OK)
+ return RTErrConvertFromWin32(Status);
+
+ /* copy it. */
+ int rc = VINF_SUCCESS;
+ size_t cchTmpStr = strlen((char *)pszTmpStr);
+ if (cchTmpStr < cchString)
+ memcpy(pszString, pszTmpStr, cchTmpStr + 1);
+ else
+ {
+ AssertFailed();
+ rc = ERROR_BUFFER_OVERFLOW;
+ }
+
+ /* free buffer */
+#ifdef RPC_UNICODE_SUPPORTED
+ /* always use ASCII version! */
+ RpcStringFreeA(&pszTmpStr);
+#else
+ RpcStringFree(&pszTmpStr);
+#endif
+
+ /* all done */
+ return rc;
+}
+
+
+RTDECL(int) RTUuidFromStr(PRTUUID pUuid, const char *pszString)
+{
+ /* check params */
+ AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszString, VERR_INVALID_POINTER);
+
+ RPC_STATUS rc;
+#ifdef RPC_UNICODE_SUPPORTED
+ /* always use ASCII version! */
+ rc = UuidFromStringA((unsigned char *)pszString, (UUID *)pUuid);
+#else
+ rc = UuidFromString((unsigned char *)pszString, (UUID *)pUuid);
+#endif
+
+ return RTErrConvertFromWin32(rc);
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h
new file mode 100644
index 00000000..1708ee52
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h
@@ -0,0 +1,43 @@
+
+COMMENT("XP SP2 / W2K3 SP1 / VISTA")
+MAKE_IMPORT_ENTRY(6,0, DecodePointer, 4)
+MAKE_IMPORT_ENTRY(6,0, EncodePointer, 4)
+COMMENT("XP")
+MAKE_IMPORT_ENTRY(5,1, CreateIoCompletionPort, 16)
+MAKE_IMPORT_ENTRY(5,1, GetQueuedCompletionStatus, 20)
+MAKE_IMPORT_ENTRY(5,1, HeapSetInformation, 16)
+MAKE_IMPORT_ENTRY(5,1, HeapQueryInformation, 20)
+MAKE_IMPORT_ENTRY(5,1, InitializeSListHead, 4)
+MAKE_IMPORT_ENTRY(5,1, InterlockedFlushSList, 4)
+MAKE_IMPORT_ENTRY(5,1, InterlockedPopEntrySList, 4)
+MAKE_IMPORT_ENTRY(5,1, InterlockedPushEntrySList, 8)
+MAKE_IMPORT_ENTRY(5,1, PostQueuedCompletionStatus, 16)
+MAKE_IMPORT_ENTRY(5,1, QueryDepthSList, 4)
+COMMENT("W2K")
+MAKE_IMPORT_ENTRY(5,0, CreateTimerQueue, 0)
+MAKE_IMPORT_ENTRY(5,0, CreateTimerQueueTimer, 28)
+MAKE_IMPORT_ENTRY(5,0, DeleteTimerQueueTimer, 12)
+MAKE_IMPORT_ENTRY(5,0, VerSetConditionMask, 16)
+COMMENT("NT 4 SP4+")
+MAKE_IMPORT_ENTRY(5,0, VerifyVersionInfoA, 16)
+COMMENT("NT 4 SP3+")
+MAKE_IMPORT_ENTRY(5,0, InitializeCriticalSectionAndSpinCount, 8)
+COMMENT("NT 4")
+MAKE_IMPORT_ENTRY(4,0, IsProcessorFeaturePresent, 4)
+MAKE_IMPORT_ENTRY(4,0, CancelIo, 4)
+COMMENT("NT 3.51")
+MAKE_IMPORT_ENTRY(3,51, IsDebuggerPresent, 0)
+MAKE_IMPORT_ENTRY(3,51, GetSystemTimeAsFileTime, 4)
+COMMENT("NT 3.50")
+MAKE_IMPORT_ENTRY(3,50, GetVersionExA, 4)
+MAKE_IMPORT_ENTRY(3,50, GetVersionExW, 4)
+MAKE_IMPORT_ENTRY(3,50, GetEnvironmentStringsW, 0)
+MAKE_IMPORT_ENTRY(3,50, FreeEnvironmentStringsW, 4)
+MAKE_IMPORT_ENTRY(3,50, GetLocaleInfoA, 16)
+MAKE_IMPORT_ENTRY(3,50, EnumSystemLocalesA, 8)
+MAKE_IMPORT_ENTRY(3,50, IsValidLocale, 8)
+MAKE_IMPORT_ENTRY(3,50, SetThreadAffinityMask, 8)
+MAKE_IMPORT_ENTRY(3,50, GetProcessAffinityMask, 12)
+MAKE_IMPORT_ENTRY(3,50, GetHandleInformation, 8)
+MAKE_IMPORT_ENTRY(3,50, SetHandleInformation, 12)
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h
new file mode 100644
index 00000000..a65e6392
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h
@@ -0,0 +1,42 @@
+MAKE_IMPORT_ENTRY(6,0, DecodePointer, 4)
+MAKE_IMPORT_ENTRY(6,0, EncodePointer, 4)
+MAKE_IMPORT_ENTRY(5,1, InitializeSListHead, 4)
+MAKE_IMPORT_ENTRY(5,1, GetModuleHandleExW, 12)
+MAKE_IMPORT_ENTRY(5,0, VerifyVersionInfoA, 16)
+MAKE_IMPORT_ENTRY(5,0, SetFilePointerEx, 20)
+MAKE_IMPORT_ENTRY(5,0, GetFileSizeEx, 8)
+MAKE_IMPORT_ENTRY(5,0, InitializeCriticalSectionAndSpinCount, 8)
+MAKE_IMPORT_ENTRY(4,0, FindFirstFileExW, 24)
+MAKE_IMPORT_ENTRY(4,0, IsProcessorFeaturePresent, 4)
+MAKE_IMPORT_ENTRY(4,0, CancelIo, 4)
+MAKE_IMPORT_ENTRY(3,51, FreeLibraryAndExitThread, 8)
+MAKE_IMPORT_ENTRY(3,51, IsDebuggerPresent, 0)
+MAKE_IMPORT_ENTRY(3,51, GetSystemTimeAsFileTime, 4)
+MAKE_IMPORT_ENTRY(3,50, EnumSystemLocalesW, 8)
+MAKE_IMPORT_ENTRY(3,50, GetVersionExA, 4)
+MAKE_IMPORT_ENTRY(3,50, GetVersionExW, 4)
+MAKE_IMPORT_ENTRY(3,50, GetEnvironmentStringsW, 0)
+MAKE_IMPORT_ENTRY(3,50, FreeEnvironmentStringsW, 4)
+MAKE_IMPORT_ENTRY(3,50, IsValidLocale, 8)
+MAKE_IMPORT_ENTRY(3,50, SetThreadAffinityMask, 8)
+MAKE_IMPORT_ENTRY(3,50, GetProcessAffinityMask, 12)
+MAKE_IMPORT_ENTRY(3,50, GetHandleInformation, 8)
+MAKE_IMPORT_ENTRY(3,50, SetHandleInformation, 12)
+
+COMMENT("Not needed (usually)")
+MAKE_IMPORT_ENTRY(5,1, HeapSetInformation, 16)
+MAKE_IMPORT_ENTRY(5,1, HeapQueryInformation, 20)
+MAKE_IMPORT_ENTRY(5,1, InterlockedFlushSList, 4)
+MAKE_IMPORT_ENTRY(5,1, InterlockedPopEntrySList, 4)
+MAKE_IMPORT_ENTRY(5,1, InterlockedPushEntrySList, 8)
+MAKE_IMPORT_ENTRY(5,1, QueryDepthSList, 4)
+MAKE_IMPORT_ENTRY(5,0, CreateTimerQueue, 0)
+MAKE_IMPORT_ENTRY(5,0, CreateTimerQueueTimer, 28)
+MAKE_IMPORT_ENTRY(5,0, DeleteTimerQueueTimer, 12)
+MAKE_IMPORT_ENTRY(5,0, VerSetConditionMask, 16)
+MAKE_IMPORT_ENTRY(3,51, PostQueuedCompletionStatus, 16)
+MAKE_IMPORT_ENTRY(3,51, CreateIoCompletionPort, 16)
+MAKE_IMPORT_ENTRY(3,51, GetQueuedCompletionStatus, 20)
+MAKE_IMPORT_ENTRY(3,50, GetLocaleInfoA, 16)
+MAKE_IMPORT_ENTRY(3,50, EnumSystemLocalesA, 8)
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm
new file mode 100644
index 00000000..eb1449c3
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm
@@ -0,0 +1,56 @@
+; $Id: vcc-fakes-kernel32-A.asm $
+;; @file
+; IPRT - Wrappers for kernel32 APIs missing in NT4 and earlier.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "vcc-fakes.mac"
+
+%define FAKE_MODULE_NAME kernel32
+
+BEGINDATA
+GLOBALNAME vcc100_kernel32_fakes_asm
+
+%ifdef VCC_FAKES_TARGET_VCC100
+ %include "vcc-fakes-kernel32-100.h"
+%elifdef VCC_FAKES_TARGET_VCC140
+ %include "vcc-fakes-kernel32-141.h"
+%elifdef VCC_FAKES_TARGET_VCC141
+ %include "vcc-fakes-kernel32-141.h"
+%elifdef VCC_FAKES_TARGET_VCC142
+ %include "vcc-fakes-kernel32-141.h"
+%else
+ %error "PORT ME!"
+%endif
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp
new file mode 100644
index 00000000..697f22ca
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp
@@ -0,0 +1,891 @@
+/* $Id: vcc-fakes-kernel32.cpp $ */
+/** @file
+ * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#ifdef DEBUG
+# include <stdio.h> /* _snprintf */
+#endif
+
+#ifndef RT_ARCH_X86
+# error "This code is X86 only"
+#endif
+
+#include <iprt/nt/nt-and-windows.h>
+
+#include "vcc-fakes.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifndef HEAP_STANDARD
+# define HEAP_STANDARD 0
+#endif
+
+
+/** Declare a kernel32 API.
+ * @note We are not exporting them as that causes duplicate symbol troubles in
+ * the OpenGL bits. */
+#define DECL_KERNEL32(a_Type) extern "C" a_Type WINAPI
+
+#if defined(WDK_NTDDI_VERSION) && defined(NTDDI_WIN10)
+# if WDK_NTDDI_VERSION >= NTDDI_WIN10 /* In Windows 10 SDK the 'Sequence' field has been renamed to 'CpuId'. */
+# define SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID
+# endif
+#endif
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static volatile bool g_fInitialized = false;
+#define MAKE_IMPORT_ENTRY(a_uMajorVer, a_uMinorVer, a_Name, a_cb) DECLARE_FUNCTION_POINTER(a_Name, a_cb)
+#ifdef VCC_FAKES_TARGET_VCC100
+# include "vcc-fakes-kernel32-100.h"
+#elif defined(VCC_FAKES_TARGET_VCC140)
+# include "vcc-fakes-kernel32-141.h"
+#elif defined(VCC_FAKES_TARGET_VCC141)
+# include "vcc-fakes-kernel32-141.h"
+#elif defined(VCC_FAKES_TARGET_VCC142)
+# include "vcc-fakes-kernel32-141.h"
+#else
+# error "Port me!"
+#endif
+
+
+static BOOL FakeSetLastErrorFromNtStatus(NTSTATUS rcNt)
+{
+ DWORD dwErr;
+ switch (rcNt)
+ {
+ case STATUS_INVALID_PARAMETER:
+ case STATUS_INVALID_PARAMETER_1:
+ case STATUS_INVALID_PARAMETER_2:
+ case STATUS_INVALID_PARAMETER_3:
+ case STATUS_INVALID_PARAMETER_4:
+ case STATUS_INVALID_PARAMETER_5:
+ case STATUS_INVALID_PARAMETER_6:
+ case STATUS_INVALID_PARAMETER_7:
+ case STATUS_INVALID_PARAMETER_8:
+ case STATUS_INVALID_PARAMETER_9:
+ case STATUS_INVALID_PARAMETER_10:
+ case STATUS_INVALID_PARAMETER_11:
+ case STATUS_INVALID_PARAMETER_12:
+ dwErr = ERROR_INVALID_PARAMETER;
+ break;
+
+ case STATUS_INVALID_HANDLE:
+ dwErr = ERROR_INVALID_HANDLE;
+ break;
+
+ case STATUS_ACCESS_DENIED:
+ dwErr = ERROR_ACCESS_DENIED;
+ break;
+
+ default:
+ dwErr = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ SetLastError(dwErr);
+ return FALSE;
+}
+
+
+
+DECL_KERNEL32(PVOID) Fake_DecodePointer(PVOID pvEncoded)
+{
+ return pvEncoded;
+}
+
+
+DECL_KERNEL32(PVOID) Fake_EncodePointer(PVOID pvNative)
+{
+ return pvNative;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION pCritSect, DWORD cSpin)
+{
+ RT_NOREF(cSpin);
+ InitializeCriticalSection(pCritSect);
+ return TRUE;
+}
+
+
+DECL_KERNEL32(HANDLE) Fake_CreateIoCompletionPort(HANDLE hFile, HANDLE hExistingCompletionPort, ULONG_PTR uCompletionKey,
+ DWORD cConcurrentThreads)
+{
+ RT_NOREF(hFile, hExistingCompletionPort, uCompletionKey, cConcurrentThreads);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return NULL;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_GetQueuedCompletionStatus(HANDLE hCompletionPort, PDWORD_PTR pcbTransfered, PULONG_PTR puCompletionKey,
+ LPOVERLAPPED *ppOverlapped, DWORD cMs)
+{
+ RT_NOREF(hCompletionPort, pcbTransfered, puCompletionKey, ppOverlapped, cMs);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_PostQueuedCompletionStatus(HANDLE hCompletionPort, DWORD cbTransfered, ULONG_PTR uCompletionKey,
+ LPOVERLAPPED pOverlapped)
+{
+ RT_NOREF(hCompletionPort, cbTransfered, uCompletionKey, pOverlapped);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_HeapSetInformation(HANDLE hHeap, HEAP_INFORMATION_CLASS enmInfoClass, PVOID pvBuf, SIZE_T cbBuf)
+{
+ RT_NOREF(hHeap);
+ if (enmInfoClass == HeapCompatibilityInformation)
+ {
+ if ( cbBuf != sizeof(ULONG)
+ || !pvBuf
+ || *(PULONG)pvBuf == HEAP_STANDARD
+ )
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_HeapQueryInformation(HANDLE hHeap, HEAP_INFORMATION_CLASS enmInfoClass,
+ PVOID pvBuf, SIZE_T cbBuf, PSIZE_T pcbRet)
+{
+ RT_NOREF(hHeap);
+ if (enmInfoClass == HeapCompatibilityInformation)
+ {
+ *pcbRet = sizeof(ULONG);
+ if (cbBuf < sizeof(ULONG) || !pvBuf)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+ *(PULONG)pvBuf = HEAP_STANDARD;
+ return TRUE;
+ }
+
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+}
+
+
+/* These are used by INTEL\mt_obj\Timer.obj: */
+
+DECL_KERNEL32(HANDLE) Fake_CreateTimerQueue(void)
+{
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return NULL;
+}
+
+DECL_KERNEL32(BOOL) Fake_CreateTimerQueueTimer(PHANDLE phTimer, HANDLE hTimerQueue, WAITORTIMERCALLBACK pfnCallback, PVOID pvUser,
+ DWORD msDueTime, DWORD msPeriod, ULONG fFlags)
+{
+ NOREF(phTimer); NOREF(hTimerQueue); NOREF(pfnCallback); NOREF(pvUser); NOREF(msDueTime); NOREF(msPeriod); NOREF(fFlags);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+DECL_KERNEL32(BOOL) Fake_DeleteTimerQueueTimer(HANDLE hTimerQueue, HANDLE hTimer, HANDLE hEvtCompletion)
+{
+ NOREF(hTimerQueue); NOREF(hTimer); NOREF(hEvtCompletion);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+/* This is used by several APIs. */
+
+DECL_KERNEL32(VOID) Fake_InitializeSListHead(PSLIST_HEADER pHead)
+{
+ pHead->Alignment = 0;
+}
+
+
+DECL_KERNEL32(PSLIST_ENTRY) Fake_InterlockedFlushSList(PSLIST_HEADER pHead)
+{
+ PSLIST_ENTRY pRet = NULL;
+ if (pHead->Next.Next)
+ {
+ for (;;)
+ {
+ SLIST_HEADER OldHead = *pHead;
+ SLIST_HEADER NewHead;
+ NewHead.Alignment = 0;
+#ifdef SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID
+ NewHead.CpuId = OldHead.CpuId + 1;
+#else
+ NewHead.Sequence = OldHead.Sequence + 1;
+#endif
+ if (ASMAtomicCmpXchgU64(&pHead->Alignment, NewHead.Alignment, OldHead.Alignment))
+ {
+ pRet = OldHead.Next.Next;
+ break;
+ }
+ }
+ }
+ return pRet;
+}
+
+DECL_KERNEL32(PSLIST_ENTRY) Fake_InterlockedPopEntrySList(PSLIST_HEADER pHead)
+{
+ PSLIST_ENTRY pRet = NULL;
+ for (;;)
+ {
+ SLIST_HEADER OldHead = *pHead;
+ pRet = OldHead.Next.Next;
+ if (pRet)
+ {
+ SLIST_HEADER NewHead;
+ __try
+ {
+ NewHead.Next.Next = pRet->Next;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ continue;
+ }
+ NewHead.Depth = OldHead.Depth - 1;
+#ifdef SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID
+ NewHead.CpuId = OldHead.CpuId + 1;
+#else
+ NewHead.Sequence = OldHead.Sequence + 1;
+#endif
+ if (ASMAtomicCmpXchgU64(&pHead->Alignment, NewHead.Alignment, OldHead.Alignment))
+ break;
+ }
+ else
+ break;
+ }
+ return pRet;
+}
+
+DECL_KERNEL32(PSLIST_ENTRY) Fake_InterlockedPushEntrySList(PSLIST_HEADER pHead, PSLIST_ENTRY pEntry)
+{
+ PSLIST_ENTRY pRet = NULL;
+ for (;;)
+ {
+ SLIST_HEADER OldHead = *pHead;
+ pRet = OldHead.Next.Next;
+ pEntry->Next = pRet;
+ SLIST_HEADER NewHead;
+ NewHead.Next.Next = pEntry;
+ NewHead.Depth = OldHead.Depth + 1;
+#ifdef SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID
+ NewHead.CpuId = OldHead.CpuId + 1;
+#else
+ NewHead.Sequence = OldHead.Sequence + 1;
+#endif
+ if (ASMAtomicCmpXchgU64(&pHead->Alignment, NewHead.Alignment, OldHead.Alignment))
+ break;
+ }
+ return pRet;
+}
+
+DECL_KERNEL32(WORD) Fake_QueryDepthSList(PSLIST_HEADER pHead)
+{
+ return pHead->Depth;
+}
+
+
+/* curl drags these in: */
+DECL_KERNEL32(BOOL) Fake_VerifyVersionInfoA(LPOSVERSIONINFOEXA pInfo, DWORD fTypeMask, DWORDLONG fConditionMask)
+{
+ OSVERSIONINFOEXA VerInfo;
+ RT_ZERO(VerInfo);
+ VerInfo.dwOSVersionInfoSize = sizeof(VerInfo);
+ if (!GetVersionExA((OSVERSIONINFO *)&VerInfo))
+ {
+ RT_ZERO(VerInfo);
+ VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ BOOL fRet = GetVersionExA((OSVERSIONINFO *)&VerInfo);
+ if (fRet)
+ { /* likely */ }
+ else
+ {
+ MY_ASSERT(false, "VerifyVersionInfoA: #1");
+ return FALSE;
+ }
+ }
+
+ BOOL fRet = TRUE;
+ for (unsigned i = 0; i < 8 && fRet; i++)
+ if (fTypeMask & RT_BIT_32(i))
+ {
+ uint32_t uLeft, uRight;
+ switch (RT_BIT_32(i))
+ {
+#define MY_CASE(a_Num, a_Member) case a_Num: uLeft = VerInfo.a_Member; uRight = pInfo->a_Member; break
+ MY_CASE(VER_MINORVERSION, dwMinorVersion);
+ MY_CASE(VER_MAJORVERSION, dwMajorVersion);
+ MY_CASE(VER_BUILDNUMBER, dwBuildNumber);
+ MY_CASE(VER_PLATFORMID, dwPlatformId);
+ MY_CASE(VER_SERVICEPACKMINOR, wServicePackMinor);
+ MY_CASE(VER_SERVICEPACKMAJOR, wServicePackMinor);
+ MY_CASE(VER_SUITENAME, wSuiteMask);
+ MY_CASE(VER_PRODUCT_TYPE, wProductType);
+#undef MY_CASE
+ default: uLeft = uRight = 0; MY_ASSERT(false, "VerifyVersionInfoA: #2");
+ }
+ switch ((uint8_t)(fConditionMask >> (i*8)))
+ {
+ case VER_EQUAL: fRet = uLeft == uRight; break;
+ case VER_GREATER: fRet = uLeft > uRight; break;
+ case VER_GREATER_EQUAL: fRet = uLeft >= uRight; break;
+ case VER_LESS: fRet = uLeft < uRight; break;
+ case VER_LESS_EQUAL: fRet = uLeft <= uRight; break;
+ case VER_AND: fRet = (uLeft & uRight) == uRight; break;
+ case VER_OR: fRet = (uLeft & uRight) != 0; break;
+ default: fRet = FALSE; MY_ASSERT(false, "VerifyVersionInfoA: #3"); break;
+ }
+ }
+
+ return fRet;
+}
+
+
+DECL_KERNEL32(ULONGLONG) Fake_VerSetConditionMask(ULONGLONG fConditionMask, DWORD fTypeMask, BYTE bOperator)
+{
+ for (unsigned i = 0; i < 8; i++)
+ if (fTypeMask & RT_BIT_32(i))
+ {
+ uint64_t fMask = UINT64_C(0xff) << (i*8);
+ fConditionMask &= ~fMask;
+ fConditionMask |= (uint64_t)bOperator << (i*8);
+
+ }
+ return fConditionMask;
+}
+
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 5.0 (windows 2000) */
+DECL_KERNEL32(BOOL) Fake_GetModuleHandleExW(DWORD dwFlags, LPCWSTR pwszModuleName, HMODULE *phModule)
+{
+ HMODULE hmod;
+ if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)
+ {
+ /** @todo search the loader list. */
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+ else
+ {
+ hmod = GetModuleHandleW(pwszModuleName);
+ if (!hmod)
+ return FALSE;
+ }
+
+ /*
+ * Get references to the module.
+ */
+ if ( !(dwFlags & (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_PIN))
+ && GetModuleHandleW(NULL) != hmod)
+ {
+ WCHAR wszModule[MAX_PATH];
+ if (GetModuleFileNameW(hmod, wszModule, RT_ELEMENTS(wszModule)) > 0)
+ {
+ if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN)
+ {
+ uint32_t cRefs = 32;
+ while (cRefs-- > 0)
+ LoadLibraryW(wszModule);
+ }
+ else if (!LoadLibraryW(wszModule))
+ return FALSE;
+ }
+ }
+
+ *phModule = hmod;
+ return TRUE;
+}
+#endif /* VCC_FAKES_TARGET >= 141 */
+
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 5.0 (windows 2000) */
+DECL_KERNEL32(BOOL) Fake_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offDistanceToMove,
+ PLARGE_INTEGER pNewFilePointer, DWORD dwMoveMethod)
+{
+ NTSTATUS rcNt;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ FILE_POSITION_INFORMATION PosInfo;
+ switch (dwMoveMethod)
+ {
+ case FILE_BEGIN:
+ PosInfo.CurrentByteOffset = offDistanceToMove;
+ break;
+
+ case FILE_CURRENT:
+ PosInfo.CurrentByteOffset.QuadPart = INT64_MAX;
+ rcNt = NtQueryInformationFile(hFile, &Ios, &PosInfo, sizeof(PosInfo), FilePositionInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ PosInfo.CurrentByteOffset.QuadPart += offDistanceToMove.QuadPart;
+ break;
+ }
+ return FakeSetLastErrorFromNtStatus(rcNt);
+
+ case FILE_END:
+ {
+ FILE_STANDARD_INFO StdInfo = {{0}};
+ rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ PosInfo.CurrentByteOffset.QuadPart = offDistanceToMove.QuadPart + StdInfo.EndOfFile.QuadPart;
+ break;
+ }
+ return FakeSetLastErrorFromNtStatus(rcNt);
+ }
+
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ rcNt = NtSetInformationFile(hFile, &Ios, &PosInfo, sizeof(PosInfo), FilePositionInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (pNewFilePointer)
+ *pNewFilePointer = PosInfo.CurrentByteOffset;
+ return TRUE;
+ }
+ return FakeSetLastErrorFromNtStatus(rcNt);
+}
+#endif /* VCC_FAKES_TARGET >= 140 */
+
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 5.0 (windows 2000) */
+DECL_KERNEL32(BOOL) Fake_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
+{
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ FILE_STANDARD_INFO StdInfo = {{0}};
+ NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ *pcbFile = StdInfo.EndOfFile;
+ return TRUE;
+ }
+ return FakeSetLastErrorFromNtStatus(rcNt);
+}
+#endif /* VCC_FAKES_TARGET >= 140 */
+
+
+
+
+/*
+ * NT 3.51 stuff.
+ */
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 4.0 */
+DECL_KERNEL32(HANDLE) Fake_FindFirstFileExW(LPCWSTR pwszFileName, FINDEX_INFO_LEVELS enmInfoLevel, LPVOID pvFindFileData,
+ FINDEX_SEARCH_OPS enmSearchOp, LPVOID pvSearchFilter, DWORD dwAdditionalFlags)
+{
+ // STL: FindFirstFileExW(, FindExInfoBasic, , FindExSearchNameMatch, NULL, 0);
+ // CRT/_findfile: FindFirstFileExW(, FindExInfoStandard, , FindExSearchNameMatch, NULL, 0);
+ // CRT/argv_wildcards: FindFirstFileExW(, FindExInfoStandard, , FindExSearchNameMatch, NULL, 0);
+ MY_ASSERT_STMT_RETURN(dwAdditionalFlags == 0, SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE);
+ MY_ASSERT_STMT_RETURN(pvSearchFilter == NULL, SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE);
+ MY_ASSERT_STMT_RETURN(enmSearchOp == FindExSearchNameMatch, SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE);
+ MY_ASSERT_STMT_RETURN(enmInfoLevel == FindExInfoStandard || enmInfoLevel == FindExInfoBasic,
+ SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE);
+
+ return FindFirstFileW(pwszFileName, (WIN32_FIND_DATAW *)pvFindFileData);
+}
+#endif /* VCC_FAKES_TARGET >= 140 */
+
+
+DECL_KERNEL32(BOOL) Fake_IsProcessorFeaturePresent(DWORD enmProcessorFeature)
+{
+ /* Could make more of an effort here... */
+ RT_NOREF(enmProcessorFeature);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_CancelIo(HANDLE hHandle)
+{
+ /* All NT versions the NTDLL API this corresponds to. */
+ RESOLVE_NTDLL_API(NtCancelIoFile);
+ if (pfnNtCancelIoFile)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = pfnNtCancelIoFile(hHandle, &Ios);
+ if (RT_SUCCESS(rcNt))
+ return TRUE;
+ if (rcNt == STATUS_INVALID_HANDLE)
+ SetLastError(ERROR_INVALID_HANDLE);
+ else
+ SetLastError(ERROR_INVALID_FUNCTION);
+ }
+ else
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+
+/*
+ * NT 3.50 stuff.
+ */
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 3.51 */
+DECL_KERNEL32(VOID) Fake_FreeLibraryAndExitThread(HMODULE hLibModule, DWORD dwExitCode)
+{
+ if (hLibModule)
+ FreeModule(hLibModule);
+ ExitThread(dwExitCode);
+}
+#endif /* VCC_FAKES_TARGET >= 140 */
+
+
+DECL_KERNEL32(BOOL) Fake_IsDebuggerPresent(VOID)
+{
+ /* Fallback: */
+ return FALSE;
+}
+
+
+DECL_KERNEL32(VOID) Fake_GetSystemTimeAsFileTime(LPFILETIME pTime)
+{
+ DWORD dwVersion = GetVersion();
+ if ( (dwVersion & 0xff) > 3
+ || ( (dwVersion & 0xff) == 3
+ && ((dwVersion >> 8) & 0xff) >= 50) )
+ {
+ PKUSER_SHARED_DATA pUsd = (PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA;
+
+ /* use interrupt time */
+ LARGE_INTEGER Time;
+ do
+ {
+ Time.HighPart = pUsd->SystemTime.High1Time;
+ Time.LowPart = pUsd->SystemTime.LowPart;
+ } while (pUsd->SystemTime.High2Time != Time.HighPart);
+
+ pTime->dwHighDateTime = Time.HighPart;
+ pTime->dwLowDateTime = Time.LowPart;
+ }
+ else
+ {
+ /* NT 3.1 didn't have a KUSER_SHARED_DATA nor a GetSystemTimeAsFileTime export. */
+ SYSTEMTIME SystemTime;
+ GetSystemTime(&SystemTime);
+ BOOL fRet = SystemTimeToFileTime(&SystemTime, pTime);
+ if (fRet)
+ { /* likely */ }
+ else
+ {
+ MY_ASSERT(false, "GetSystemTimeAsFileTime: #2");
+ pTime->dwHighDateTime = 0;
+ pTime->dwLowDateTime = 0;
+ }
+ }
+}
+
+
+/*
+ * NT 3.1 stuff.
+ */
+
+DECL_KERNEL32(BOOL) Fake_GetVersionExA(LPOSVERSIONINFOA pInfo)
+{
+ DWORD dwVersion = GetVersion();
+
+ /* Common fields: */
+ pInfo->dwMajorVersion = dwVersion & 0xff;
+ pInfo->dwMinorVersion = (dwVersion >> 8) & 0xff;
+ if (!(dwVersion & RT_BIT_32(31)))
+ pInfo->dwBuildNumber = dwVersion >> 16;
+ else
+ pInfo->dwBuildNumber = 511;
+ pInfo->dwPlatformId = VER_PLATFORM_WIN32_NT;
+/** @todo get CSD from registry. */
+ pInfo->szCSDVersion[0] = '\0';
+
+ /* OSVERSIONINFOEX fields: */
+ if (pInfo->dwOSVersionInfoSize > sizeof((*pInfo)))
+ {
+ LPOSVERSIONINFOEXA pInfoEx = (LPOSVERSIONINFOEXA)pInfo;
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXA, wServicePackMinor))
+ {
+ pInfoEx->wServicePackMajor = 0;
+ pInfoEx->wServicePackMinor = 0;
+ }
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXA, wSuiteMask))
+ pInfoEx->wSuiteMask = 0;
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXA, wProductType))
+ pInfoEx->wProductType = VER_NT_WORKSTATION;
+ if (pInfoEx->wReserved > RT_UOFFSETOF(OSVERSIONINFOEXA, wProductType))
+ pInfoEx->wReserved = 0;
+ }
+
+ return TRUE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_GetVersionExW(LPOSVERSIONINFOW pInfo)
+{
+ DWORD dwVersion = GetVersion();
+
+ /* Common fields: */
+ pInfo->dwMajorVersion = dwVersion & 0xff;
+ pInfo->dwMinorVersion = (dwVersion >> 8) & 0xff;
+ if (!(dwVersion & RT_BIT_32(31)))
+ pInfo->dwBuildNumber = dwVersion >> 16;
+ else
+ pInfo->dwBuildNumber = 511;
+ pInfo->dwPlatformId = VER_PLATFORM_WIN32_NT;
+/** @todo get CSD from registry. */
+ pInfo->szCSDVersion[0] = '\0';
+
+ /* OSVERSIONINFOEX fields: */
+ if (pInfo->dwOSVersionInfoSize > sizeof((*pInfo)))
+ {
+ LPOSVERSIONINFOEXW pInfoEx = (LPOSVERSIONINFOEXW)pInfo;
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXW, wServicePackMinor))
+ {
+ pInfoEx->wServicePackMajor = 0;
+ pInfoEx->wServicePackMinor = 0;
+ }
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXW, wSuiteMask))
+ pInfoEx->wSuiteMask = 0;
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXW, wProductType))
+ pInfoEx->wProductType = VER_NT_WORKSTATION;
+ if (pInfoEx->wReserved > RT_UOFFSETOF(OSVERSIONINFOEXW, wProductType))
+ pInfoEx->wReserved = 0;
+ }
+
+ return TRUE;
+}
+
+
+DECL_KERNEL32(LPWCH) Fake_GetEnvironmentStringsW(void)
+{
+ /*
+ * Environment is ANSI in NT 3.1. We should only be here for NT 3.1.
+ * For now, don't try do a perfect job converting it, just do it.
+ */
+ char *pszzEnv = (char *)RTNtCurrentPeb()->ProcessParameters->Environment;
+ size_t offEnv = 0;
+ while (pszzEnv[offEnv] != '\0')
+ {
+ size_t cchLen = strlen(&pszzEnv[offEnv]);
+ offEnv += cchLen + 1;
+ }
+ size_t const cchEnv = offEnv + 1;
+
+ PRTUTF16 pwszzEnv = (PRTUTF16)HeapAlloc(GetProcessHeap(), 0, cchEnv * sizeof(RTUTF16));
+ if (!pwszzEnv)
+ return NULL;
+ for (offEnv = 0; offEnv < cchEnv; offEnv++)
+ {
+ unsigned char ch = pwszzEnv[offEnv];
+ if (!(ch & 0x80))
+ pwszzEnv[offEnv] = ch;
+ else
+ pwszzEnv[offEnv] = '_';
+ }
+ return pwszzEnv;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_FreeEnvironmentStringsW(LPWCH pwszzEnv)
+{
+ if (pwszzEnv)
+ HeapFree(GetProcessHeap(), 0, pwszzEnv);
+ return TRUE;
+}
+
+
+DECL_KERNEL32(int) Fake_GetLocaleInfoA(LCID idLocale, LCTYPE enmType, LPSTR pData, int cchData)
+{
+ NOREF(idLocale); NOREF(enmType); NOREF(pData); NOREF(cchData);
+ //MY_ASSERT(false, "GetLocaleInfoA: idLocale=%#x enmType=%#x cchData=%#x", idLocale, enmType, cchData);
+ MY_ASSERT(false, "GetLocaleInfoA");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return 0;
+}
+
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 3.51 */
+DECL_KERNEL32(BOOL) Fake_EnumSystemLocalesW(LOCALE_ENUMPROCW pfnLocaleEnum, DWORD dwFlags)
+{
+ RT_NOREF(pfnLocaleEnum, dwFlags);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+#endif /* VCC_FAKES_TARGET >= 140 */
+
+
+DECL_KERNEL32(BOOL) Fake_EnumSystemLocalesA(LOCALE_ENUMPROCA pfnCallback, DWORD fFlags)
+{
+ NOREF(pfnCallback); NOREF(fFlags);
+ //MY_ASSERT(false, "EnumSystemLocalesA: pfnCallback=%p fFlags=%#x", pfnCallback, fFlags);
+ MY_ASSERT(false, "EnumSystemLocalesA");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_IsValidLocale(LCID idLocale, DWORD fFlags)
+{
+ NOREF(idLocale); NOREF(fFlags);
+ //MY_ASSERT(false, "IsValidLocale: idLocale fFlags=%#x", idLocale, fFlags);
+ MY_ASSERT(false, "IsValidLocale");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(DWORD_PTR) Fake_SetThreadAffinityMask(HANDLE hThread, DWORD_PTR fAffinityMask)
+{
+ SYSTEM_INFO SysInfo;
+ GetSystemInfo(&SysInfo);
+ //MY_ASSERT(false, "SetThreadAffinityMask: hThread=%p fAffinityMask=%p SysInfo.dwActiveProcessorMask=%p",
+ // hThread, fAffinityMask, SysInfo.dwActiveProcessorMask);
+ MY_ASSERT(false, "SetThreadAffinityMask");
+ if ( SysInfo.dwActiveProcessorMask == fAffinityMask
+ || fAffinityMask == ~(DWORD_PTR)0)
+ return fAffinityMask;
+
+ SetLastError(ERROR_NOT_SUPPORTED);
+ RT_NOREF(hThread);
+ return 0;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR pfProcessAffinityMask, PDWORD_PTR pfSystemAffinityMask)
+{
+ SYSTEM_INFO SysInfo;
+ GetSystemInfo(&SysInfo);
+ //MY_ASSERT(false, "GetProcessAffinityMask: SysInfo.dwActiveProcessorMask=%p", SysInfo.dwActiveProcessorMask);
+ MY_ASSERT(false, "GetProcessAffinityMask");
+ if (pfProcessAffinityMask)
+ *pfProcessAffinityMask = SysInfo.dwActiveProcessorMask;
+ if (pfSystemAffinityMask)
+ *pfSystemAffinityMask = SysInfo.dwActiveProcessorMask;
+ RT_NOREF(hProcess);
+ return TRUE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_GetHandleInformation(HANDLE hObject, DWORD *pfFlags)
+{
+ OBJECT_HANDLE_FLAG_INFORMATION Info = { 0, 0 };
+ DWORD cbRet = sizeof(Info);
+ NTSTATUS rcNt = NtQueryObject(hObject, ObjectHandleFlagInformation, &Info, sizeof(Info), &cbRet);
+ if (NT_SUCCESS(rcNt))
+ {
+ *pfFlags = (Info.Inherit ? HANDLE_FLAG_INHERIT : 0)
+ | (Info.ProtectFromClose ? HANDLE_FLAG_PROTECT_FROM_CLOSE : 0);
+ return TRUE;
+ }
+ *pfFlags = 0;
+ //MY_ASSERT(rcNt == STATUS_INVALID_HANDLE, "rcNt=%#x", rcNt);
+ MY_ASSERT(rcNt == STATUS_INVALID_HANDLE || rcNt == STATUS_INVALID_INFO_CLASS, "GetHandleInformation");
+ SetLastError(rcNt == STATUS_INVALID_HANDLE ? ERROR_INVALID_HANDLE : ERROR_INVALID_FUNCTION); /* see also process-win.cpp */
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_SetHandleInformation(HANDLE hObject, DWORD fMask, DWORD fFlags)
+{
+ NOREF(hObject); NOREF(fMask); NOREF(fFlags);
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return FALSE;
+}
+
+
+
+/**
+ * Resolves all the APIs ones and for all, updating the fake IAT entries.
+ */
+DECLASM(void) FakeResolve_kernel32(void)
+{
+ CURRENT_VERSION_VARIABLE();
+
+ HMODULE hmod = GetModuleHandleW(L"kernel32");
+ MY_ASSERT(hmod != NULL, "kernel32");
+
+#undef MAKE_IMPORT_ENTRY
+#define MAKE_IMPORT_ENTRY(a_uMajorVer, a_uMinorVer, a_Name, a_cb) RESOLVE_IMPORT(a_uMajorVer, a_uMinorVer, a_Name, a_cb)
+#ifdef VCC_FAKES_TARGET_VCC100
+# include "vcc-fakes-kernel32-100.h"
+#elif defined(VCC_FAKES_TARGET_VCC140)
+# include "vcc-fakes-kernel32-141.h"
+#elif defined(VCC_FAKES_TARGET_VCC141)
+# include "vcc-fakes-kernel32-141.h"
+#elif defined(VCC_FAKES_TARGET_VCC142)
+# include "vcc-fakes-kernel32-141.h"
+#else
+# error "Port me!"
+#endif
+
+ g_fInitialized = true;
+}
+
+
+/* Dummy to force dragging in this object in the link, so the linker
+ won't accidentally use the symbols from kernel32. */
+extern "C" int vcc100_kernel32_fakes_cpp(void)
+{
+ return 42;
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp
new file mode 100644
index 00000000..9aaf2d4d
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp
@@ -0,0 +1,93 @@
+/* $Id: vcc-fakes-msvcrt.cpp $ */
+/** @file
+ * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <stdlib.h>
+
+
+#ifndef RT_ARCH_X86
+# error "This code is X86 only"
+#endif
+
+
+
+/* This one is static in libcmt, fortunately no rocket science. */
+extern "C" void __cdecl my_initterm(PFNRT *papfnStart, PFNRT *papfnEnd)
+{
+ for (; (uintptr_t)papfnStart < (uintptr_t)papfnEnd; papfnStart++)
+ if (*papfnStart)
+ (*papfnStart)();
+}
+
+extern "C" PFNRT __cdecl my_dllonexit(PFNRT pfnToAdd, PFNRT **ppapfnStart, PFNRT **ppapfnEnd)
+{
+ /* This is _very_ crude, but it'll probably do for our purposes... */
+ size_t cItems = *ppapfnEnd - *ppapfnStart;
+ *ppapfnStart = (PFNRT *)realloc(*ppapfnStart, (cItems + 1) * sizeof(**ppapfnStart));
+ (*ppapfnStart)[cItems] = pfnToAdd;
+ *ppapfnEnd = &(*ppapfnStart)[cItems + 1];
+ return pfnToAdd;
+}
+
+extern "C" int _newmode;
+extern "C" int __cdecl __setargv(void);
+extern "C" int __cdecl _setargv(void);
+
+extern "C" int __cdecl my_getmainargs(int *pcArgs, char ***ppapszArgs, char ***ppapszEnv, int fDoWildcardExp, int *pfNewMode)
+{
+ _newmode = *pfNewMode;
+
+ Assert(!fDoWildcardExp);
+ int rc = _setargv();
+ if (rc >= 0)
+ {
+ *pcArgs = __argc;
+ *ppapszArgs = __argv;
+ *ppapszEnv = _environ;
+ }
+ return rc;
+}
+
+extern "C" void __cdecl my_setusermatherr(PFNRT pfnIgnore)
+{
+ RT_NOREF(pfnIgnore);
+ /* pure stub. */
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm
new file mode 100644
index 00000000..4eb482b2
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm
@@ -0,0 +1,57 @@
+; $Id: vcc-fakes-ntdll-A.asm $
+;; @file
+; IPRT - Wrappers for ntdll APIs misisng NT4.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "iprt/asmdefs.mac"
+
+%ifndef RT_ARCH_X86
+ %error "This is x86 only code.
+%endif
+
+
+%macro MAKE_IMPORT_ENTRY 2
+extern _ %+ %1 %+ @ %+ %2
+global __imp__ %+ %1 %+ @ %+ %2
+__imp__ %+ %1 %+ @ %+ %2:
+ dd _ %+ %1 %+ @ %+ %2
+
+%endmacro
+
+
+BEGINDATA
+GLOBALNAME vcc100_ntdll_fakes_asm
+
+MAKE_IMPORT_ENTRY RtlGetLastWin32Error, 0
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp
new file mode 100644
index 00000000..9aa5dd01
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp
@@ -0,0 +1,84 @@
+/* $Id: vcc-fakes-ntdll.cpp $ */
+/** @file
+ * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP - NTDLL.DLL.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+
+#ifndef RT_ARCH_X86
+# error "This code is X86 only"
+#endif
+
+#include <iprt/win/windows.h>
+
+
+
+/** Dynamically resolves an kernel32 API. */
+#define RESOLVE_ME(ApiNm) \
+ static bool volatile s_fInitialized = false; \
+ static decltype(ApiNm) *s_pfnApi = NULL; \
+ decltype(ApiNm) *pfnApi; \
+ if (s_fInitialized) \
+ pfnApi = s_pfnApi; \
+ else \
+ { \
+ pfnApi = (decltype(pfnApi))GetProcAddress(GetModuleHandleW(L"ntdll.dll"), #ApiNm); \
+ s_pfnApi = pfnApi; \
+ s_fInitialized = true; \
+ } do {} while (0) \
+
+
+extern "C"
+__declspec(dllexport)
+ULONG WINAPI RtlGetLastWin32Error(VOID)
+{
+ RESOLVE_ME(RtlGetLastWin32Error);
+ if (pfnApi)
+ return pfnApi();
+ return GetLastError();
+}
+
+
+/* Dummy to force dragging in this object in the link, so the linker
+ won't accidentally use the symbols from kernel32. */
+extern "C" int vcc100_ntdll_fakes_cpp(void)
+{
+ return 42;
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm
new file mode 100644
index 00000000..ddf7491b
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm
@@ -0,0 +1,59 @@
+; $Id: vcc-fakes-shell32-A.asm $
+;; @file
+; IPRT - Wrappers for shell32 APIs missing in NT 4 and earlier.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+
+%ifndef RT_ARCH_X86
+ %error "This is x86 only code.
+%endif
+
+
+%macro MAKE_IMPORT_ENTRY 2
+extern _ %+ %1 %+ @ %+ %2
+global __imp__ %+ %1 %+ @ %+ %2
+__imp__ %+ %1 %+ @ %+ %2:
+ dd _ %+ %1 %+ @ %+ %2
+
+%endmacro
+
+
+BEGINDATA
+GLOBALNAME vcc100_shell32_fakes_asm
+
+; NT 3.1
+MAKE_IMPORT_ENTRY CommandLineToArgvW, 8
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp
new file mode 100644
index 00000000..bd82befa
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp
@@ -0,0 +1,103 @@
+/* $Id: vcc-fakes-shell32.cpp $ */
+/** @file
+ * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RT_NO_STRICT /* Minimal deps so that it works on NT 3.51 too. */
+#include <iprt/types.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#ifndef RT_ARCH_X86
+# error "This code is X86 only"
+#endif
+
+#define CommandLineToArgvW Ignore_CommandLineToArgvW
+
+#include <iprt/nt/nt-and-windows.h>
+
+#undef CommandLineToArgvW
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Dynamically resolves an kernel32 API. */
+#define RESOLVE_ME(ApiNm) \
+ static decltype(ShellExecuteW) * volatile s_pfnInitialized = NULL; \
+ static decltype(ApiNm) *s_pfnApi = NULL; \
+ decltype(ApiNm) *pfnApi; \
+ if (s_pfnInitialized) \
+ pfnApi = s_pfnApi; \
+ else \
+ { \
+ pfnApi = (decltype(pfnApi))GetProcAddress(GetModuleHandleW(L"shell32"), #ApiNm); \
+ s_pfnApi = pfnApi; \
+ s_pfnInitialized = ShellExecuteW; /* ensures shell32 is loaded */ \
+ } do {} while (0)
+
+
+/** Declare a shell32 API.
+ * @note We are not exporting them as that causes duplicate symbol troubles in
+ * the OpenGL bits. */
+#define DECL_SHELL32(a_Type) extern "C" a_Type WINAPI
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+DECL_SHELL32(LPWSTR *) CommandLineToArgvW(LPCWSTR pwszCmdLine, int *pcArgs)
+{
+ RESOLVE_ME(CommandLineToArgvW);
+ if (pfnApi)
+ return pfnApi(pwszCmdLine, pcArgs);
+
+ *pcArgs = 0;
+ return NULL;
+}
+
+
+/* Dummy to force dragging in this object in the link, so the linker
+ won't accidentally use the symbols from shell32. */
+extern "C" int vcc100_shell32_fakes_cpp(void)
+{
+ return 42;
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm
new file mode 100644
index 00000000..2158917a
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm
@@ -0,0 +1,58 @@
+; $Id: vcc-fakes-ws2_32-A.asm $
+;; @file
+; IPRT - Wrappers for ws2_32 APIs misisng NT4.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "iprt/asmdefs.mac"
+
+%ifndef RT_ARCH_X86
+ %error "This is x86 only code.
+%endif
+
+
+%macro MAKE_IMPORT_ENTRY 2
+extern _ %+ %1 %+ @ %+ %2
+global __imp__ %+ %1 %+ @ %+ %2
+__imp__ %+ %1 %+ @ %+ %2:
+ dd _ %+ %1 %+ @ %+ %2
+
+%endmacro
+
+
+BEGINDATA
+GLOBALNAME vcc100_ws2_32_fakes_asm
+
+MAKE_IMPORT_ENTRY getaddrinfo, 16
+MAKE_IMPORT_ENTRY freeaddrinfo, 4
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp
new file mode 100644
index 00000000..b12f253e
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp
@@ -0,0 +1,113 @@
+/* $Id: vcc-fakes-ws2_32.cpp $ */
+/** @file
+ * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP - WS2_32.DLL.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/asm.h>
+#include <iprt/string.h>
+
+#ifndef RT_ARCH_X86
+# error "This code is X86 only"
+#endif
+
+#define getaddrinfo Ignore_getaddrinfo
+#define freeaddrinfo Ignore_freeaddrinfo
+
+#include <iprt/win/winsock2.h>
+#include <iprt/win/ws2tcpip.h>
+
+#undef getaddrinfo
+#undef freeaddrinfo
+
+
+/** Dynamically resolves an kernel32 API. */
+#define RESOLVE_ME(ApiNm) \
+ static bool volatile s_fInitialized = false; \
+ static decltype(ApiNm) *s_pfnApi = NULL; \
+ decltype(ApiNm) *pfnApi; \
+ if (s_fInitialized) \
+ pfnApi = s_pfnApi; \
+ else \
+ { \
+ pfnApi = (decltype(pfnApi))GetProcAddress(GetModuleHandleW(L"wc2_32.dll"), #ApiNm); \
+ s_pfnApi = pfnApi; \
+ s_fInitialized = true; \
+ } do {} while (0) \
+
+
+extern "C"
+__declspec(dllexport)
+int WINAPI getaddrinfo(const char *pszNodeName, const char *pszServiceName, const struct addrinfo *pHints,
+ struct addrinfo **ppResults)
+{
+ RESOLVE_ME(getaddrinfo);
+ if (pfnApi)
+ return pfnApi(pszNodeName, pszServiceName, pHints, ppResults);
+
+ /** @todo fallback */
+ WSASetLastError(WSAEAFNOSUPPORT);
+ return WSAEAFNOSUPPORT;
+}
+
+
+
+extern "C"
+__declspec(dllexport)
+void WINAPI freeaddrinfo(struct addrinfo *pResults)
+{
+ RESOLVE_ME(freeaddrinfo);
+ if (pfnApi)
+ pfnApi(pResults);
+ else
+ {
+ Assert(!pResults);
+ /** @todo fallback */
+ }
+}
+
+
+
+/* Dummy to force dragging in this object in the link, so the linker
+ won't accidentally use the symbols from kernel32. */
+extern "C" int vcc100_ws2_32_fakes_cpp(void)
+{
+ return 42;
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes.h b/src/VBox/Runtime/r3/win/vcc-fakes.h
new file mode 100644
index 00000000..6e72e2b8
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes.h
@@ -0,0 +1,155 @@
+/* $Id: vcc-fakes.h $ */
+/** @file
+ * IPRT - Common macros for the Visual C++ 2010+ CRT import fakes.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_win_vcc_fakes_h
+#define IPRT_INCLUDED_SRC_r3_win_vcc_fakes_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef RT_STRICT
+# include <stdio.h> /* _snprintf */
+#endif
+
+
+/** @def MY_ASSERT
+ * We use a special assertion macro here to avoid dragging in IPRT bits in
+ * places which cannot handle it (direct GA 3D bits or something like that).
+ *
+ * Turns out snprintf is off limits too. Needs Locale info and runs out of
+ * stack
+ */
+#ifdef RT_STRICT
+# if 1
+# define MY_ASSERT(a_Expr, g_szMsg) \
+ do { \
+ if ((a_Expr)) \
+ { /*likely*/ } \
+ else \
+ { \
+ OutputDebugStringA("Assertion failed on line " RT_XSTR(__LINE__) ": " RT_XSTR(a_Expr) "\n"); \
+ OutputDebugStringA("Assertion message: " g_szMsg "\n"); \
+ RT_BREAKPOINT(); \
+ } \
+ } while (0)
+# define MY_ASSERT_STMT_RETURN(a_Expr, a_Stmt, a_rc) \
+ do { \
+ if (a_Expr) \
+ { /* likely */ } \
+ else \
+ { \
+ OutputDebugStringA("Assertion failed on line " RT_XSTR(__LINE__) ": " RT_XSTR(a_Expr) "\n"); \
+ RT_BREAKPOINT(); \
+ a_Stmt; \
+ return (a_rc); \
+ } \
+ } while (0)
+# else
+# define MY_ASSERT(a_Expr, ...) \
+ do { \
+ if ((a_Expr)) \
+ { /*likely*/ } \
+ else \
+ { \
+ char szTmp[256]; \
+ _snprintf(szTmp, sizeof(szTmp), "Assertion failed on line %u in '%s': %s", __LINE__, __PRETTY_FUNCTION__, #a_Expr); \
+ OutputDebugStringA(szTmp); \
+ _snprintf(szTmp, sizeof(szTmp), __VA_ARGS__); \
+ OutputDebugStringA(szTmp); \
+ RT_BREAKPOINT(); \
+ } \
+ } while (0)
+# endif
+#else
+# define MY_ASSERT(a_Expr, ...) do { } while (0)
+# define MY_ASSERT_STMT_RETURN(a_Expr, a_Stmt, a_rc) \
+ do { if (a_Expr) { /* likely */ } else { a_Stmt; return (a_rc); }} while (0)
+#endif
+
+
+/** Dynamically resolves an NTDLL API we need. */
+#define RESOLVE_NTDLL_API(ApiNm) \
+ static bool volatile s_fInitialized##ApiNm = false; \
+ static decltype(ApiNm) *s_pfn##ApiNm = NULL; \
+ decltype(ApiNm) *pfn##ApiNm; \
+ if (s_fInitialized##ApiNm) \
+ pfn##ApiNm = s_pfn##ApiNm; \
+ else \
+ { \
+ pfn##ApiNm = (decltype(pfn##ApiNm))GetProcAddress(GetModuleHandleW(L"ntdll"), #ApiNm); \
+ s_pfn##ApiNm = pfn##ApiNm; \
+ s_fInitialized##ApiNm = true; \
+ } do {} while (0)
+
+
+/** Declare a kernel32 API.
+ * @note We are not exporting them as that causes duplicate symbol troubles in
+ * the OpenGL bits. */
+#define DECL_KERNEL32(a_Type) extern "C" a_Type WINAPI
+
+
+/** Ignore comments. */
+#define COMMENT(a_Text)
+
+/** Used for MAKE_IMPORT_ENTRY when declaring external g_pfnXxxx variables. */
+#define DECLARE_FUNCTION_POINTER(a_Name, a_cb) extern "C" decltype(a_Name) *RT_CONCAT(g_pfn, a_Name);
+
+/** Used in the InitFakes method to decl are uCurVersion used by assertion. */
+#ifdef RT_STRICT
+# define CURRENT_VERSION_VARIABLE() \
+ unsigned uCurVersion = GetVersion(); \
+ uCurVersion = ((uCurVersion & 0xff) << 8) | ((uCurVersion >> 8) & 0xff)
+#else
+# define CURRENT_VERSION_VARIABLE() (void)0
+#endif
+
+
+/** Used for MAKE_IMPORT_ENTRY when resolving an import. */
+#define RESOLVE_IMPORT(a_uMajorVer, a_uMinorVer, a_Name, a_cb) \
+ do { \
+ FARPROC pfnApi = GetProcAddress(hmod, #a_Name); \
+ if (pfnApi) \
+ RT_CONCAT(g_pfn, a_Name) = (decltype(a_Name) *)pfnApi; \
+ else \
+ { \
+ MY_ASSERT(uCurVersion < (((a_uMajorVer) << 8) | (a_uMinorVer)), #a_Name); \
+ RT_CONCAT(g_pfn, a_Name) = RT_CONCAT(Fake_,a_Name); \
+ } \
+ } while (0);
+
+
+#endif /* !IPRT_INCLUDED_SRC_r3_win_vcc_fakes_h */
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes.mac b/src/VBox/Runtime/r3/win/vcc-fakes.mac
new file mode 100644
index 00000000..37c51543
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes.mac
@@ -0,0 +1,76 @@
+; $Id: vcc-fakes.mac $
+;; @file
+; IPRT - Common macros for the Visual C++ 2010+ CRT import fakes.
+;
+
+;
+; Copyright (C) 2006-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+
+%ifndef RT_ARCH_X86
+ %error "This is x86 only code.
+%endif
+
+
+;;
+; Fakes an API import table entry.
+;
+; @param 1 The function name
+; @param 2 Number of bytes of parameters for x86.
+%macro MAKE_IMPORT_ENTRY_INTERNAL 2
+
+BEGINDATA
+extern _FakeResolve_ %+ FAKE_MODULE_NAME
+
+extern _Fake_ %+ %1 %+ @ %+ %2
+global __imp__ %+ %1 %+ @ %+ %2 ; The import address table (IAT) entry name.
+__imp__ %+ %1 %+ @ %+ %2:
+global _g_pfn %+ %1 ; C accessible label.
+_g_pfn %+ %1:
+ dd FakeLazyInit_ %+ %1
+
+BEGINCODE
+FakeLazyInit_ %+ %1:
+ pusha
+ call _FakeResolve_ %+ FAKE_MODULE_NAME
+ popa
+global _ %+ %1 %+ @ %+ %2 ; The imported function stub.
+_ %+ %1 %+ @ %+ %2:
+ jmp [_g_pfn %+ %1]
+
+
+%endmacro
+
+%define MAKE_IMPORT_ENTRY(a_uMajorVer, a_uMinorVer, a_Name, a_cbParams) MAKE_IMPORT_ENTRY_INTERNAL a_Name, a_cbParams
+%define COMMENT(a_Text)
+
diff --git a/src/VBox/Runtime/r3/xml.cpp b/src/VBox/Runtime/r3/xml.cpp
new file mode 100644
index 00000000..a6661760
--- /dev/null
+++ b/src/VBox/Runtime/r3/xml.cpp
@@ -0,0 +1,2357 @@
+/* $Id: xml.cpp $ */
+/** @file
+ * IPRT - XML Manipulation API.
+ *
+ * @note Not available in no-CRT mode because it relies too heavily on
+ * exceptions, as well as using std::list and std::map.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cpp/xml.h>
+
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/cpp/lock.h>
+
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/globals.h>
+#include <libxml/xmlIO.h>
+#include <libxml/xmlsave.h>
+#include <libxml/uri.h>
+
+#include <libxml/xmlschemas.h>
+
+#include <map>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Global module initialization structure. This is to wrap non-reentrant bits
+ * of libxml, among other things.
+ *
+ * The constructor and destructor of this structure are used to perform global
+ * module initialization and cleanup. There must be only one global variable of
+ * this structure.
+ */
+static class Global
+{
+public:
+
+ Global()
+ {
+ /* Check the parser version. The docs say it will kill the app if
+ * there is a serious version mismatch, but I couldn't find it in the
+ * source code (it only prints the error/warning message to the console) so
+ * let's leave it as is for informational purposes. */
+ LIBXML_TEST_VERSION
+
+ /* Init libxml */
+ xmlInitParser();
+
+ /* Save the default entity resolver before someone has replaced it */
+ sxml.defaultEntityLoader = xmlGetExternalEntityLoader();
+ }
+
+ ~Global()
+ {
+ /* Shutdown libxml */
+ xmlCleanupParser();
+ }
+
+ struct
+ {
+ xmlExternalEntityLoader defaultEntityLoader;
+
+ /** Used to provide some thread safety missing in libxml2 (see e.g.
+ * XmlTreeBackend::read()) */
+ RTCLockMtx lock;
+ }
+ sxml; /* XXX naming this xml will break with gcc-3.3 */
+} gGlobal;
+
+
+
+namespace xml
+{
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Exceptions
+//
+////////////////////////////////////////////////////////////////////////////////
+
+LogicError::LogicError(RT_SRC_POS_DECL)
+ : RTCError(NULL)
+{
+ char *msg = NULL;
+ RTStrAPrintf(&msg, "In '%s', '%s' at #%d",
+ pszFunction, pszFile, iLine);
+ setWhat(msg);
+ RTStrFree(msg);
+}
+
+XmlError::XmlError(xmlErrorPtr aErr)
+{
+ if (!aErr)
+ throw EInvalidArg(RT_SRC_POS);
+
+ char *msg = Format(aErr);
+ setWhat(msg);
+ RTStrFree(msg);
+}
+
+/**
+ * Composes a single message for the given error. The caller must free the
+ * returned string using RTStrFree() when no more necessary.
+ */
+/* static */ char *XmlError::Format(xmlErrorPtr aErr)
+{
+ const char *msg = aErr->message ? aErr->message : "<none>";
+ size_t msgLen = strlen(msg);
+ /* strip spaces, trailing EOLs and dot-like char */
+ while (msgLen && strchr(" \n.?!", msg [msgLen - 1]))
+ --msgLen;
+
+ char *finalMsg = NULL;
+ RTStrAPrintf(&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
+ msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
+
+ return finalMsg;
+}
+
+EIPRTFailure::EIPRTFailure(int aRC, const char *pszContextFmt, ...)
+ : RuntimeError(NULL)
+ , mRC(aRC)
+{
+ va_list va;
+ va_start(va, pszContextFmt);
+ m_strMsg.printfVNoThrow(pszContextFmt, va);
+ va_end(va);
+ m_strMsg.appendPrintfNoThrow(" %Rrc (%Rrs)", aRC, aRC);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// File Class
+//
+//////////////////////////////////////////////////////////////////////////////
+
+struct File::Data
+{
+ Data(RTFILE a_hHandle, const char *a_pszFilename, bool a_fFlushOnClose)
+ : strFileName(a_pszFilename)
+ , handle(a_hHandle)
+ , opened(a_hHandle != NIL_RTFILE)
+ , flushOnClose(a_fFlushOnClose)
+ { }
+
+ ~Data()
+ {
+ if (flushOnClose)
+ {
+ RTFileFlush(handle);
+ if (!strFileName.isEmpty())
+ RTDirFlushParent(strFileName.c_str());
+ }
+
+ if (opened)
+ {
+ RTFileClose(handle);
+ handle = NIL_RTFILE;
+ opened = false;
+ }
+ }
+
+ RTCString strFileName;
+ RTFILE handle;
+ bool opened : 1;
+ bool flushOnClose : 1;
+};
+
+File::File(Mode aMode, const char *aFileName, bool aFlushIt /* = false */)
+ : m(NULL)
+{
+ /* Try open the file first, as the destructor will not be invoked if we throw anything from here. For details see:
+ https://stackoverflow.com/questions/9971782/destructor-not-invoked-when-an-exception-is-thrown-in-the-constructor */
+ uint32_t flags = 0;
+ const char *pcszMode = "???";
+ switch (aMode)
+ {
+ /** @todo change to RTFILE_O_DENY_WRITE where appropriate. */
+ case Mode_Read:
+ flags = RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
+ pcszMode = "reading";
+ break;
+ case Mode_WriteCreate: // fail if file exists
+ flags = RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE;
+ pcszMode = "writing";
+ break;
+ case Mode_Overwrite: // overwrite if file exists
+ flags = RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE;
+ pcszMode = "overwriting";
+ break;
+ case Mode_ReadWrite:
+ flags = RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE;
+ pcszMode = "reading/writing";
+ break;
+ }
+ RTFILE hFile = NIL_RTFILE;
+ int vrc = RTFileOpen(&hFile, aFileName, flags);
+ if (RT_FAILURE(vrc))
+ throw EIPRTFailure(vrc, "Runtime error opening '%s' for %s", aFileName, pcszMode);
+
+ /* Now we can create the data and stuff: */
+ try
+ {
+ m = new Data(hFile, aFileName, aFlushIt && (flags & RTFILE_O_ACCESS_MASK) != RTFILE_O_READ);
+ }
+ catch (std::bad_alloc &)
+ {
+ RTFileClose(hFile);
+ throw;
+ }
+}
+
+File::File(RTFILE aHandle, const char *aFileName /* = NULL */, bool aFlushIt /* = false */)
+ : m(NULL)
+{
+ if (aHandle == NIL_RTFILE)
+ throw EInvalidArg(RT_SRC_POS);
+
+ m = new Data(aHandle, aFileName, aFlushIt);
+
+ setPos(0);
+}
+
+File::~File()
+{
+ if (m)
+ {
+ delete m;
+ m = NULL;
+ }
+}
+
+const char *File::uri() const
+{
+ return m->strFileName.c_str();
+}
+
+uint64_t File::pos() const
+{
+ uint64_t p = 0;
+ int vrc = RTFileSeek(m->handle, 0, RTFILE_SEEK_CURRENT, &p);
+ if (RT_SUCCESS(vrc))
+ return p;
+
+ throw EIPRTFailure(vrc, "Runtime error seeking in file '%s'", m->strFileName.c_str());
+}
+
+void File::setPos(uint64_t aPos)
+{
+ uint64_t p = 0;
+ unsigned method = RTFILE_SEEK_BEGIN;
+ int vrc = VINF_SUCCESS;
+
+ /* check if we overflow int64_t and move to INT64_MAX first */
+ if ((int64_t)aPos < 0)
+ {
+ vrc = RTFileSeek(m->handle, INT64_MAX, method, &p);
+ aPos -= (uint64_t)INT64_MAX;
+ method = RTFILE_SEEK_CURRENT;
+ }
+ /* seek the rest */
+ if (RT_SUCCESS(vrc))
+ vrc = RTFileSeek(m->handle, (int64_t) aPos, method, &p);
+ if (RT_SUCCESS(vrc))
+ return;
+
+ throw EIPRTFailure(vrc, "Runtime error seeking in file '%s'", m->strFileName.c_str());
+}
+
+int File::read(char *aBuf, int aLen)
+{
+ size_t len = aLen;
+ int vrc = RTFileRead(m->handle, aBuf, len, &len);
+ if (RT_SUCCESS(vrc))
+ return (int)len;
+
+ throw EIPRTFailure(vrc, "Runtime error reading from file '%s'", m->strFileName.c_str());
+}
+
+int File::write(const char *aBuf, int aLen)
+{
+ size_t len = aLen;
+ int vrc = RTFileWrite(m->handle, aBuf, len, &len);
+ if (RT_SUCCESS(vrc))
+ return (int)len;
+
+ throw EIPRTFailure(vrc, "Runtime error writing to file '%s'", m->strFileName.c_str());
+}
+
+void File::truncate()
+{
+ int vrc = RTFileSetSize(m->handle, pos());
+ if (RT_SUCCESS(vrc))
+ return;
+
+ throw EIPRTFailure(vrc, "Runtime error truncating file '%s'", m->strFileName.c_str());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// MemoryBuf Class
+//
+//////////////////////////////////////////////////////////////////////////////
+
+struct MemoryBuf::Data
+{
+ Data()
+ : buf(NULL), len(0), uri(NULL), pos(0) {}
+
+ const char *buf;
+ size_t len;
+ char *uri;
+
+ size_t pos;
+};
+
+MemoryBuf::MemoryBuf(const char *aBuf, size_t aLen, const char *aURI /* = NULL */)
+ : m(new Data())
+{
+ if (aBuf == NULL)
+ throw EInvalidArg(RT_SRC_POS);
+
+ m->buf = aBuf;
+ m->len = aLen;
+ m->uri = RTStrDup(aURI);
+}
+
+MemoryBuf::~MemoryBuf()
+{
+ RTStrFree(m->uri);
+}
+
+const char *MemoryBuf::uri() const
+{
+ return m->uri;
+}
+
+uint64_t MemoryBuf::pos() const
+{
+ return m->pos;
+}
+
+void MemoryBuf::setPos(uint64_t aPos)
+{
+ size_t off = (size_t)aPos;
+ if ((uint64_t) off != aPos)
+ throw EInvalidArg();
+
+ if (off > m->len)
+ throw EInvalidArg();
+
+ m->pos = off;
+}
+
+int MemoryBuf::read(char *aBuf, int aLen)
+{
+ if (m->pos >= m->len)
+ return 0 /* nothing to read */;
+
+ size_t len = m->pos + aLen < m->len ? aLen : m->len - m->pos;
+ memcpy(aBuf, m->buf + m->pos, len);
+ m->pos += len;
+
+ return (int)len;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// GlobalLock class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+struct GlobalLock::Data
+{
+ PFNEXTERNALENTITYLOADER pfnOldLoader;
+ RTCLock lock;
+
+ Data()
+ : pfnOldLoader(NULL)
+ , lock(gGlobal.sxml.lock)
+ {
+ }
+};
+
+GlobalLock::GlobalLock()
+ : m(new Data())
+{
+}
+
+GlobalLock::~GlobalLock()
+{
+ if (m->pfnOldLoader)
+ xmlSetExternalEntityLoader(m->pfnOldLoader);
+ delete m;
+ m = NULL;
+}
+
+void GlobalLock::setExternalEntityLoader(PFNEXTERNALENTITYLOADER pfnLoader)
+{
+ m->pfnOldLoader = (PFNEXTERNALENTITYLOADER)xmlGetExternalEntityLoader();
+ xmlSetExternalEntityLoader(pfnLoader);
+}
+
+// static
+xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI,
+ const char *aID,
+ xmlParserCtxt *aCtxt)
+{
+ return gGlobal.sxml.defaultEntityLoader(aURI, aID, aCtxt);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Node class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+Node::Node(EnumType type,
+ Node *pParent,
+ PRTLISTANCHOR pListAnchor,
+ xmlNode *pLibNode,
+ xmlAttr *pLibAttr)
+ : m_Type(type)
+ , m_pParent(pParent)
+ , m_pLibNode(pLibNode)
+ , m_pLibAttr(pLibAttr)
+ , m_pcszNamespacePrefix(NULL)
+ , m_pcszNamespaceHref(NULL)
+ , m_pcszName(NULL)
+ , m_pParentListAnchor(pListAnchor)
+{
+ RTListInit(&m_listEntry);
+}
+
+Node::~Node()
+{
+}
+
+/**
+ * Returns the name of the node, which is either the element name or
+ * the attribute name. For other node types it probably returns NULL.
+ * @return
+ */
+const char *Node::getName() const
+{
+ return m_pcszName;
+}
+
+/**
+ * Returns the name of the node, which is either the element name or
+ * the attribute name. For other node types it probably returns NULL.
+ * @return
+ */
+const char *Node::getPrefix() const
+{
+ return m_pcszNamespacePrefix;
+}
+
+/**
+ * Returns the XML namespace URI, which is the attribute name. For other node types it probably
+ * returns NULL.
+ * @return
+ */
+const char *Node::getNamespaceURI() const
+{
+ return m_pcszNamespaceHref;
+}
+
+/**
+ * Variant of nameEquals that checks the namespace as well.
+ * @param pcszNamespace
+ * @param pcsz
+ * @return
+ */
+bool Node::nameEqualsNS(const char *pcszNamespace, const char *pcsz) const
+{
+ if (m_pcszName == pcsz)
+ return true;
+ if (m_pcszName == NULL)
+ return false;
+ if (pcsz == NULL)
+ return false;
+ if (strcmp(m_pcszName, pcsz))
+ return false;
+
+ // name matches: then check namespaces as well
+ if (!pcszNamespace)
+ return true;
+ // caller wants namespace:
+ if (!m_pcszNamespacePrefix)
+ // but node has no namespace:
+ return false;
+ return !strcmp(m_pcszNamespacePrefix, pcszNamespace);
+}
+
+/**
+ * Variant of nameEquals that checks the namespace as well.
+ *
+ * @returns true if equal, false if not.
+ * @param pcsz The element name.
+ * @param cchMax The maximum number of character from @a pcsz to
+ * match.
+ * @param pcszNamespace The name space prefix or NULL (default).
+ */
+bool Node::nameEqualsN(const char *pcsz, size_t cchMax, const char *pcszNamespace /* = NULL*/) const
+{
+ /* Match the name. */
+ if (!m_pcszName)
+ return false;
+ if (!pcsz || cchMax == 0)
+ return false;
+ if (strncmp(m_pcszName, pcsz, cchMax))
+ return false;
+ if (strlen(m_pcszName) > cchMax)
+ return false;
+
+ /* Match name space. */
+ if (!pcszNamespace)
+ return true; /* NULL, anything goes. */
+ if (!m_pcszNamespacePrefix)
+ return false; /* Element has no namespace. */
+ return !strcmp(m_pcszNamespacePrefix, pcszNamespace);
+}
+
+/**
+ * Returns the value of a node. If this node is an attribute, returns
+ * the attribute value; if this node is an element, then this returns
+ * the element text content.
+ * @return
+ */
+const char *Node::getValue() const
+{
+ if ( m_pLibAttr
+ && m_pLibAttr->children
+ )
+ // libxml hides attribute values in another node created as a
+ // single child of the attribute node, and it's in the content field
+ return (const char *)m_pLibAttr->children->content;
+
+ if ( m_pLibNode
+ && m_pLibNode->children)
+ return (const char *)m_pLibNode->children->content;
+
+ return NULL;
+}
+
+/**
+ * Returns the value of a node. If this node is an attribute, returns
+ * the attribute value; if this node is an element, then this returns
+ * the element text content.
+ * @return
+ * @param cchValueLimit If the length of the returned value exceeds this
+ * limit a EIPRTFailure exception will be thrown.
+ */
+const char *Node::getValueN(size_t cchValueLimit) const
+{
+ if ( m_pLibAttr
+ && m_pLibAttr->children
+ )
+ {
+ // libxml hides attribute values in another node created as a
+ // single child of the attribute node, and it's in the content field
+ AssertStmt(strlen((const char *)m_pLibAttr->children->content) <= cchValueLimit, throw EIPRTFailure(VERR_BUFFER_OVERFLOW, "Attribute '%s' exceeds limit of %zu bytes", m_pcszName, cchValueLimit));
+ return (const char *)m_pLibAttr->children->content;
+ }
+
+ if ( m_pLibNode
+ && m_pLibNode->children)
+ {
+ AssertStmt(strlen((const char *)m_pLibNode->children->content) <= cchValueLimit, throw EIPRTFailure(VERR_BUFFER_OVERFLOW, "Element '%s' exceeds limit of %zu bytes", m_pcszName, cchValueLimit));
+ return (const char *)m_pLibNode->children->content;
+ }
+
+ return NULL;
+}
+
+/**
+ * Copies the value of a node into the given integer variable.
+ * Returns TRUE only if a value was found and was actually an
+ * integer of the given type.
+ * @return
+ */
+bool Node::copyValue(int32_t &i) const
+{
+ const char *pcsz;
+ if ( ((pcsz = getValue()))
+ && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
+ )
+ return true;
+
+ return false;
+}
+
+/**
+ * Copies the value of a node into the given integer variable.
+ * Returns TRUE only if a value was found and was actually an
+ * integer of the given type.
+ * @return
+ */
+bool Node::copyValue(uint32_t &i) const
+{
+ const char *pcsz;
+ if ( ((pcsz = getValue()))
+ && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
+ )
+ return true;
+
+ return false;
+}
+
+/**
+ * Copies the value of a node into the given integer variable.
+ * Returns TRUE only if a value was found and was actually an
+ * integer of the given type.
+ * @return
+ */
+bool Node::copyValue(int64_t &i) const
+{
+ const char *pcsz;
+ if ( ((pcsz = getValue()))
+ && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
+ )
+ return true;
+
+ return false;
+}
+
+/**
+ * Copies the value of a node into the given integer variable.
+ * Returns TRUE only if a value was found and was actually an
+ * integer of the given type.
+ * @return
+ */
+bool Node::copyValue(uint64_t &i) const
+{
+ const char *pcsz;
+ if ( ((pcsz = getValue()))
+ && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
+ )
+ return true;
+
+ return false;
+}
+
+/**
+ * Returns the line number of the current node in the source XML file.
+ * Useful for error messages.
+ * @return
+ */
+int Node::getLineNumber() const
+{
+ if (m_pLibAttr)
+ return m_pParent->m_pLibNode->line;
+
+ return m_pLibNode->line;
+}
+
+/**
+ * Private element constructor.
+ *
+ * @param pElmRoot Pointer to the root element.
+ * @param pParent Pointer to the parent element (always an ElementNode,
+ * despite the type). NULL for the root node.
+ * @param pListAnchor Pointer to the m_children member of the parent. NULL
+ * for the root node.
+ * @param pLibNode Pointer to the libxml2 node structure.
+ */
+ElementNode::ElementNode(const ElementNode *pElmRoot,
+ Node *pParent,
+ PRTLISTANCHOR pListAnchor,
+ xmlNode *pLibNode)
+ : Node(IsElement,
+ pParent,
+ pListAnchor,
+ pLibNode,
+ NULL)
+{
+ m_pElmRoot = pElmRoot ? pElmRoot : this; // If NULL is passed, then this is the root element.
+ m_pcszName = (const char *)pLibNode->name;
+
+ if (pLibNode->ns)
+ {
+ m_pcszNamespacePrefix = (const char *)m_pLibNode->ns->prefix;
+ m_pcszNamespaceHref = (const char *)m_pLibNode->ns->href;
+ }
+
+ RTListInit(&m_children);
+ RTListInit(&m_attributes);
+}
+
+ElementNode::~ElementNode()
+{
+ Node *pCur, *pNext;
+ RTListForEachSafeCpp(&m_children, pCur, pNext, Node, m_listEntry)
+ {
+ delete pCur;
+ }
+ RTListInit(&m_children);
+
+ RTListForEachSafeCpp(&m_attributes, pCur, pNext, Node, m_listEntry)
+ {
+ delete pCur;
+ }
+ RTListInit(&m_attributes);
+}
+
+
+ElementNode const *ElementNode::getNextTreeElement(ElementNode const *pElmRoot /*= NULL */) const
+{
+ /*
+ * Consider children first.
+ */
+ ElementNode const *pChild = getFirstChildElement();
+ if (pChild)
+ return pChild;
+
+ /*
+ * Then siblings, aunts and uncles.
+ */
+ ElementNode const *pCur = this;
+ do
+ {
+ ElementNode const *pSibling = pCur->getNextSibilingElement();
+ if (pSibling != NULL)
+ return pSibling;
+
+ pCur = static_cast<const xml::ElementNode *>(pCur->m_pParent);
+ Assert(pCur || pCur == pElmRoot);
+ } while (pCur != pElmRoot);
+
+ return NULL;
+}
+
+
+/**
+ * Private implementation.
+ *
+ * @param pElmRoot The root element.
+ */
+/*static*/ void ElementNode::buildChildren(ElementNode *pElmRoot) // protected
+{
+ for (ElementNode *pCur = pElmRoot; pCur; pCur = pCur->getNextTreeElement(pElmRoot))
+ {
+ /*
+ * Go thru this element's attributes creating AttributeNodes for them.
+ */
+ for (xmlAttr *pLibAttr = pCur->m_pLibNode->properties; pLibAttr; pLibAttr = pLibAttr->next)
+ {
+ AttributeNode *pNew = new AttributeNode(pElmRoot, pCur, &pCur->m_attributes, pLibAttr);
+ RTListAppend(&pCur->m_attributes, &pNew->m_listEntry);
+ }
+
+ /*
+ * Go thru this element's child elements (element and text nodes).
+ */
+ for (xmlNodePtr pLibNode = pCur->m_pLibNode->children; pLibNode; pLibNode = pLibNode->next)
+ {
+ Node *pNew;
+ if (pLibNode->type == XML_ELEMENT_NODE)
+ pNew = new ElementNode(pElmRoot, pCur, &pCur->m_children, pLibNode);
+ else if (pLibNode->type == XML_TEXT_NODE)
+ pNew = new ContentNode(pCur, &pCur->m_children, pLibNode);
+ else
+ continue;
+ RTListAppend(&pCur->m_children, &pNew->m_listEntry);
+ }
+ }
+}
+
+
+/**
+ * Builds a list of direct child elements of the current element that
+ * match the given string; if pcszMatch is NULL, all direct child
+ * elements are returned.
+ * @param children out: list of nodes to which children will be appended.
+ * @param pcszMatch in: match string, or NULL to return all children.
+ * @return Number of items appended to the list (0 if none).
+ */
+int ElementNode::getChildElements(ElementNodesList &children,
+ const char *pcszMatch /*= NULL*/)
+ const
+{
+ int i = 0;
+ Node *p;
+ RTListForEachCpp(&m_children, p, Node, m_listEntry)
+ {
+ // export this child node if ...
+ if (p->isElement())
+ if ( !pcszMatch // ... the caller wants all nodes or ...
+ || !strcmp(pcszMatch, p->getName()) // ... the element name matches.
+ )
+ {
+ children.push_back(static_cast<ElementNode *>(p));
+ ++i;
+ }
+ }
+ return i;
+}
+
+/**
+ * Returns the first child element whose name matches pcszMatch.
+ *
+ * @param pcszNamespace Namespace prefix (e.g. "vbox") or NULL to match any namespace.
+ * @param pcszMatch Element name to match.
+ * @return
+ */
+const ElementNode *ElementNode::findChildElementNS(const char *pcszNamespace, const char *pcszMatch) const
+{
+ Node *p;
+ RTListForEachCpp(&m_children, p, Node, m_listEntry)
+ {
+ if (p->isElement())
+ {
+ ElementNode *pelm = static_cast<ElementNode*>(p);
+ if (pelm->nameEqualsNS(pcszNamespace, pcszMatch))
+ return pelm;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Returns the first child element whose "id" attribute matches pcszId.
+ * @param pcszId identifier to look for.
+ * @return child element or NULL if not found.
+ */
+const ElementNode *ElementNode::findChildElementFromId(const char *pcszId) const
+{
+ const Node *p;
+ RTListForEachCpp(&m_children, p, Node, m_listEntry)
+ {
+ if (p->isElement())
+ {
+ const ElementNode *pElm = static_cast<const ElementNode *>(p);
+ const AttributeNode *pAttr = pElm->findAttribute("id");
+ if (pAttr && !strcmp(pAttr->getValue(), pcszId))
+ return pElm;
+ }
+ }
+ return NULL;
+}
+
+
+const ElementNode *ElementNode::findChildElementP(const char *pcszPath, const char *pcszNamespace /*= NULL*/) const
+{
+ size_t cchThis = strchr(pcszPath, '/') - pcszPath;
+ if (cchThis == (size_t)((const char *)0 - pcszPath))
+ return findChildElementNS(pcszNamespace, pcszPath);
+
+ /** @todo Can be done without recursion as we have both sibling lists and parent
+ * pointers in this variant. */
+ const Node *p;
+ RTListForEachCpp(&m_children, p, Node, m_listEntry)
+ {
+ if (p->isElement())
+ {
+ const ElementNode *pElm = static_cast<const ElementNode *>(p);
+ if (pElm->nameEqualsN(pcszPath, cchThis, pcszNamespace))
+ {
+ pElm = findChildElementP(pcszPath + cchThis, pcszNamespace);
+ if (pElm)
+ return pElm;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+const ElementNode *ElementNode::getFirstChildElement() const
+{
+ const Node *p;
+ RTListForEachCpp(&m_children, p, Node, m_listEntry)
+ {
+ if (p->isElement())
+ return static_cast<const ElementNode *>(p);
+ }
+ return NULL;
+}
+
+const ElementNode *ElementNode::getLastChildElement() const
+{
+ const Node *p;
+ RTListForEachReverseCpp(&m_children, p, Node, m_listEntry)
+ {
+ if (p->isElement())
+ return static_cast<const ElementNode *>(p);
+ }
+ return NULL;
+}
+
+const ElementNode *ElementNode::getPrevSibilingElement() const
+{
+ if (!m_pParent)
+ return NULL;
+ const Node *pSibling = this;
+ for (;;)
+ {
+ pSibling = RTListGetPrevCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
+ if (!pSibling)
+ return NULL;
+ if (pSibling->isElement())
+ return static_cast<const ElementNode *>(pSibling);
+ }
+}
+
+const ElementNode *ElementNode::getNextSibilingElement() const
+{
+ if (!m_pParent)
+ return NULL;
+ const Node *pSibling = this;
+ for (;;)
+ {
+ pSibling = RTListGetNextCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
+ if (!pSibling)
+ return NULL;
+ if (pSibling->isElement())
+ return static_cast<const ElementNode *>(pSibling);
+ }
+}
+
+const ElementNode *ElementNode::findPrevSibilingElement(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const
+{
+ if (!m_pParent)
+ return NULL;
+ const Node *pSibling = this;
+ for (;;)
+ {
+ pSibling = RTListGetPrevCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
+ if (!pSibling)
+ return NULL;
+ if (pSibling->isElement())
+ {
+ const ElementNode *pElem = static_cast<const ElementNode *>(pSibling);
+ if (pElem->nameEqualsNS(pcszNamespace, pcszMatch))
+ return pElem;
+ }
+ }
+}
+
+const ElementNode *ElementNode::findNextSibilingElement(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const
+{
+ if (!m_pParent)
+ return NULL;
+ const Node *pSibling = this;
+ for (;;)
+ {
+ pSibling = RTListGetNextCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry);
+ if (!pSibling)
+ return NULL;
+ if (pSibling->isElement())
+ {
+ const ElementNode *pElem = static_cast<const ElementNode *>(pSibling);
+ if (pElem->nameEqualsNS(pcszNamespace, pcszMatch))
+ return pElem;
+ }
+ }
+}
+
+
+/**
+ * Looks up the given attribute node in this element's attribute map.
+ *
+ * @param pcszMatch The name of the attribute to find.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ */
+const AttributeNode *ElementNode::findAttribute(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const
+{
+ AttributeNode *p;
+ RTListForEachCpp(&m_attributes, p, AttributeNode, m_listEntry)
+ {
+ if (p->nameEqualsNS(pcszNamespace, pcszMatch))
+ return p;
+ }
+ return NULL;
+}
+
+/**
+ * Convenience method which attempts to find the attribute with the given
+ * name and returns its value as a string.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param ppcsz Where to return the attribute.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ */
+bool ElementNode::getAttributeValue(const char *pcszMatch, const char **ppcsz, const char *pcszNamespace /*= NULL*/) const
+{
+ const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace);
+ if (pAttr)
+ {
+ *ppcsz = pAttr->getValue();
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Convenience method which attempts to find the attribute with the given
+ * name and returns its value as a string.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param pStr Pointer to the string object that should receive the
+ * attribute value.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ *
+ * @throws Whatever the string class may throw on assignment.
+ */
+bool ElementNode::getAttributeValue(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace /*= NULL*/) const
+{
+ const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace);
+ if (pAttr)
+ {
+ *pStr = pAttr->getValue();
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Like getAttributeValue (ministring variant), but makes sure that all backslashes
+ * are converted to forward slashes.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param pStr Pointer to the string object that should
+ * receive the attribute path value.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ */
+bool ElementNode::getAttributeValuePath(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace /*= NULL*/) const
+{
+ if (getAttributeValue(pcszMatch, pStr, pcszNamespace))
+ {
+ pStr->findReplace('\\', '/');
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Convenience method which attempts to find the attribute with the given
+ * name and returns its value as a signed 32-bit integer.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param piValue Where to return the value.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ */
+bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t *piValue, const char *pcszNamespace /*= NULL*/) const
+{
+ const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
+ if (pcsz)
+ {
+ int rc = RTStrToInt32Ex(pcsz, NULL, 0, piValue);
+ if (rc == VINF_SUCCESS)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Convenience method which attempts to find the attribute with the given
+ * name and returns its value as an unsigned 32-bit integer.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param puValue Where to return the value.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ */
+bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t *puValue, const char *pcszNamespace /*= NULL*/) const
+{
+ const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
+ if (pcsz)
+ {
+ int rc = RTStrToUInt32Ex(pcsz, NULL, 0, puValue);
+ if (rc == VINF_SUCCESS)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Convenience method which attempts to find the attribute with the given
+ * name and returns its value as a signed 64-bit integer.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param piValue Where to return the value.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ */
+bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t *piValue, const char *pcszNamespace /*= NULL*/) const
+{
+ const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
+ if (pcsz)
+ {
+ int rc = RTStrToInt64Ex(pcsz, NULL, 0, piValue);
+ if (rc == VINF_SUCCESS)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Convenience method which attempts to find the attribute with the given
+ * name and returns its value as an unsigned 64-bit integer.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param puValue Where to return the value.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ */
+bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t *puValue, const char *pcszNamespace /*= NULL*/) const
+{
+ const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
+ if (pcsz)
+ {
+ int rc = RTStrToUInt64Ex(pcsz, NULL, 0, puValue);
+ if (rc == VINF_SUCCESS)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Convenience method which attempts to find the attribute with the given
+ * name and returns its value as a boolean. This accepts "true", "false",
+ * "yes", "no", "1" or "0" as valid values.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param pfValue Where to return the value.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ */
+bool ElementNode::getAttributeValue(const char *pcszMatch, bool *pfValue, const char *pcszNamespace /*= NULL*/) const
+{
+ const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace);
+ if (pcsz)
+ {
+ if ( !strcmp(pcsz, "true")
+ || !strcmp(pcsz, "yes")
+ || !strcmp(pcsz, "1")
+ )
+ {
+ *pfValue = true;
+ return true;
+ }
+ if ( !strcmp(pcsz, "false")
+ || !strcmp(pcsz, "no")
+ || !strcmp(pcsz, "0")
+ )
+ {
+ *pfValue = false;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Convenience method which attempts to find the attribute with the given
+ * name and returns its value as a string.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param ppcsz Where to return the attribute.
+ * @param cchValueLimit If the length of the returned value exceeds this
+ * limit a EIPRTFailure exception will be thrown.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ */
+bool ElementNode::getAttributeValueN(const char *pcszMatch, const char **ppcsz, size_t cchValueLimit, const char *pcszNamespace /*= NULL*/) const
+{
+ const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace);
+ if (pAttr)
+ {
+ *ppcsz = pAttr->getValueN(cchValueLimit);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Convenience method which attempts to find the attribute with the given
+ * name and returns its value as a string.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param pStr Pointer to the string object that should receive the
+ * attribute value.
+ * @param cchValueLimit If the length of the returned value exceeds this
+ * limit a EIPRTFailure exception will be thrown.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ *
+ * @throws Whatever the string class may throw on assignment.
+ */
+bool ElementNode::getAttributeValueN(const char *pcszMatch, RTCString *pStr, size_t cchValueLimit, const char *pcszNamespace /*= NULL*/) const
+{
+ const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace);
+ if (pAttr)
+ {
+ *pStr = pAttr->getValueN(cchValueLimit);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Like getAttributeValue (ministring variant), but makes sure that all backslashes
+ * are converted to forward slashes.
+ *
+ * @param pcszMatch Name of attribute to find.
+ * @param pStr Pointer to the string object that should
+ * receive the attribute path value.
+ * @param cchValueLimit If the length of the returned value exceeds this
+ * limit a EIPRTFailure exception will be thrown.
+ * @param pcszNamespace The attribute name space prefix or NULL.
+ * @returns Boolean success indicator.
+ */
+bool ElementNode::getAttributeValuePathN(const char *pcszMatch, RTCString *pStr, size_t cchValueLimit, const char *pcszNamespace /*= NULL*/) const
+{
+ if (getAttributeValueN(pcszMatch, pStr, cchValueLimit, pcszNamespace))
+ {
+ pStr->findReplace('\\', '/');
+ return true;
+ }
+
+ return false;
+}
+
+
+bool ElementNode::getElementValue(int32_t *piValue) const
+{
+ const char *pszValue = getValue();
+ if (pszValue)
+ {
+ int rc = RTStrToInt32Ex(pszValue, NULL, 0, piValue);
+ if (rc == VINF_SUCCESS)
+ return true;
+ }
+ return false;
+}
+
+bool ElementNode::getElementValue(uint32_t *puValue) const
+{
+ const char *pszValue = getValue();
+ if (pszValue)
+ {
+ int rc = RTStrToUInt32Ex(pszValue, NULL, 0, puValue);
+ if (rc == VINF_SUCCESS)
+ return true;
+ }
+ return false;
+}
+
+bool ElementNode::getElementValue(int64_t *piValue) const
+{
+ const char *pszValue = getValue();
+ if (pszValue)
+ {
+ int rc = RTStrToInt64Ex(pszValue, NULL, 0, piValue);
+ if (rc == VINF_SUCCESS)
+ return true;
+ }
+ return false;
+}
+
+bool ElementNode::getElementValue(uint64_t *puValue) const
+{
+ const char *pszValue = getValue();
+ if (pszValue)
+ {
+ int rc = RTStrToUInt64Ex(pszValue, NULL, 0, puValue);
+ if (rc == VINF_SUCCESS)
+ return true;
+ }
+ return false;
+}
+
+bool ElementNode::getElementValue(bool *pfValue) const
+{
+ const char *pszValue = getValue();
+ if (pszValue)
+ {
+ if ( !strcmp(pszValue, "true")
+ || !strcmp(pszValue, "yes")
+ || !strcmp(pszValue, "1")
+ )
+ {
+ *pfValue = true;
+ return true;
+ }
+ if ( !strcmp(pszValue, "false")
+ || !strcmp(pszValue, "no")
+ || !strcmp(pszValue, "0")
+ )
+ {
+ *pfValue = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
+ * Creates a new child element node and appends it to the list
+ * of children in "this".
+ *
+ * @param pcszElementName
+ * @return
+ */
+ElementNode *ElementNode::createChild(const char *pcszElementName)
+{
+ // we must be an element, not an attribute
+ if (!m_pLibNode)
+ throw ENodeIsNotElement(RT_SRC_POS);
+
+ // libxml side: create new node
+ xmlNode *pLibNode;
+ if (!(pLibNode = xmlNewNode(NULL, // namespace
+ (const xmlChar*)pcszElementName)))
+ throw std::bad_alloc();
+ xmlAddChild(m_pLibNode, pLibNode);
+
+ // now wrap this in C++
+ ElementNode *p = new ElementNode(m_pElmRoot, this, &m_children, pLibNode);
+ RTListAppend(&m_children, &p->m_listEntry);
+
+ return p;
+}
+
+
+/**
+ * Creates a content node and appends it to the list of children
+ * in "this".
+ *
+ * @param pcszContent
+ * @return
+ */
+ContentNode *ElementNode::addContent(const char *pcszContent)
+{
+ // libxml side: create new node
+ xmlNode *pLibNode = xmlNewText((const xmlChar*)pcszContent);
+ if (!pLibNode)
+ throw std::bad_alloc();
+ xmlAddChild(m_pLibNode, pLibNode);
+
+ // now wrap this in C++
+ ContentNode *p = new ContentNode(this, &m_children, pLibNode);
+ RTListAppend(&m_children, &p->m_listEntry);
+
+ return p;
+}
+
+/**
+ * Changes the contents of node and appends it to the list of
+ * children
+ *
+ * @param pcszContent
+ * @return
+ */
+ContentNode *ElementNode::setContent(const char *pcszContent)
+{
+// 1. Update content
+ xmlNodeSetContent(m_pLibNode, (const xmlChar*)pcszContent);
+
+// 2. Remove Content node from the list
+ /* Check that the order is right. */
+ xml::Node * pNode;
+ RTListForEachCpp(&m_children, pNode, xml::Node, m_listEntry)
+ {
+ bool fLast = RTListNodeIsLast(&m_children, &pNode->m_listEntry);
+
+ if (pNode->isContent())
+ {
+ RTListNodeRemove(&pNode->m_listEntry);
+ }
+
+ if (fLast)
+ break;
+ }
+
+// 3. Create a new node and append to the list
+ // now wrap this in C++
+ ContentNode *pCNode = new ContentNode(this, &m_children, m_pLibNode);
+ RTListAppend(&m_children, &pCNode->m_listEntry);
+
+ return pCNode;
+}
+
+/**
+ * Sets the given attribute; overloaded version for const char *.
+ *
+ * If an attribute with the given name exists, it is overwritten,
+ * otherwise a new attribute is created. Returns the attribute node
+ * that was either created or changed.
+ *
+ * @param pcszName The attribute name.
+ * @param pcszValue The attribute value.
+ * @return Pointer to the attribute node that was created or modified.
+ */
+AttributeNode *ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
+{
+ /*
+ * Do we already have an attribute and should we just update it?
+ */
+ AttributeNode *pAttr;
+ RTListForEachCpp(&m_attributes, pAttr, AttributeNode, m_listEntry)
+ {
+ if (pAttr->nameEquals(pcszName))
+ {
+ /* Overwrite existing libxml attribute node ... */
+ xmlAttrPtr pLibAttr = xmlSetProp(m_pLibNode, (xmlChar *)pcszName, (xmlChar *)pcszValue);
+
+ /* ... and update our C++ wrapper in case the attrib pointer changed. */
+ pAttr->m_pLibAttr = pLibAttr;
+ return pAttr;
+ }
+ }
+
+ /*
+ * No existing attribute, create a new one.
+ */
+ /* libxml side: xmlNewProp creates an attribute. */
+ xmlAttr *pLibAttr = xmlNewProp(m_pLibNode, (xmlChar *)pcszName, (xmlChar *)pcszValue);
+
+ /* C++ side: Create an attribute node around it. */
+ pAttr = new AttributeNode(m_pElmRoot, this, &m_attributes, pLibAttr);
+ RTListAppend(&m_attributes, &pAttr->m_listEntry);
+
+ return pAttr;
+}
+
+/**
+ * Like setAttribute (ministring variant), but replaces all backslashes with forward slashes
+ * before calling that one.
+ * @param pcszName
+ * @param strValue
+ * @return
+ */
+AttributeNode* ElementNode::setAttributePath(const char *pcszName, const RTCString &strValue)
+{
+ RTCString strTemp(strValue);
+ strTemp.findReplace('\\', '/');
+ return setAttribute(pcszName, strTemp.c_str());
+}
+
+/**
+ * Sets the given attribute; overloaded version for int32_t.
+ *
+ * If an attribute with the given name exists, it is overwritten,
+ * otherwise a new attribute is created. Returns the attribute node
+ * that was either created or changed.
+ *
+ * @param pcszName
+ * @param i
+ * @return
+ */
+AttributeNode* ElementNode::setAttribute(const char *pcszName, int32_t i)
+{
+ char szValue[12]; // negative sign + 10 digits + \0
+ RTStrPrintf(szValue, sizeof(szValue), "%RI32", i);
+ AttributeNode *p = setAttribute(pcszName, szValue);
+ return p;
+}
+
+/**
+ * Sets the given attribute; overloaded version for uint32_t.
+ *
+ * If an attribute with the given name exists, it is overwritten,
+ * otherwise a new attribute is created. Returns the attribute node
+ * that was either created or changed.
+ *
+ * @param pcszName
+ * @param u
+ * @return
+ */
+AttributeNode* ElementNode::setAttribute(const char *pcszName, uint32_t u)
+{
+ char szValue[11]; // 10 digits + \0
+ RTStrPrintf(szValue, sizeof(szValue), "%RU32", u);
+ AttributeNode *p = setAttribute(pcszName, szValue);
+ return p;
+}
+
+/**
+ * Sets the given attribute; overloaded version for int64_t.
+ *
+ * If an attribute with the given name exists, it is overwritten,
+ * otherwise a new attribute is created. Returns the attribute node
+ * that was either created or changed.
+ *
+ * @param pcszName
+ * @param i
+ * @return
+ */
+AttributeNode* ElementNode::setAttribute(const char *pcszName, int64_t i)
+{
+ char szValue[21]; // negative sign + 19 digits + \0
+ RTStrPrintf(szValue, sizeof(szValue), "%RI64", i);
+ AttributeNode *p = setAttribute(pcszName, szValue);
+ return p;
+}
+
+/**
+ * Sets the given attribute; overloaded version for uint64_t.
+ *
+ * If an attribute with the given name exists, it is overwritten,
+ * otherwise a new attribute is created. Returns the attribute node
+ * that was either created or changed.
+ *
+ * @param pcszName
+ * @param u
+ * @return
+ */
+AttributeNode* ElementNode::setAttribute(const char *pcszName, uint64_t u)
+{
+ char szValue[21]; // 20 digits + \0
+ RTStrPrintf(szValue, sizeof(szValue), "%RU64", u);
+ AttributeNode *p = setAttribute(pcszName, szValue);
+ return p;
+}
+
+/**
+ * Sets the given attribute to the given uint32_t, outputs a hexadecimal string.
+ *
+ * If an attribute with the given name exists, it is overwritten,
+ * otherwise a new attribute is created. Returns the attribute node
+ * that was either created or changed.
+ *
+ * @param pcszName
+ * @param u
+ * @return
+ */
+AttributeNode* ElementNode::setAttributeHex(const char *pcszName, uint32_t u)
+{
+ char szValue[11]; // "0x" + 8 digits + \0
+ RTStrPrintf(szValue, sizeof(szValue), "0x%RX32", u);
+ AttributeNode *p = setAttribute(pcszName, szValue);
+ return p;
+}
+
+/**
+ * Sets the given attribute; overloaded version for bool.
+ *
+ * If an attribute with the given name exists, it is overwritten,
+ * otherwise a new attribute is created. Returns the attribute node
+ * that was either created or changed.
+ *
+ * @param pcszName The attribute name.
+ * @param f The attribute value.
+ * @return
+ */
+AttributeNode* ElementNode::setAttribute(const char *pcszName, bool f)
+{
+ return setAttribute(pcszName, (f) ? "true" : "false");
+}
+
+/**
+ * Private constructor for a new attribute node.
+ *
+ * @param pElmRoot Pointer to the root element. Needed for getting the
+ * default name space.
+ * @param pParent Pointer to the parent element (always an ElementNode,
+ * despite the type). NULL for the root node.
+ * @param pListAnchor Pointer to the m_children member of the parent. NULL
+ * for the root node.
+ * @param pLibAttr Pointer to the libxml2 attribute structure.
+ */
+AttributeNode::AttributeNode(const ElementNode *pElmRoot,
+ Node *pParent,
+ PRTLISTANCHOR pListAnchor,
+ xmlAttr *pLibAttr)
+ : Node(IsAttribute,
+ pParent,
+ pListAnchor,
+ NULL,
+ pLibAttr)
+{
+ m_pcszName = (const char *)pLibAttr->name;
+ RT_NOREF_PV(pElmRoot);
+
+ if ( pLibAttr->ns
+ && pLibAttr->ns->prefix)
+ {
+ m_pcszNamespacePrefix = (const char *)pLibAttr->ns->prefix;
+ m_pcszNamespaceHref = (const char *)pLibAttr->ns->href;
+ }
+}
+
+ContentNode::ContentNode(Node *pParent, PRTLISTANCHOR pListAnchor, xmlNode *pLibNode)
+ : Node(IsContent,
+ pParent,
+ pListAnchor,
+ pLibNode,
+ NULL)
+{
+}
+
+/*
+ * NodesLoop
+ *
+ */
+
+struct NodesLoop::Data
+{
+ ElementNodesList listElements;
+ ElementNodesList::const_iterator it;
+};
+
+NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
+{
+ m = new Data;
+ node.getChildElements(m->listElements, pcszMatch);
+ m->it = m->listElements.begin();
+}
+
+NodesLoop::~NodesLoop()
+{
+ delete m;
+}
+
+
+/**
+ * Handy convenience helper for looping over all child elements. Create an
+ * instance of NodesLoop on the stack and call this method until it returns
+ * NULL, like this:
+ * <code>
+ * xml::ElementNode node; // should point to an element
+ * xml::NodesLoop loop(node, "child"); // find all "child" elements under node
+ * const xml::ElementNode *pChild = NULL;
+ * while (pChild = loop.forAllNodes())
+ * ...;
+ * </code>
+ * @return
+ */
+const ElementNode* NodesLoop::forAllNodes() const
+{
+ const ElementNode *pNode = NULL;
+
+ if (m->it != m->listElements.end())
+ {
+ pNode = *(m->it);
+ ++(m->it);
+ }
+
+ return pNode;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Document class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+struct Document::Data
+{
+ xmlDocPtr plibDocument;
+ ElementNode *pRootElement;
+ ElementNode *pComment;
+
+ Data()
+ {
+ plibDocument = NULL;
+ pRootElement = NULL;
+ pComment = NULL;
+ }
+
+ ~Data()
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ if (plibDocument)
+ {
+ xmlFreeDoc(plibDocument);
+ plibDocument = NULL;
+ }
+ if (pRootElement)
+ {
+ delete pRootElement;
+ pRootElement = NULL;
+ }
+ if (pComment)
+ {
+ delete pComment;
+ pComment = NULL;
+ }
+ }
+
+ void copyFrom(const Document::Data *p)
+ {
+ if (p->plibDocument)
+ {
+ plibDocument = xmlCopyDoc(p->plibDocument,
+ 1); // recursive == copy all
+ }
+ }
+};
+
+Document::Document()
+ : m(new Data)
+{
+}
+
+Document::Document(const Document &x)
+ : m(new Data)
+{
+ m->copyFrom(x.m);
+}
+
+Document& Document::operator=(const Document &x)
+{
+ m->reset();
+ m->copyFrom(x.m);
+ return *this;
+}
+
+Document::~Document()
+{
+ delete m;
+}
+
+/**
+ * private method to refresh all internal structures after the internal pDocument
+ * has changed. Called from XmlFileParser::read(). m->reset() must have been
+ * called before to make sure all members except the internal pDocument are clean.
+ */
+void Document::refreshInternals() // private
+{
+ m->pRootElement = new ElementNode(NULL, NULL, NULL, xmlDocGetRootElement(m->plibDocument));
+
+ ElementNode::buildChildren(m->pRootElement);
+}
+
+/**
+ * Returns the root element of the document, or NULL if the document is empty.
+ * Const variant.
+ * @return
+ */
+const ElementNode *Document::getRootElement() const
+{
+ return m->pRootElement;
+}
+
+/**
+ * Returns the root element of the document, or NULL if the document is empty.
+ * Non-const variant.
+ * @return
+ */
+ElementNode *Document::getRootElement()
+{
+ return m->pRootElement;
+}
+
+/**
+ * Creates a new element node and sets it as the root element.
+ *
+ * This will only work if the document is empty; otherwise EDocumentNotEmpty is
+ * thrown.
+ */
+ElementNode *Document::createRootElement(const char *pcszRootElementName,
+ const char *pcszComment /* = NULL */)
+{
+ if (m->pRootElement || m->plibDocument)
+ throw EDocumentNotEmpty(RT_SRC_POS);
+
+ // libxml side: create document, create root node
+ m->plibDocument = xmlNewDoc((const xmlChar *)"1.0");
+ xmlNode *plibRootNode = xmlNewNode(NULL /*namespace*/ , (const xmlChar *)pcszRootElementName);
+ if (!plibRootNode)
+ throw std::bad_alloc();
+ xmlDocSetRootElement(m->plibDocument, plibRootNode);
+
+ // now wrap this in C++
+ m->pRootElement = new ElementNode(NULL, NULL, NULL, plibRootNode);
+
+ // add document global comment if specified
+ if (pcszComment != NULL)
+ {
+ xmlNode *pComment = xmlNewDocComment(m->plibDocument, (const xmlChar *)pcszComment);
+ if (!pComment)
+ throw std::bad_alloc();
+ xmlAddPrevSibling(plibRootNode, pComment);
+
+ // now wrap this in C++
+ m->pComment = new ElementNode(NULL, NULL, NULL, pComment);
+ }
+
+ return m->pRootElement;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// XmlParserBase class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+static void xmlParserBaseGenericError(void *pCtx, const char *pszMsg, ...) RT_NOTHROW_DEF
+{
+ NOREF(pCtx);
+ va_list args;
+ va_start(args, pszMsg);
+ RTLogRelPrintfV(pszMsg, args);
+ va_end(args);
+}
+
+static void xmlParserBaseStructuredError(void *pCtx, xmlErrorPtr error) RT_NOTHROW_DEF
+{
+ NOREF(pCtx);
+ /* we expect that there is always a trailing NL */
+ LogRel(("XML error at '%s' line %d: %s", error->file, error->line, error->message));
+}
+
+XmlParserBase::XmlParserBase()
+{
+ m_ctxt = xmlNewParserCtxt();
+ if (m_ctxt == NULL)
+ throw std::bad_alloc();
+ /* per-thread so it must be here */
+ xmlSetGenericErrorFunc(NULL, xmlParserBaseGenericError);
+ xmlSetStructuredErrorFunc(NULL, xmlParserBaseStructuredError);
+}
+
+XmlParserBase::~XmlParserBase()
+{
+ xmlSetStructuredErrorFunc(NULL, NULL);
+ xmlSetGenericErrorFunc(NULL, NULL);
+ xmlFreeParserCtxt (m_ctxt);
+ m_ctxt = NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// XmlMemParser class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+XmlMemParser::XmlMemParser()
+ : XmlParserBase()
+{
+}
+
+XmlMemParser::~XmlMemParser()
+{
+}
+
+/**
+ * Parse the given buffer and fills the given Document object with its contents.
+ * Throws XmlError on parsing errors.
+ *
+ * The document that is passed in will be reset before being filled if not empty.
+ *
+ * @param pvBuf Memory buffer to parse.
+ * @param cbSize Size of the memory buffer.
+ * @param strFilename Refernece to the name of the file we're parsing.
+ * @param doc Reference to the output document. This will be reset
+ * and filled with data according to file contents.
+ */
+void XmlMemParser::read(const void *pvBuf, size_t cbSize,
+ const RTCString &strFilename,
+ Document &doc)
+{
+ GlobalLock lock;
+// global.setExternalEntityLoader(ExternalEntityLoader);
+
+ const char *pcszFilename = strFilename.c_str();
+
+ doc.m->reset();
+ const int options = XML_PARSE_NOBLANKS /* remove blank nodes */
+ | XML_PARSE_NONET /* forbit any network access */
+#if LIBXML_VERSION >= 20700
+ | XML_PARSE_HUGE /* don't restrict the node depth
+ to 256 (bad for snapshots!) */
+#endif
+ ;
+ if (!(doc.m->plibDocument = xmlCtxtReadMemory(m_ctxt,
+ (const char*)pvBuf,
+ (int)cbSize,
+ pcszFilename,
+ NULL, // encoding = auto
+ options)))
+ throw XmlError(xmlCtxtGetLastError(m_ctxt));
+
+ doc.refreshInternals();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// XmlMemWriter class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+XmlMemWriter::XmlMemWriter()
+ : m_pBuf(0)
+{
+}
+
+XmlMemWriter::~XmlMemWriter()
+{
+ if (m_pBuf)
+ xmlFree(m_pBuf);
+}
+
+void XmlMemWriter::write(const Document &doc, void **ppvBuf, size_t *pcbSize)
+{
+ if (m_pBuf)
+ {
+ xmlFree(m_pBuf);
+ m_pBuf = 0;
+ }
+ int size;
+ xmlDocDumpFormatMemory(doc.m->plibDocument, (xmlChar**)&m_pBuf, &size, 1);
+ *ppvBuf = m_pBuf;
+ *pcbSize = size;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// XmlStringWriter class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+XmlStringWriter::XmlStringWriter()
+ : m_pStrDst(NULL), m_fOutOfMemory(false)
+{
+}
+
+int XmlStringWriter::write(const Document &rDoc, RTCString *pStrDst)
+{
+ /*
+ * Clear the output string and take the global libxml2 lock so we can
+ * safely configure the output formatting.
+ */
+ pStrDst->setNull();
+
+ GlobalLock lock;
+
+ xmlIndentTreeOutput = 1;
+ xmlTreeIndentString = " ";
+ xmlSaveNoEmptyTags = 0;
+
+ /*
+ * Do a pass to calculate the size.
+ */
+ size_t cbOutput = 1; /* zero term */
+
+ xmlSaveCtxtPtr pSaveCtx= xmlSaveToIO(WriteCallbackForSize, CloseCallback, &cbOutput, NULL /*pszEncoding*/, XML_SAVE_FORMAT);
+ if (!pSaveCtx)
+ return VERR_NO_MEMORY;
+
+ long rcXml = xmlSaveDoc(pSaveCtx, rDoc.m->plibDocument);
+ xmlSaveClose(pSaveCtx);
+ if (rcXml == -1)
+ return VERR_GENERAL_FAILURE;
+
+ /*
+ * Try resize the string.
+ */
+ int rc = pStrDst->reserveNoThrow(cbOutput);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do the real run where we feed output to the string.
+ */
+ m_pStrDst = pStrDst;
+ m_fOutOfMemory = false;
+ pSaveCtx = xmlSaveToIO(WriteCallbackForReal, CloseCallback, this, NULL /*pszEncoding*/, XML_SAVE_FORMAT);
+ if (pSaveCtx)
+ {
+ rcXml = xmlSaveDoc(pSaveCtx, rDoc.m->plibDocument);
+ xmlSaveClose(pSaveCtx);
+ m_pStrDst = NULL;
+ if (rcXml != -1)
+ {
+ if (!m_fOutOfMemory)
+ return VINF_SUCCESS;
+
+ rc = VERR_NO_STR_MEMORY;
+ }
+ else
+ rc = VERR_GENERAL_FAILURE;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ pStrDst->setNull();
+ m_pStrDst = NULL;
+ }
+ return rc;
+}
+
+/*static*/ int XmlStringWriter::WriteCallbackForSize(void *pvUser, const char *pachBuf, int cbToWrite) RT_NOTHROW_DEF
+{
+ if (cbToWrite > 0)
+ *(size_t *)pvUser += (unsigned)cbToWrite;
+ RT_NOREF(pachBuf);
+ return cbToWrite;
+}
+
+/*static*/ int XmlStringWriter::WriteCallbackForReal(void *pvUser, const char *pachBuf, int cbToWrite) RT_NOTHROW_DEF
+{
+ XmlStringWriter *pThis = static_cast<XmlStringWriter*>(pvUser);
+ if (!pThis->m_fOutOfMemory)
+ {
+ if (cbToWrite > 0)
+ {
+ try
+ {
+ pThis->m_pStrDst->append(pachBuf, (size_t)cbToWrite);
+ }
+ catch (std::bad_alloc &)
+ {
+ pThis->m_fOutOfMemory = true;
+ return -1;
+ }
+ }
+ return cbToWrite;
+ }
+ return -1; /* failure */
+}
+
+/*static*/ int XmlStringWriter::CloseCallback(void *pvUser) RT_NOTHROW_DEF
+{
+ /* Nothing to do here. */
+ RT_NOREF(pvUser);
+ return 0;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// XmlFileParser class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+struct XmlFileParser::Data
+{
+ RTCString strXmlFilename;
+
+ Data()
+ {
+ }
+
+ ~Data()
+ {
+ }
+};
+
+XmlFileParser::XmlFileParser()
+ : XmlParserBase(),
+ m(new Data())
+{
+}
+
+XmlFileParser::~XmlFileParser()
+{
+ delete m;
+ m = NULL;
+}
+
+struct IOContext
+{
+ File file;
+ RTCString error;
+
+ IOContext(const char *pcszFilename, File::Mode mode, bool fFlush = false)
+ : file(mode, pcszFilename, fFlush)
+ {
+ }
+
+ void setError(const RTCError &x)
+ {
+ error = x.what();
+ }
+
+ void setError(const std::exception &x)
+ {
+ error = x.what();
+ }
+
+private:
+ DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(IOContext); /* (shuts up C4626 and C4625 MSC warnings) */
+};
+
+struct ReadContext : IOContext
+{
+ ReadContext(const char *pcszFilename)
+ : IOContext(pcszFilename, File::Mode_Read)
+ {
+ }
+
+private:
+ DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(ReadContext); /* (shuts up C4626 and C4625 MSC warnings) */
+};
+
+struct WriteContext : IOContext
+{
+ WriteContext(const char *pcszFilename, bool fFlush)
+ : IOContext(pcszFilename, File::Mode_Overwrite, fFlush)
+ {
+ }
+
+private:
+ DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(WriteContext); /* (shuts up C4626 and C4625 MSC warnings) */
+};
+
+/**
+ * Reads the given file and fills the given Document object with its contents.
+ * Throws XmlError on parsing errors.
+ *
+ * The document that is passed in will be reset before being filled if not empty.
+ *
+ * @param strFilename in: name fo file to parse.
+ * @param doc out: document to be reset and filled with data according to file contents.
+ */
+void XmlFileParser::read(const RTCString &strFilename,
+ Document &doc)
+{
+ GlobalLock lock;
+// global.setExternalEntityLoader(ExternalEntityLoader);
+
+ m->strXmlFilename = strFilename;
+ const char *pcszFilename = strFilename.c_str();
+
+ ReadContext context(pcszFilename);
+ doc.m->reset();
+ const int options = XML_PARSE_NOBLANKS /* remove blank nodes */
+ | XML_PARSE_NONET /* forbit any network access */
+#if LIBXML_VERSION >= 20700
+ | XML_PARSE_HUGE /* don't restrict the node depth
+ to 256 (bad for snapshots!) */
+#endif
+ ;
+ if (!(doc.m->plibDocument = xmlCtxtReadIO(m_ctxt,
+ ReadCallback,
+ CloseCallback,
+ &context,
+ pcszFilename,
+ NULL, // encoding = auto
+ options)))
+ throw XmlError(xmlCtxtGetLastError(m_ctxt));
+
+ doc.refreshInternals();
+}
+
+/*static*/ int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen) RT_NOTHROW_DEF
+{
+ ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
+
+ /* To prevent throwing exceptions while inside libxml2 code, we catch
+ * them and forward to our level using a couple of variables. */
+
+ try
+ {
+ return pContext->file.read(aBuf, aLen);
+ }
+ catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
+ catch (const RTCError &err) { pContext->setError(err); }
+ catch (const std::exception &err) { pContext->setError(err); }
+ catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
+
+ return -1 /* failure */;
+}
+
+/*static*/ int XmlFileParser::CloseCallback(void *aCtxt) RT_NOTHROW_DEF
+{
+ /// @todo to be written
+ NOREF(aCtxt);
+
+ return -1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// XmlFileWriter class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+struct XmlFileWriter::Data
+{
+ Document *pDoc;
+};
+
+XmlFileWriter::XmlFileWriter(Document &doc)
+{
+ m = new Data();
+ m->pDoc = &doc;
+}
+
+XmlFileWriter::~XmlFileWriter()
+{
+ delete m;
+}
+
+void XmlFileWriter::writeInternal(const char *pcszFilename, bool fSafe)
+{
+ WriteContext context(pcszFilename, fSafe);
+
+ GlobalLock lock;
+
+ /* serialize to the stream */
+ xmlIndentTreeOutput = 1;
+ xmlTreeIndentString = " ";
+ xmlSaveNoEmptyTags = 0;
+
+ xmlSaveCtxtPtr saveCtxt;
+ if (!(saveCtxt = xmlSaveToIO(WriteCallback,
+ CloseCallback,
+ &context,
+ NULL,
+ XML_SAVE_FORMAT)))
+ throw xml::LogicError(RT_SRC_POS);
+
+ long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
+ if (rc == -1)
+ {
+ /* look if there was a forwarded exception from the lower level */
+// if (m->trappedErr.get() != NULL)
+// m->trappedErr->rethrow();
+
+ /* there must be an exception from the Output implementation,
+ * otherwise the save operation must always succeed. */
+ throw xml::LogicError(RT_SRC_POS);
+ }
+
+ xmlSaveClose(saveCtxt);
+}
+
+void XmlFileWriter::write(const char *pcszFilename, bool fSafe)
+{
+ if (!fSafe)
+ writeInternal(pcszFilename, fSafe);
+ else
+ {
+ /* Empty string and directory spec must be avoid. */
+ if (RTPathFilename(pcszFilename) == NULL)
+ throw xml::LogicError(RT_SRC_POS);
+
+ /* Construct both filenames first to ease error handling. */
+ char szTmpFilename[RTPATH_MAX];
+ int rc = RTStrCopy(szTmpFilename, sizeof(szTmpFilename) - strlen(s_pszTmpSuff), pcszFilename);
+ if (RT_FAILURE(rc))
+ throw EIPRTFailure(rc, "RTStrCopy");
+ strcat(szTmpFilename, s_pszTmpSuff);
+
+ char szPrevFilename[RTPATH_MAX];
+ rc = RTStrCopy(szPrevFilename, sizeof(szPrevFilename) - strlen(s_pszPrevSuff), pcszFilename);
+ if (RT_FAILURE(rc))
+ throw EIPRTFailure(rc, "RTStrCopy");
+ strcat(szPrevFilename, s_pszPrevSuff);
+
+ /* Write the XML document to the temporary file. */
+ writeInternal(szTmpFilename, fSafe);
+
+ /* Make a backup of any existing file (ignore failure). */
+ uint64_t cbPrevFile;
+ rc = RTFileQuerySizeByPath(pcszFilename, &cbPrevFile);
+ if (RT_SUCCESS(rc) && cbPrevFile >= 16)
+ RTFileRename(pcszFilename, szPrevFilename, RTPATHRENAME_FLAGS_REPLACE);
+
+ /* Commit the temporary file. Just leave the tmp file behind on failure. */
+ rc = RTFileRename(szTmpFilename, pcszFilename, RTPATHRENAME_FLAGS_REPLACE);
+ if (RT_FAILURE(rc))
+ throw EIPRTFailure(rc, "Failed to replace '%s' with '%s'", pcszFilename, szTmpFilename);
+
+ /* Flush the directory changes (required on linux at least). */
+ RTPathStripFilename(szTmpFilename);
+ rc = RTDirFlush(szTmpFilename);
+ AssertMsg(RT_SUCCESS(rc) || rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED, ("%Rrc\n", rc));
+ }
+}
+
+/*static*/ int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen) RT_NOTHROW_DEF
+{
+ WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
+
+ /* To prevent throwing exceptions while inside libxml2 code, we catch
+ * them and forward to our level using a couple of variables. */
+ try
+ {
+ return pContext->file.write(aBuf, aLen);
+ }
+ catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
+ catch (const RTCError &err) { pContext->setError(err); }
+ catch (const std::exception &err) { pContext->setError(err); }
+ catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
+
+ return -1 /* failure */;
+}
+
+/*static*/ int XmlFileWriter::CloseCallback(void *aCtxt) RT_NOTHROW_DEF
+{
+ /// @todo to be written
+ NOREF(aCtxt);
+
+ return -1;
+}
+
+/*static*/ const char * const XmlFileWriter::s_pszTmpSuff = "-tmp";
+/*static*/ const char * const XmlFileWriter::s_pszPrevSuff = "-prev";
+
+
+} // end namespace xml
+