[Pkg-virtualbox-commits] [kbuild] 01/03: Imported Upstream version 0.1.9998svn2997+dfsg
Gianfranco Costamagna
locutusofborg at moszumanska.debian.org
Thu Nov 3 10:35:34 UTC 2016
This is an automated email from the git hooks/post-receive script.
locutusofborg pushed a commit to branch experimental
in repository kbuild.
commit c1266c442ed144fd8b9960294b7e194a5219c7b6
Author: Gianfranco Costamagna <locutusofborg at debian.org>
Date: Thu Nov 3 11:17:27 2016 +0100
Imported Upstream version 0.1.9998svn2997+dfsg
---
kBuild/footer-pass2-compiling-targets.kmk | 6 +-
kBuild/footer-pass2-fetches.kmk | 5 +-
kBuild/header.kmk | 33 +-
kBuild/tools/NASM.kmk | 4 +-
kBuild/tools/VCC100.kmk | 124 +-
kBuild/tools/VCC100AMD64.kmk | 166 +-
kBuild/tools/VCC100X86.kmk | 156 +-
kBuild/units/qt4.kmk | 49 +-
kBuild/units/qt5.kmk | 78 +-
kBuild/units/vccprecomp.kmk | 66 +
src/kDepPre/kDepPre.c | 4 +-
src/kObjCache/kObjCache.c | 6 +-
src/kWorker/Makefile.kmk | 13 +-
src/kWorker/kWorker.c | 3789 ++++++++++++++++++++++-----
src/kmk/Makefile.kmk | 20 +-
src/kmk/dir-nt-bird.c | 110 +-
src/kmk/dir.c | 9 +
src/kmk/function.c | 12 +
src/kmk/kmkbuiltin.c | 12 +-
src/kmk/kmkbuiltin.h | 10 +-
src/kmk/kmkbuiltin/common-env-and-cwd-opt.c | 240 ++
src/kmk/kmkbuiltin/cp.c | 3 +-
src/kmk/kmkbuiltin/kDepIDB.c | 4 +-
src/kmk/kmkbuiltin/kDepObj.c | 99 +-
src/kmk/kmkbuiltin/kSubmit.c | 239 +-
src/kmk/kmkbuiltin/md5sum.c | 42 +-
src/kmk/kmkbuiltin/redirect.c | 1817 ++++++++++---
src/kmk/kmkbuiltin/rm.c | 22 +-
src/kmk/kmkbuiltin/rmdir.c | 20 +-
src/kmk/main.c | 39 +-
src/kmk/make.h | 1 +
src/kmk/w32/include/sub_proc.h | 1 +
src/kmk/w32/subproc/sub_proc.c | 26 +-
src/lib/Makefile.kmk | 11 +-
src/lib/kDep.c | 27 +-
src/lib/kDep.h | 4 +-
src/lib/kStuff/include/k/kHlpAssert.h | 20 +-
src/lib/kStuff/include/k/kTypes.h | 31 +-
src/lib/msc_buffered_printf.c | 18 +-
src/lib/nt/fts-nt.c | 1015 +++++++
src/lib/nt/fts-nt.h | 177 ++
src/lib/nt/kFsCache.c | 826 ++++--
src/lib/nt/kFsCache.h | 98 +-
src/lib/nt/ntdir.c | 125 +-
src/lib/nt/ntdir.h | 18 +-
src/lib/nt/nthlp.h | 23 +-
src/lib/nt/nthlpcore.c | 20 +-
src/lib/nt/nthlpfs.c | 458 +++-
src/lib/nt/ntstat.c | 239 +-
src/lib/nt/ntstat.h | 6 +-
src/lib/nt/ntstuff.h | 58 +-
src/lib/nt/ntunlink.c | 43 +-
src/lib/nt/ntunlink.h | 5 +-
src/lib/nt/tstNtFts.c | 244 ++
src/lib/quote_argv.c | 8 +-
src/lib/quote_argv.h | 4 +-
56 files changed, 8680 insertions(+), 2023 deletions(-)
diff --git a/kBuild/footer-pass2-compiling-targets.kmk b/kBuild/footer-pass2-compiling-targets.kmk
index a04bdbf..5e3c6a8 100644
--- a/kBuild/footer-pass2-compiling-targets.kmk
+++ b/kBuild/footer-pass2-compiling-targets.kmk
@@ -1,4 +1,4 @@
-# $Id: footer-pass2-compiling-targets.kmk 2795 2015-09-15 23:35:37Z bird $
+# $Id: footer-pass2-compiling-targets.kmk 2952 2016-09-21 18:52:08Z bird $
## @file
# kBuild - Footer - Target lists - Pass 2 - Compiling Targets.
#
@@ -83,7 +83,7 @@ $(obj) + $($(target)_$(source)_OUTPUT_) +| $($(target)_$(source)_OUTPUT_MAYBE_)
$$$$($(target)_INTERMEDIATES.$(bld_trg_cpu)) \
$$$$($(target)_INTERMEDIATES.$(bld_type))
%$$(call MSG_COMPILE,$(target),$(source),$$@,$(type))
-ifdef TOOL_$(tool)_COMPILE_$(type)_DONT_PURGE_OUTPUT
+ifndef TOOL_$(tool)_COMPILE_$(type)_DONT_PURGE_OUTPUT
$$(QUIET)$$(RM) -f -- $(dep) $(obj) $($(target)_$(source)_OUTPUT_) $($(target)_OUTPUT_MAYBE_)
endif
endif
@@ -490,6 +490,7 @@ local units := \
$($(target)_USES.$(bld_type))\
$($(target)_USES)
$(foreach unit,$(units),$(evalvalctx def_unit_$(unit)_target_pre))
+$(foreach unit,$(units),$(evalvalctx def_unit_$(unit)_target_pre_2))
# source -> object
$(evalval def_target_sources)
@@ -694,6 +695,7 @@ local units := \
$($(target)_USES.$(bld_type))\
$($(target)_USES)
$(foreach unit,$(units),$(evalvalctx def_unit_$(unit)_target_pre))
+$(foreach unit,$(units),$(evalvalctx def_unit_$(unit)_target_pre_2))
# source -> object
$(evalval def_target_sources)
diff --git a/kBuild/footer-pass2-fetches.kmk b/kBuild/footer-pass2-fetches.kmk
index dcd19e8..48f333e 100644
--- a/kBuild/footer-pass2-fetches.kmk
+++ b/kBuild/footer-pass2-fetches.kmk
@@ -1,4 +1,4 @@
-# $Id: footer-pass2-fetches.kmk 2726 2014-02-26 23:23:54Z bird $
+# $Id: footer-pass2-fetches.kmk 2912 2016-09-14 13:36:15Z bird $
## @file
# kBuild - Footer - Target lists - Pass 2 - Fetches.
#
@@ -361,6 +361,9 @@ $(out): $(comp-vars _TARGET_$(target)_DIGEST_PREV,_TARGET_$(target)_DIGEST,FORCE
%$$(if $$(_TARGET_$(target)_DIGEST),$$(if $$(eq $$(file-size $(out).lst),-1)\
,$$(call MSG_REFETCH,$(target)),$$(call MSG_FETCH,$(target))),$$(call MSG_UNFETCH,$(target)))
$$(QUIET)$(TEST_EXT) -f $(out).lst -- $$(MAKE) -f $(MAKEFILE) --no-print-directory $(out)_unfetched
+if $(KBUILD_KMK_REVISION) > 2911
+ $$(QUIET)kmk_builtin_dircache deleted "$(dir $(out))"
+endif
$$(QUIET)$$(if $$(_TARGET_$(target)_DIGEST),$$(MAKE) -f $(MAKEFILE) --no-print-directory $(out).lst,$$(RMDIR) -p --ignore-fail-on-non-empty --ignore-fail-on-not-exist -- $$(dir $$@))
$$(QUIET2)$$(if $$(_TARGET_$(target)_DIGEST),$$(APPEND) $$@ "_TARGET_$(target)_DIGEST_PREV := $(_TARGET_$(target)_DIGEST)")
diff --git a/kBuild/header.kmk b/kBuild/header.kmk
index 462213a..21a0e3d 100644
--- a/kBuild/header.kmk
+++ b/kBuild/header.kmk
@@ -1,4 +1,4 @@
-# $Id: header.kmk 2895 2016-09-08 13:28:37Z bird $
+# $Id: header.kmk 2971 2016-09-27 11:25:09Z bird $
## @file
# kBuild - File included at top of a makefile.
#
@@ -79,7 +79,7 @@ endif
# The revision in which this file was last modified.
# This can be useful when using development versions of kBuild.
#
-KMK_REVISION := $(patsubst %:,, $Rev: 2895 $ )
+KMK_REVISION := $(patsubst %:,, $Rev: 2971 $ )
#
@@ -195,6 +195,27 @@ KBUILD_ARCHES_32 := x86 sparc32 s390 ppc32 mips32 hppa32 arm
#
+# Mapping of kBuild OS + ARCH to GNU system type wildcards.
+# For use with the foreach/append/prepend and wildcard functions.
+#
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.darwin.x86 = i?86-apple-darwin*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.darwin.amd64 = x86_64-apple-darwin* amd64-apple-darwin*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.freebsd.x86 = i?86-*freebsd*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.freebsd.amd64 = x86_64-*freebsd* amd64-*freebsd*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.linux.x86 = i?86-*linux-gnu
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.linux.amd64 = x86_64-*linux-gnu amd64-*linux-gnu
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.netbsd.x86 = i?86-*netbsd*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.netbsd.amd64 = x86_64-*netbsd* amd64-*netbsd*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.os2.x86 = i?86-*os2*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.solaris.x86 = i?86-*solaris2*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.solaris.amd64 = amd64-*solaris2* x86_64-*solaris2*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.solaris.sparc32 = sparc-*solaris2*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.solaris.sparc64 = sparc64-*solaris2* sparcv9-*solaris2*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.win.x86 = i?86-*mingw32* i?86-*msys* i?86-*cygwin*
+KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.win.amd64 = x86_64-*mingw64* amd64-*mingw64* x86_64-*msys* amd64-*msys* x86_64-*cygwin* amd64-*cygwin*
+
+
+#
# Set default build type.
#
ifndef KBUILD_TYPE
@@ -369,6 +390,10 @@ else
endif
endif
+# Short hand for $(KBUILD_TARGET).$(KBUILD_TARGET_ARCH).
+KBUILD_TARGET_DOT_ARCH = $(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)
+KBUILD_HOST_DOT_ARCH = $(KBUILD_HOST).$(KBUILD_TARGET_ARCH)
+
#
# Paths and stuff.
@@ -701,7 +726,11 @@ PRINTF_INT := kmk_builtin_printf
PRINTF := $(PRINTF_INT)
REDIRECT_EXT:= $(KBUILD_BIN_PATH)/kmk_redirect$(HOSTSUFF_EXE)
+if $(KBUILD_KMK_REVISION) > 2911
+REDIRECT_INT:= kmk_builtin_redirect
+else
REDIRECT_INT:= $(REDIRECT_EXT)
+endif
REDIRECT := $(REDIRECT_INT)
RM_EXT := $(KBUILD_BIN_PATH)/kmk_rm$(HOSTSUFF_EXE)
diff --git a/kBuild/tools/NASM.kmk b/kBuild/tools/NASM.kmk
index 4846033..b3ab243 100644
--- a/kBuild/tools/NASM.kmk
+++ b/kBuild/tools/NASM.kmk
@@ -1,4 +1,4 @@
-# $Id: NASM.kmk 2890 2016-09-07 23:39:29Z bird $
+# $Id: NASM.kmk 2929 2016-09-16 21:19:14Z bird $
## @file
# kBuild Tool Config - Netwide Assembler v0.98+.
#
@@ -86,7 +86,7 @@ ifdef TOOL_NASM_KSUBMIT
-MD "$(dep)" -MP\
$(abspath $(source))
else
- $(QUIET)$(REDIRECT) -C $(PATH_OUT_BASE) $(TOOL_NASM_AS)\
+ $(QUIET)$(REDIRECT) -C $(PATH_OUT_BASE) -- $(TOOL_NASM_AS)\
$(flags) $(addsuffix /,$(addprefix -i, $(incs))) $(addprefix -D, $(defs))\
-l $(outbase).lst\
-o $(obj)\
diff --git a/kBuild/tools/VCC100.kmk b/kBuild/tools/VCC100.kmk
index e47e887..34ed55a 100644
--- a/kBuild/tools/VCC100.kmk
+++ b/kBuild/tools/VCC100.kmk
@@ -1,4 +1,4 @@
-# $Id: VCC100.kmk 2795 2015-09-15 23:35:37Z bird $
+# $Id: VCC100.kmk 2964 2016-09-23 11:08:04Z bird $
## @file
# kBuild Tool Config - Visual C++ 10.0 (aka Visual 2010 and MSC v16), targeting $(KBUILD_TARGET).
#
@@ -107,19 +107,21 @@ TOOL_VCC100_FN_FIND_SDK_TOOL = $(if-expr !defined($3),$(TOOL_VCC100_FN_FIND_SDK_
# @param $(2) The extension.
TOOL_VCC100_PDB = $(dir $(1))$(tolower $(notdir $(1))).$(2)
+
+# General Properties used by kBuild
TOOL_VCC100_COBJSUFF ?= .obj
-TOOL_VCC100_CFLAGS ?= -TC -nologo
-TOOL_VCC100_CFLAGS.debug ?= -Zi
-TOOL_VCC100_CFLAGS.dbgopt ?= -O2 -Zi
+TOOL_VCC100_CFLAGS ?= -TC -nologo -Zi
+TOOL_VCC100_CFLAGS.debug ?=
+TOOL_VCC100_CFLAGS.dbgopt ?= -O2
TOOL_VCC100_CFLAGS.release ?= -O2
TOOL_VCC100_CFLAGS.profile ?= -O2
TOOL_VCC100_CINCS ?= $(PATH_TOOL_VCC100_INC)
TOOL_VCC100_CDEFS ?=
TOOL_VCC100_CXXOBJSUFF ?= .obj
-TOOL_VCC100_CXXFLAGS ?= -TP -nologo
-TOOL_VCC100_CXXFLAGS.debug ?= -Zi
-TOOL_VCC100_CXXFLAGS.dbgopt ?= -O2 -Zi
+TOOL_VCC100_CXXFLAGS ?= -TP -nologo -Zi
+TOOL_VCC100_CXXFLAGS.debug ?=
+TOOL_VCC100_CXXFLAGS.dbgopt ?= -O2
TOOL_VCC100_CXXFLAGS.release ?= -O2
TOOL_VCC100_CXXFLAGS.profile ?= -O2
TOOL_VCC100_CXXINCS ?= $(PATH_TOOL_VCC100_INC) $(PATH_TOOL_VCC100_ATLMFC_INC)
@@ -162,37 +164,16 @@ TOOL_VCC100_LIBPATH.x86 ?= $(PATH_TOOL_VCC100_LIB.x86) $(PATH_TOOL_VCC100_A
# @param $(objsuff) Object suffix.
TOOL_VCC100_COMPILE_C_DEPEND =
TOOL_VCC100_COMPILE_C_DEPORD =
-ifdef KBUILD_USE_KOBJCACHE
-TOOL_VCC100_COMPILE_C_USES_KOBJCACHE = 1
-TOOL_VCC100_COMPILE_C_OUTPUT = $(outbase).i
-TOOL_VCC100_COMPILE_C_OUTPUT_MAYBE =
-define TOOL_VCC100_COMPILE_C_CMDS
- $(QUIET)$(KOBJCACHE) -f $(outbase).koc -d $(PATH_OBJCACHE) -t $(bld_trg).$(bld_trg_arch) -O2 -r\
- --make-dep-fix-case --make-dep-gen-stubs --make-dep-quiet --make-dep-file $(dep)\
- --kObjCache-cpp $(outbase).i\
- $(TOOL_VCC100_CC) -E\
- $(subst -Zi,-Z7,$(flags))\
- $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- $(subst /,\\,$(abspath $(source))) \
- --kObjCache-cc $(obj)\
- $(TOOL_VCC100_CC) -c\
- $(subst -Zi,-Z7,$(flags))\
- -Fo$(obj)\
- $(outbase).i
-endef
-else # !KBUILD_USE_KOBJCACHE
-TOOL_VCC100_COMPILE_C_OUTPUT = $(call TOOL_VCC100_PDB, $(outbase)-obj,idb)
-TOOL_VCC100_COMPILE_C_OUTPUT_MAYBE = $(call TOOL_VCC100_PDB, $(outbase)-obj,pdb)
+TOOL_VCC100_COMPILE_C_OUTPUT =
+TOOL_VCC100_COMPILE_C_OUTPUT_MAYBE = $(call TOOL_VCC100_PDB, $(outbase)-obj,pdb) $(call TOOL_VCC100_PDB, $(outbase)-obj,idb)
define TOOL_VCC100_COMPILE_C_CMDS
$(QUIET)$(TOOL_VCC100_CC) -c\
$(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
-Fd$(outbase)-obj.pdb \
- -FD\
-Fo$(obj)\
$(subst /,\\,$(abspath $(source)))
- $(QUIET)$(DEP_IDB) -f -s -q -o $(dep) -t $(obj) $(call TOOL_VCC100_PDB,$(outbase)-obj,idb)
+ $(QUIET)$(DEP_OBJ) -f -s -q -o $(dep) -t $(obj) $(obj)
endef
-endif # !KBUILD_USE_KOBJCACHE
## Compile C++ source.
@@ -208,39 +189,66 @@ endif # !KBUILD_USE_KOBJCACHE
#
# @param $(outbase) Output basename (full). Use this for list files and such.
# @param $(objsuff) Object suffix.
-TOOL_VCC100_COMPILE_CXX_DEPEND =
+TOOL_VCC100_COMPILE_CXX_DEPEND = $($(target)_1_VCC_PCH_FILE)
TOOL_VCC100_COMPILE_CXX_DEPORD =
-ifdef KBUILD_USE_KOBJCACHE
-TOOL_VCC100_COMPILE_CXX_USES_KOBJCACHE = 1
-TOOL_VCC100_COMPILE_CXX_OUTPUT = $(outbase).ii
-TOOL_VCC100_COMPILE_CXX_OUTPUT_MAYBE =
-define TOOL_VCC100_COMPILE_CXX_CMDS
- $(QUIET)$(KOBJCACHE) -f $(outbase).koc -d $(PATH_OBJCACHE) -t $(bld_trg).$(bld_trg_arch) -O2 -r\
- --make-dep-fix-case --make-dep-gen-stubs --make-dep-quiet --make-dep-file $(dep)\
- --kObjCache-cpp $(outbase).ii\
- $(TOOL_VCC100_CXX) -E\
- $(subst -Zi,-Z7,$(flags))\
- $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- $(subst /,\\,$(abspath $(source))) \
- --kObjCache-cc $(obj)\
- $(TOOL_VCC100_CXX) -c\
- $(subst -Zi,-Z7,$(flags))\
- -Fo$(obj)\
- $(outbase).ii
-endef
-else # !KBUILD_USE_KOBJCACHE
-TOOL_VCC100_COMPILE_CXX_OUTPUT = $(call TOOL_VCC100_PDB, $(outbase)-obj,idb)
-TOOL_VCC100_COMPILE_CXX_OUTPUT_MAYBE = $(call TOOL_VCC100_PDB, $(outbase)-obj,pdb)
+TOOL_VCC100_COMPILE_CXX_OUTPUT =
+TOOL_VCC100_COMPILE_CXX_OUTPUT_MAYBE = $(if-expr defined($(target)_1_VCC_COMMON_OBJ_PDB)\
+ ,,$(call TOOL_VCC100_PDB, $(outbase)-obj,pdb) $(call TOOL_VCC100_PDB, $(outbase)-obj,idb))
define TOOL_VCC100_COMPILE_CXX_CMDS
$(QUIET)$(TOOL_VCC100_CXX) -c\
$(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- -Fd$(outbase)-obj.pdb \
- -FD\
+ $(if-expr defined($(target)_PCH_HDR)\
+ ,-FI$($(target)_PCH_HDR) -Yu$($(target)_PCH_HDR) -Fp$($(target)_1_VCC_PCH_FILE),)\
+ -Fd$(if-expr defined($(target)_1_VCC_COMMON_OBJ_PDB),$($(target)_1_VCC_COMMON_OBJ_PDB),$(outbase)-obj.pdb) \
-Fo$(obj)\
$(subst /,\\,$(abspath $(source)))
- $(QUIET)$(DEP_IDB) -f -s -q -o $(dep) -t $(obj) $(call TOOL_VCC100_PDB,$(outbase)-obj,idb)
+ $(QUIET)$(DEP_OBJ) -f -s -q -o $(dep) -t $(obj) $(obj)
endef
-endif # !KBUILD_USE_KOBJCACHE
+
+
+#
+# Helper tool for creating the precompiled C++ header.
+#
+# It only have the C++ compile bits and it's purpose is to skip bits
+# related _1_VCC_PCH_FILE and add -Yc.
+#
+TOOL_VCC100-PCH := Helper for creating precompiled header using CXX handling.
+TOOL_VCC100-PCH_EXTENDS := VCC100
+TOOL_VCC100-PCH_CXXOBJSUFF := .obj
+TOOL_VCC100-PCH_CXXINCS = $(TOOL_VCC100_CXXINCS)
+TOOL_VCC100-PCH_CXXFLAGS.debug = $(TOOL_VCC100_CXXFLAGS.debug)
+TOOL_VCC100-PCH_CXXFLAGS.dbgopt = $(TOOL_VCC100_CXXFLAGS.dbgopt)
+TOOL_VCC100-PCH_CXXFLAGS.release = $(TOOL_VCC100_CXXFLAGS.release)
+TOOL_VCC100-PCH_CXXFLAGS.profile = $(TOOL_VCC100_CXXFLAGS.profile)
+TOOL_VCC100-PCH_COMPILE_CXX_DEPEND = $(NO_SUCH_VARIABLE)
+TOOL_VCC100-PCH_COMPILE_CXX_DEPORD = $(NO_SUCH_VARIABLE)
+TOOL_VCC100-PCH_COMPILE_CXX_OUTPUT = $($(target)_1_VCC_PCH_FILE) $($(target)_1_VCC_COMMON_OBJ_PDB)
+TOOL_VCC100-PCH_COMPILE_CXX_OUTPUT_MAYBE = $(NO_SUCH_VARIABLE)
+ifdef TOOL_VCC100_KSUBMIT
+ define TOOL_VCC100-PCH_COMPILE_CXX_CMDS
+ $(QUIET)$(TOOL_VCC100_KSUBMIT) --no-pch-caching -P $(DEP_OBJ_INT) -f -s -q -e .pch -o $(dep) -t $(obj) $(obj)\
+ -- $(TOOL_VCC100_CXX) -c -Yc\
+ $(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
+ -Fp$($(target)_1_VCC_PCH_FILE) \
+ -Fd$($(target)_1_VCC_COMMON_OBJ_PDB) \
+ -Fo$(obj)\
+ -TP \
+ $(subst /,\\,$(abspath $(source)))
+ endef
+else
+ define TOOL_VCC100-PCH_COMPILE_CXX_CMDS
+ $(QUIET)$(TOOL_VCC100_CXX) -c -Yc\
+ $(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
+ -Fp$($(target)_1_VCC_PCH_FILE) \
+ -Fd$($(target)_1_VCC_COMMON_OBJ_PDB) \
+ -Fo$(obj)\
+ -TP \
+ $(subst /,\\,$(abspath $(source)))
+ $(QUIET)$(DEP_OBJ) -f -s -q -e .pch -o $(dep) -t $(obj) $(obj)
+
+ endef
+endif # !TOOL_VCC100_KSUBMIT
+
## @todo configure the assembler template.
@@ -293,8 +301,6 @@ define TOOL_VCC100_LINK_LIBRARY_CMDS
endef
-
-
## Link program
# @param $(target) Normalized main target name.
# @param $(out) Program name.
diff --git a/kBuild/tools/VCC100AMD64.kmk b/kBuild/tools/VCC100AMD64.kmk
index a4740db..316882f 100644
--- a/kBuild/tools/VCC100AMD64.kmk
+++ b/kBuild/tools/VCC100AMD64.kmk
@@ -1,4 +1,4 @@
-# $Id: VCC100AMD64.kmk 2902 2016-09-09 17:15:22Z bird $
+# $Id: VCC100AMD64.kmk 2964 2016-09-23 11:08:04Z bird $
## @file
# kBuild Tool Config - Visual C++ 10.0 (aka Visual 2010 and MSC v16), targeting AMD64.
#
@@ -79,10 +79,12 @@ ifdef TOOL_VCC100AMD64_USE_KSUBMIT
ifeq ($(KBUILD_HOST),win)
ifneq ($(substr $(PATH_TOOL_VCC100AMD64_BIN),-9),x86_amd64)
TOOL_VCC100AMD64_KSUBMIT ?= kmk_builtin_kSubmit --64-bit
+ TOOL_VCC100AMD64_KSUBMIT_DD = $(TOOL_VCC100AMD64_KSUBMIT) --
else
- TOOL_VCC100AMD64_KSUBMIT ?= kmk_builtin_kSubmit --32-bit
+ # "fatal error C1902: Program database manager mismatch; please check your installation" when mixing with the 32-bit compiler.
+ #TOOL_VCC100AMD64_KSUBMIT ?= kmk_builtin_kSubmit --32-bit
+ #TOOL_VCC100AMD64_KSUBMIT_DD = $(TOOL_VCC100AMD64_KSUBMIT) --
endif
- TOOL_VCC100AMD64_KSUBMIT_DD = $(TOOL_VCC100AMD64_KSUBMIT) --
endif
endif
@@ -103,19 +105,21 @@ TOOL_VCC100_FN_FIND_SDK_TOOL = $(if-expr !defined($3),$(TOOL_VCC100_FN_FIND_SDK_
# @param $(2) The extension.
TOOL_VCC100AMD64_PDB = $(dir $(1))$(tolower $(notdir $(1))).$(2)
+
+# General Properties used by kBuild
TOOL_VCC100AMD64_COBJSUFF ?= .obj
-TOOL_VCC100AMD64_CFLAGS ?= -TC -nologo
-TOOL_VCC100AMD64_CFLAGS.debug ?= -Zi
-TOOL_VCC100AMD64_CFLAGS.dbgopt ?= -O2 -Zi
+TOOL_VCC100AMD64_CFLAGS ?= -TC -nologo -Zi
+TOOL_VCC100AMD64_CFLAGS.debug ?=
+TOOL_VCC100AMD64_CFLAGS.dbgopt ?= -O2
TOOL_VCC100AMD64_CFLAGS.release ?= -O2
TOOL_VCC100AMD64_CFLAGS.profile ?= -O2
TOOL_VCC100AMD64_CINCS ?= $(PATH_TOOL_VCC100AMD64_INC)
TOOL_VCC100AMD64_CDEFS ?=
TOOL_VCC100AMD64_CXXOBJSUFF ?= .obj
-TOOL_VCC100AMD64_CXXFLAGS ?= -TP -nologo
-TOOL_VCC100AMD64_CXXFLAGS.debug ?= -Zi
-TOOL_VCC100AMD64_CXXFLAGS.dbgopt ?= -O2 -Zi
+TOOL_VCC100AMD64_CXXFLAGS ?= -TP -nologo -Zi
+TOOL_VCC100AMD64_CXXFLAGS.debug ?=
+TOOL_VCC100AMD64_CXXFLAGS.dbgopt ?= -O2
TOOL_VCC100AMD64_CXXFLAGS.release ?= -O2
TOOL_VCC100AMD64_CXXFLAGS.profile ?= -O2
TOOL_VCC100AMD64_CXXINCS ?= $(PATH_TOOL_VCC100AMD64_INC) $(PATH_TOOL_VCC100AMD64_ATLMFC_INC)
@@ -152,49 +156,28 @@ TOOL_VCC100AMD64_LDFLAGS.release ?=
# @param $(objsuff) Object suffix.
TOOL_VCC100AMD64_COMPILE_C_DEPEND =
TOOL_VCC100AMD64_COMPILE_C_DEPORD =
-ifdef KBUILD_USE_KOBJCACHE
-TOOL_VCC100AMD64_COMPILE_C_USES_KOBJCACHE = 1
-TOOL_VCC100AMD64_COMPILE_C_OUTPUT = $(outbase).i
-TOOL_VCC100AMD64_COMPILE_C_OUTPUT_MAYBE =
-define TOOL_VCC100AMD64_COMPILE_C_CMDS
- $(QUIET)$(KOBJCACHE) -f $(outbase).koc -d $(PATH_OBJCACHE) -t $(bld_trg).$(bld_trg_arch) -O2 -r\
- --make-dep-fix-case --make-dep-gen-stubs --make-dep-quiet --make-dep-file $(dep)\
- --kObjCache-cpp $(outbase).i\
- $(TOOL_VCC100AMD64_CC) -E\
- $(subst -Zi,-Z7,$(flags))\
- $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- $(subst /,\\,$(abspath $(source))) \
- --kObjCache-cc $(obj)\
- $(TOOL_VCC100AMD64_CC) -c\
- $(subst -Zi,-Z7,$(flags))\
- -Fo$(obj)\
- $(outbase).i
-endef
-else # !KBUILD_USE_KOBJCACHE
-TOOL_VCC100AMD64_COMPILE_C_OUTPUT = $(call TOOL_VCC100AMD64_PDB, $(outbase)-obj,idb)
-TOOL_VCC100AMD64_COMPILE_C_OUTPUT_MAYBE = $(call TOOL_VCC100AMD64_PDB, $(outbase)-obj,pdb)
- ifdef TOOL_VCC100AMD64_KSUBMIT
-define TOOL_VCC100AMD64_COMPILE_C_CMDS
+TOOL_VCC100AMD64_COMPILE_C_OUTPUT =
+TOOL_VCC100AMD64_COMPILE_C_OUTPUT_MAYBE = $(call TOOL_VCC100AMD64_PDB, $(outbase)-obj,pdb) $(call TOOL_VCC100AMD64_PDB, $(outbase)-obj,idb)
+ifdef TOOL_VCC100AMD64_KSUBMIT
+ TOOL_VCC100AMD64_COMPILE_C_DONT_PURGE_OUTPUT := 1 # speed
+ define TOOL_VCC100AMD64_COMPILE_C_CMDS
$(QUIET)$(TOOL_VCC100AMD64_KSUBMIT) -P $(DEP_OBJ_INT) -f -s -q -o $(dep) -t $(obj) $(obj)\
-- $(TOOL_VCC100AMD64_CC) -c\
$(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
-Fd$(outbase)-obj.pdb \
- -FD\
-Fo$(obj)\
$(subst /,\\,$(abspath $(source)))
-endef
- else
-define TOOL_VCC100AMD64_COMPILE_C_CMDS
+ endef
+else
+ define TOOL_VCC100AMD64_COMPILE_C_CMDS
$(QUIET)$(TOOL_VCC100AMD64_CC) -c\
$(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
-Fd$(outbase)-obj.pdb \
- -FD\
-Fo$(obj)\
$(subst /,\\,$(abspath $(source)))
- $(QUIET)$(DEP_IDB) -f -s -q -o $(dep) -t $(obj) $(call TOOL_VCC100AMD64_PDB,$(outbase)-obj,idb)
-endef
- endif # !TOOL_VCC100AMD64_KSUBMIT
-endif # !KBUILD_USE_KOBJCACHE
+ $(QUIET)$(DEP_OBJ) -f -s -q -o $(dep) -t $(obj) $(obj)
+ endef
+endif # !TOOL_VCC100AMD64_KSUBMIT
## Compile C++ source.
@@ -210,51 +193,82 @@ endif # !KBUILD_USE_KOBJCACHE
#
# @param $(outbase) Output basename (full). Use this for list files and such.
# @param $(objsuff) Object suffix.
-TOOL_VCC100AMD64_COMPILE_CXX_DEPEND =
+TOOL_VCC100AMD64_COMPILE_CXX_DEPEND = $($(target)_1_VCC_PCH_FILE)
TOOL_VCC100AMD64_COMPILE_CXX_DEPORD =
-ifdef KBUILD_USE_KOBJCACHE
-TOOL_VCC100AMD64_COMPILE_CXX_USES_KOBJCACHE = 1
-TOOL_VCC100AMD64_COMPILE_CXX_OUTPUT = $(outbase).ii
-TOOL_VCC100AMD64_COMPILE_CXX_OUTPUT_MAYBE =
-define TOOL_VCC100AMD64_COMPILE_CXX_CMDS
- $(QUIET)$(KOBJCACHE) -f $(outbase).koc -d $(PATH_OBJCACHE) -t $(bld_trg).$(bld_trg_arch) -O2 -r\
- --make-dep-fix-case --make-dep-gen-stubs --make-dep-quiet --make-dep-file $(dep)\
- --kObjCache-cpp $(outbase).ii\
- $(TOOL_VCC100AMD64_CXX) -E\
- $(subst -Zi,-Z7,$(flags))\
- $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- $(subst /,\\,$(abspath $(source))) \
- --kObjCache-cc $(obj)\
- $(TOOL_VCC100AMD64_CXX) -c\
- $(subst -Zi,-Z7,$(flags))\
- -Fo$(obj)\
- $(outbase).ii
-endef
-else # !KBUILD_USE_KOBJCACHE
-TOOL_VCC100AMD64_COMPILE_CXX_OUTPUT = $(call TOOL_VCC100AMD64_PDB, $(outbase)-obj,idb)
-TOOL_VCC100AMD64_COMPILE_CXX_OUTPUT_MAYBE = $(call TOOL_VCC100AMD64_PDB, $(outbase)-obj,pdb)
- ifdef TOOL_VCC100AMD64_KSUBMIT
-define TOOL_VCC100AMD64_COMPILE_CXX_CMDS
+TOOL_VCC100AMD64_COMPILE_CXX_OUTPUT =
+TOOL_VCC100AMD64_COMPILE_CXX_OUTPUT_MAYBE = $(if-expr defined($(target)_1_VCC_COMMON_OBJ_PDB)\
+ ,,$(call TOOL_VCC100AMD64_PDB, $(outbase)-obj,pdb) $(call TOOL_VCC100AMD64_PDB, $(outbase)-obj,idb))
+ifdef TOOL_VCC100AMD64_KSUBMIT
+ TOOL_VCC100AMD64_COMPILE_CXX_DONT_PURGE_OUTPUT := 1 # speed
+ define TOOL_VCC100AMD64_COMPILE_CXX_CMDS
$(QUIET)$(TOOL_VCC100AMD64_KSUBMIT) -P $(DEP_OBJ_INT) -f -s -q -o $(dep) -t $(obj) $(obj)\
-- $(TOOL_VCC100AMD64_CXX) -c\
$(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- -Fd$(outbase)-obj.pdb \
- -FD\
+ $(if-expr defined($(target)_PCH_HDR)\
+ ,-FI$($(target)_PCH_HDR) -Yu$($(target)_PCH_HDR) -Fp$($(target)_1_VCC_PCH_FILE),)\
+ -Fd$(if-expr defined($(target)_1_VCC_COMMON_OBJ_PDB),$($(target)_1_VCC_COMMON_OBJ_PDB),$(outbase)-obj.pdb) \
-Fo$(obj)\
$(subst /,\\,$(abspath $(source)))
-endef
- else
-define TOOL_VCC100AMD64_COMPILE_CXX_CMDS
+ endef
+else
+ define TOOL_VCC100AMD64_COMPILE_CXX_CMDS
$(QUIET)$(TOOL_VCC100AMD64_CXX) -c\
$(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- -Fd$(outbase)-obj.pdb \
- -FD\
+ $(if-expr defined($(target)_PCH_HDR)\
+ ,-FI$($(target)_PCH_HDR) -Yu$($(target)_PCH_HDR) -Fp$($(target)_1_VCC_PCH_FILE),)\
+ -Fd$(if-expr defined($(target)_1_VCC_COMMON_OBJ_PDB),$($(target)_1_VCC_COMMON_OBJ_PDB),$(outbase)-obj.pdb) \
-Fo$(obj)\
$(subst /,\\,$(abspath $(source)))
- $(QUIET)$(DEP_IDB) -f -s -q -o $(dep) -t $(obj) $(call TOOL_VCC100AMD64_PDB,$(outbase)-obj,idb)
-endef
- endif # !TOOL_VCC100AMD64_KSUBMIT
-endif # !KBUILD_USE_KOBJCACHE
+ $(QUIET)$(DEP_OBJ) -f -s -q -o $(dep) -t $(obj) $(obj)
+ endef
+endif # !TOOL_VCC100AMD64_KSUBMIT
+
+
+#
+# Helper tool for creating the precompiled C++ header.
+#
+# It only have the C++ compile bits and it's purpose is to skip bits
+# related _1_VCC_PCH_FILE and add -Yc.
+#
+TOOL_VCC100AMD64-PCH := Helper for creating precompiled header using CXX handling.
+TOOL_VCC100AMD64-PCH_EXTENDS := VCC100AMD64
+TOOL_VCC100AMD64-PCH_CXXOBJSUFF := .obj
+TOOL_VCC100AMD64-PCH_CXXINCS = $(TOOL_VCC100AMD64_CXXINCS)
+TOOL_VCC100AMD64-PCH_CXXFLAGS.debug = $(TOOL_VCC100AMD64_CXXFLAGS.debug)
+TOOL_VCC100AMD64-PCH_CXXFLAGS.dbgopt = $(TOOL_VCC100AMD64_CXXFLAGS.dbgopt)
+TOOL_VCC100AMD64-PCH_CXXFLAGS.release = $(TOOL_VCC100AMD64_CXXFLAGS.release)
+TOOL_VCC100AMD64-PCH_CXXFLAGS.profile = $(TOOL_VCC100AMD64_CXXFLAGS.profile)
+TOOL_VCC100AMD64-PCH_COMPILE_CXX_DEPEND = $(NO_SUCH_VARIABLE)
+TOOL_VCC100AMD64-PCH_COMPILE_CXX_DEPORD = $(NO_SUCH_VARIABLE)
+TOOL_VCC100AMD64-PCH_COMPILE_CXX_OUTPUT = $($(target)_1_VCC_PCH_FILE) $($(target)_1_VCC_COMMON_OBJ_PDB)
+TOOL_VCC100AMD64-PCH_COMPILE_CXX_OUTPUT_MAYBE = $(NO_SUCH_VARIABLE)
+ifdef TOOL_VCC100AMD64_KSUBMIT
+ define TOOL_VCC100AMD64-PCH_COMPILE_CXX_CMDS
+ $(QUIET)$(RM) -f -- $($(target)_1_VCC_PCH_FILE) $($(target)_1_VCC_COMMON_OBJ_PDB)
+ $(QUIET)$(TOOL_VCC100AMD64_KSUBMIT) --no-pch-caching -P $(DEP_OBJ_INT) -f -s -q -e .pch -o $(dep) -t $(obj) $(obj)\
+ -- $(TOOL_VCC100AMD64_CXX) -c -Yc\
+ $(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
+ -Fp$($(target)_1_VCC_PCH_FILE) \
+ -Fd$($(target)_1_VCC_COMMON_OBJ_PDB) \
+ -Fo$(obj)\
+ -TP \
+ $(subst /,\\,$(abspath $(source)))
+ endef
+else
+ define TOOL_VCC100AMD64-PCH_COMPILE_CXX_CMDS
+ $(QUIET)$(RM) -f -- $($(target)_1_VCC_PCH_FILE) $($(target)_1_VCC_COMMON_OBJ_PDB)
+ $(QUIET)$(TOOL_VCC100AMD64_CXX) -c -Yc\
+ $(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
+ -Fp$($(target)_1_VCC_PCH_FILE) \
+ -Fd$($(target)_1_VCC_COMMON_OBJ_PDB) \
+ -Fo$(obj)\
+ -TP \
+ $(subst /,\\,$(abspath $(source)))
+ $(QUIET)$(DEP_OBJ) -f -s -q -e .pch -o $(dep) -t $(obj) $(obj)
+
+ endef
+endif # !TOOL_VCC100AMD64_KSUBMIT
+
## @todo configure the assembler template.
diff --git a/kBuild/tools/VCC100X86.kmk b/kBuild/tools/VCC100X86.kmk
index 95d6ac8..fb81816 100644
--- a/kBuild/tools/VCC100X86.kmk
+++ b/kBuild/tools/VCC100X86.kmk
@@ -1,4 +1,4 @@
-# $Id: VCC100X86.kmk 2902 2016-09-09 17:15:22Z bird $
+# $Id: VCC100X86.kmk 2964 2016-09-23 11:08:04Z bird $
## @file
# kBuild Tool Config - Visual C++ 10.0 (aka Visual 2010 and MSC v16), targeting x86.
#
@@ -101,18 +101,18 @@ TOOL_VCC100X86_PDB = $(dir $(1))$(tolower $(notdir $(1))).$(2)
# General Properties used by kBuild
TOOL_VCC100X86_COBJSUFF ?= .obj
-TOOL_VCC100X86_CFLAGS ?= -TC -nologo
-TOOL_VCC100X86_CFLAGS.debug ?= -Zi
-TOOL_VCC100X86_CFLAGS.dbgopt ?= -O2 -Zi
+TOOL_VCC100X86_CFLAGS ?= -TC -nologo -Zi
+TOOL_VCC100X86_CFLAGS.debug ?=
+TOOL_VCC100X86_CFLAGS.dbgopt ?= -O2
TOOL_VCC100X86_CFLAGS.release ?= -O2
TOOL_VCC100X86_CFLAGS.profile ?= -O2
TOOL_VCC100X86_CINCS ?= $(PATH_TOOL_VCC100X86_INC)
TOOL_VCC100X86_CDEFS ?=
TOOL_VCC100X86_CXXOBJSUFF ?= .obj
-TOOL_VCC100X86_CXXFLAGS ?= -TP -nologo
-TOOL_VCC100X86_CXXFLAGS.debug ?= -Zi
-TOOL_VCC100X86_CXXFLAGS.dbgopt ?= -O2 -Zi
+TOOL_VCC100X86_CXXFLAGS ?= -TP -nologo -Zi
+TOOL_VCC100X86_CXXFLAGS.debug ?=
+TOOL_VCC100X86_CXXFLAGS.dbgopt ?= -O2
TOOL_VCC100X86_CXXFLAGS.release ?= -O2
TOOL_VCC100X86_CXXFLAGS.profile ?= -O2
TOOL_VCC100X86_CXXINCS ?= $(PATH_TOOL_VCC100X86_INC) $(PATH_TOOL_VCC100X86_ATLMFC_INC)
@@ -149,49 +149,28 @@ TOOL_VCC100X86_LDFLAGS.release ?=
# @param $(objsuff) Object suffix.
TOOL_VCC100X86_COMPILE_C_DEPEND =
TOOL_VCC100X86_COMPILE_C_DEPORD =
-ifdef KBUILD_USE_KOBJCACHE
-TOOL_VCC100X86_COMPILE_C_USES_KOBJCACHE = 1
-TOOL_VCC100X86_COMPILE_C_OUTPUT = $(outbase).i
-TOOL_VCC100X86_COMPILE_C_OUTPUT_MAYBE =
-define TOOL_VCC100X86_COMPILE_C_CMDS
- $(QUIET)$(KOBJCACHE) -f $(outbase).koc -d $(PATH_OBJCACHE) -t $(bld_trg).$(bld_trg_arch) -O2 -r\
- --make-dep-fix-case --make-dep-gen-stubs --make-dep-quiet --make-dep-file $(dep)\
- --kObjCache-cpp $(outbase).i\
- $(TOOL_VCC100X86_CC) -E\
- $(subst -Zi,-Z7,$(flags))\
- $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- $(subst /,\\,$(abspath $(source))) \
- --kObjCache-cc $(obj)\
- $(TOOL_VCC100X86_CC) -c\
- $(subst -Zi,-Z7,$(flags))\
- -Fo$(obj)\
- $(outbase).i
-endef
-else # !KBUILD_USE_KOBJCACHE
-TOOL_VCC100X86_COMPILE_C_OUTPUT = $(call TOOL_VCC100X86_PDB, $(outbase)-obj,idb)
-TOOL_VCC100X86_COMPILE_C_OUTPUT_MAYBE = $(call TOOL_VCC100X86_PDB, $(outbase)-obj,pdb)
- ifdef TOOL_VCC100X86_KSUBMIT
-define TOOL_VCC100X86_COMPILE_C_CMDS
+TOOL_VCC100X86_COMPILE_C_OUTPUT =
+TOOL_VCC100X86_COMPILE_C_OUTPUT_MAYBE = $(call TOOL_VCC100X86_PDB, $(outbase)-obj,pdb) $(call TOOL_VCC100X86_PDB, $(outbase)-obj,idb)
+ifdef TOOL_VCC100X86_KSUBMIT
+ TOOL_VCC100X86_COMPILE_C_DONT_PURGE_OUTPUT = 1 # speed
+ define TOOL_VCC100X86_COMPILE_C_CMDS
$(QUIET)$(TOOL_VCC100X86_KSUBMIT) -P $(DEP_OBJ_INT) -f -s -q -o $(dep) -t $(obj) $(obj)\
-- $(TOOL_VCC100X86_CC) -c\
$(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
-Fd$(outbase)-obj.pdb \
- -FD\
-Fo$(obj)\
$(subst /,\\,$(abspath $(source)))
-endef
- else
-define TOOL_VCC100X86_COMPILE_C_CMDS
+ endef
+else
+ define TOOL_VCC100X86_COMPILE_C_CMDS
$(QUIET)$(TOOL_VCC100X86_CC) -c\
$(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
-Fd$(outbase)-obj.pdb \
- -FD\
-Fo$(obj)\
$(subst /,\\,$(abspath $(source)))
- $(QUIET)$(DEP_IDB) -f -s -q -o $(dep) -t $(obj) $(call TOOL_VCC100X86_PDB,$(outbase)-obj,idb)
-endef
- endif # !TOOL_VCC100X86_KSUBMIT
-endif # !KBUILD_USE_KOBJCACHE
+ $(QUIET)$(DEP_OBJ) -f -s -q -o $(dep) -t $(obj) $(obj)
+ endef
+endif # !TOOL_VCC100X86_KSUBMIT
## Compile C++ source.
@@ -207,51 +186,80 @@ endif # !KBUILD_USE_KOBJCACHE
#
# @param $(outbase) Output basename (full). Use this for list files and such.
# @param $(objsuff) Object suffix.
-TOOL_VCC100X86_COMPILE_CXX_DEPEND =
+TOOL_VCC100X86_COMPILE_CXX_DEPEND = $($(target)_1_VCC_PCH_FILE)
TOOL_VCC100X86_COMPILE_CXX_DEPORD =
-ifdef KBUILD_USE_KOBJCACHE
-TOOL_VCC100X86_COMPILE_CXX_USES_KOBJCACHE = 1
-TOOL_VCC100X86_COMPILE_CXX_OUTPUT = $(outbase).ii
-TOOL_VCC100X86_COMPILE_CXX_OUTPUT_MAYBE =
-define TOOL_VCC100X86_COMPILE_CXX_CMDS
- $(QUIET)$(KOBJCACHE) -f $(outbase).koc -d $(PATH_OBJCACHE) -t $(bld_trg).$(bld_trg_arch) -O2 -r\
- --make-dep-fix-case --make-dep-gen-stubs --make-dep-quiet --make-dep-file $(dep)\
- --kObjCache-cpp $(outbase).ii\
- $(TOOL_VCC100X86_CXX) -E\
- $(subst -Zi,-Z7,$(flags))\
- $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- $(subst /,\\,$(abspath $(source))) \
- --kObjCache-cc $(obj)\
- $(TOOL_VCC100X86_CXX) -c\
- $(subst -Zi,-Z7,$(flags))\
- -Fo$(obj)\
- $(outbase).ii
-endef
-else # !KBUILD_USE_KOBJCACHE
-TOOL_VCC100X86_COMPILE_CXX_OUTPUT = $(call TOOL_VCC100X86_PDB, $(outbase)-obj,idb)
-TOOL_VCC100X86_COMPILE_CXX_OUTPUT_MAYBE = $(call TOOL_VCC100X86_PDB, $(outbase)-obj,pdb)
- ifdef TOOL_VCC100X86_KSUBMIT
-define TOOL_VCC100X86_COMPILE_CXX_CMDS
+TOOL_VCC100X86_COMPILE_CXX_OUTPUT =
+TOOL_VCC100X86_COMPILE_CXX_OUTPUT_MAYBE = $(if-expr defined($(target)_1_VCC_COMMON_OBJ_PDB)\
+ ,,$(call TOOL_VCC100X86_PDB, $(outbase)-obj,pdb) $(call TOOL_VCC100X86_PDB, $(outbase)-obj,idb))
+ifdef TOOL_VCC100X86_KSUBMIT
+ TOOL_VCC100X86_COMPILE_CXX_DONT_PURGE_OUTPUT = 1 # speed
+ define TOOL_VCC100X86_COMPILE_CXX_CMDS
$(QUIET)$(TOOL_VCC100X86_KSUBMIT) -P $(DEP_OBJ_INT) -f -s -q -o $(dep) -t $(obj) $(obj)\
-- $(TOOL_VCC100X86_CXX) -c\
$(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- -Fd$(outbase)-obj.pdb \
- -FD\
+ $(if-expr defined($(target)_PCH_HDR)\
+ ,-FI$($(target)_PCH_HDR) -Yu$($(target)_PCH_HDR) -Fp$($(target)_1_VCC_PCH_FILE),)\
+ -Fd$(if-expr defined($(target)_1_VCC_COMMON_OBJ_PDB),$($(target)_1_VCC_COMMON_OBJ_PDB),$(outbase)-obj.pdb) \
-Fo$(obj)\
$(subst /,\\,$(abspath $(source)))
-endef
- else
-define TOOL_VCC100X86_COMPILE_CXX_CMDS
+ endef
+else
+ define TOOL_VCC100X86_COMPILE_CXX_CMDS
$(QUIET)$(TOOL_VCC100X86_CXX) -c\
$(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
- -Fd$(outbase)-obj.pdb \
- -FD\
+ $(if-expr defined($(target)_PCH_HDR)\
+ ,-FI$($(target)_PCH_HDR) -Yu$($(target)_PCH_HDR) -Fp$($(target)_1_VCC_PCH_FILE),)\
+ -Fd$(if-expr defined($(target)_1_VCC_COMMON_OBJ_PDB),$($(target)_1_VCC_COMMON_OBJ_PDB),$(outbase)-obj.pdb) \
-Fo$(obj)\
$(subst /,\\,$(abspath $(source)))
- $(QUIET)$(DEP_IDB) -f -s -q -o $(dep) -t $(obj) $(call TOOL_VCC100X86_PDB,$(outbase)-obj,idb)
-endef
- endif # !TOOL_VCC100X86_KSUBMIT
-endif # !KBUILD_USE_KOBJCACHE
+ $(QUIET)$(DEP_OBJ) -f -s -q -o $(dep) -t $(obj) $(obj)
+ endef
+endif # !TOOL_VCC100X86_KSUBMIT
+
+
+#
+# Helper tool for creating the precompiled C++ header.
+#
+# It only have the C++ compile bits and it's purpose is to skip bits
+# related _1_VCC_PCH_FILE and add -Yc.
+#
+TOOL_VCC100X86-PCH := Helper for creating precompiled header using CXX handling.
+TOOL_VCC100X86-PCH_EXTENDS := VCC100X86
+TOOL_VCC100X86-PCH_CXXOBJSUFF := .obj
+TOOL_VCC100X86-PCH_CXXINCS = $(TOOL_VCC100X86_CXXINCS)
+TOOL_VCC100X86-PCH_CXXFLAGS.debug = $(TOOL_VCC100X86_CXXFLAGS.debug)
+TOOL_VCC100X86-PCH_CXXFLAGS.dbgopt = $(TOOL_VCC100X86_CXXFLAGS.dbgopt)
+TOOL_VCC100X86-PCH_CXXFLAGS.release = $(TOOL_VCC100X86_CXXFLAGS.release)
+TOOL_VCC100X86-PCH_CXXFLAGS.profile = $(TOOL_VCC100X86_CXXFLAGS.profile)
+TOOL_VCC100X86-PCH_COMPILE_CXX_DEPEND = $(NO_SUCH_VARIABLE)
+TOOL_VCC100X86-PCH_COMPILE_CXX_DEPORD = $(NO_SUCH_VARIABLE)
+TOOL_VCC100X86-PCH_COMPILE_CXX_OUTPUT = $($(target)_1_VCC_PCH_FILE) $($(target)_1_VCC_COMMON_OBJ_PDB)
+TOOL_VCC100X86-PCH_COMPILE_CXX_OUTPUT_MAYBE = $(NO_SUCH_VARIABLE)
+ifdef TOOL_VCC100X86_KSUBMIT
+ define TOOL_VCC100X86-PCH_COMPILE_CXX_CMDS
+ $(QUIET)$(TOOL_VCC100X86_KSUBMIT) --no-pch-caching -P $(DEP_OBJ_INT) -f -s -q -e .pch -o $(dep) -t $(obj) $(obj)\
+ -- $(TOOL_VCC100X86_CXX) -c -Yc\
+ $(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
+ -Fp$($(target)_1_VCC_PCH_FILE) \
+ -Fd$($(target)_1_VCC_COMMON_OBJ_PDB) \
+ -Fo$(obj)\
+ -TP \
+ $(subst /,\\,$(abspath $(source)))
+ endef
+else
+ define TOOL_VCC100X86-PCH_COMPILE_CXX_CMDS
+ $(QUIET)$(TOOL_VCC100X86_CXX) -c -Yc\
+ $(flags) $(addprefix -I, $(incs)) $(addprefix -D, $(defs))\
+ -Fp$($(target)_1_VCC_PCH_FILE) \
+ -Fd$($(target)_1_VCC_COMMON_OBJ_PDB) \
+ -Fo$(obj)\
+ -TP \
+ $(subst /,\\,$(abspath $(source)))
+ $(QUIET)$(DEP_OBJ) -f -s -q -e .pch -o $(dep) -t $(obj) $(obj)
+
+ endef
+endif # !TOOL_VCC100X86_KSUBMIT
+
## @todo configure the assembler template.
diff --git a/kBuild/units/qt4.kmk b/kBuild/units/qt4.kmk
index dde5991..553fb89 100644
--- a/kBuild/units/qt4.kmk
+++ b/kBuild/units/qt4.kmk
@@ -1,4 +1,4 @@
-# $Id: qt4.kmk 2805 2016-01-28 11:08:44Z bird $
+# $Id: qt4.kmk 2979 2016-09-27 14:36:32Z bird $
## @file
# Qt 4 unit.
#
@@ -103,6 +103,7 @@ ifndef PATH_SDK_QT4
endif
ifeq ($(PATH_SDK_QT4),)
PATH_SDK_QT4 := $(patsubst %/bin/rcc,%,$(firstword $(wildcard \
+ /usr/lib/*/qt4/bin/rcc \
/usr/bin/rcc \
/usr/local/bin/rcc \
/usr/qt/4/bin/rcc \
@@ -115,6 +116,7 @@ ifndef PATH_SDK_QT4
# Locate the include files.
ifeq ($(PATH_SDK_QT4_INC),)
PATH_SDK_QT4_INC := $(patsubst %/QtCore/qglobal.h,%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET_DOT_ARCH)),/usr/include/$(type)/qt4/QtCore/qglobal.h) \
$(PATH_SDK_QT4)/include/QtCore/qglobal.h \
$(PATH_SDK_QT4)/include/qt4/QtCore/qglobal.h \
/usr/include/qt4/QtCore/qtglobal.h \
@@ -132,10 +134,10 @@ ifndef PATH_SDK_QT4
$(PATH_SDK_QT4)/lib32/qt4/libQtCore$(SUFF_DLL) \
/usr/lib32/libQtCore$(SUFF_DLL) \
/usr/lib32/qt4/libQtCore$(SUFF_DLL) \
- /usr/lib/i386-linux-gnu/libQtCore$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET).x86),/usr/lib/$(type)/libQtCore$(SUFF_DLL)) \
/usr/local/lib32/libQtCore$(SUFF_DLL) \
/usr/local/lib32/qt4/libQtCore$(SUFF_DLL) \
- /usr/local/lib/i386-linux-gnu/libQtCore$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET).x86),/usr/local/lib/$(type)/libQtCore$(SUFF_DLL)) \
$(PATH_SDK_QT4)/lib/libQtCore$(SUFF_DLL) \
$(PATH_SDK_QT4)/lib/qt4/libQtCore$(SUFF_DLL) \
$(PATH_SDK_QT4)/lib/i386-linux-gnu/libQtCore$(SUFF_DLL) \
@@ -152,11 +154,11 @@ ifndef PATH_SDK_QT4
/usr/lib64/libQtCore$(SUFF_DLL) \
/usr/lib64/qt4/libQtCore$(SUFF_DLL) \
/usr/lib/amd64/libQtCore$(SUFF_DLL) \
- /usr/lib/x86_64-linux-gnu/libQtCore$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET).amd64),/usr/lib/$(type)/libQtCore$(SUFF_DLL)) \
/usr/local/lib64/libQtCore$(SUFF_DLL) \
/usr/local/lib64/qt4/libQtCore$(SUFF_DLL) \
/usr/local/lib/amd64/libQtCore$(SUFF_DLL) \
- /usr/local/lib/x86_64-linux-gnu/libQtCore$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET).amd64),/usr/local/lib/$(type)/libQtCore$(SUFF_DLL)) \
$(PATH_SDK_QT4)/lib/libQtCore$(SUFF_DLL) \
$(PATH_SDK_QT4)/lib/qt4/libQtCore$(SUFF_DLL) \
$(PATH_SDK_QT4)/lib/x86_64-linux-gnu/libQtCore$(SUFF_DLL) \
@@ -175,8 +177,10 @@ ifndef PATH_SDK_QT4
$(PATH_SDK_QT4)/lib/qt4/libQtCore$(SUFF_DLL) \
/usr/lib/libQtCore$(SUFF_DLL) \
/usr/lib/qt4/libQtCore$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET_DOT_ARCH)),/usr/lib/$(type)/libQtCore$(SUFF_DLL)) \
/usr/local/lib/libQtCore$(SUFF_DLL) \
/usr/local/lib/qt4/libQtCore$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET_DOT_ARCH)),/usr/local/lib/$(type)/libQtCore$(SUFF_DLL)) \
)))
endif
ifneq ($(PATH_SDK_QT4_LIB),)
@@ -241,9 +245,10 @@ ifndef PATH_TOOL_QT4_BIN
ifdef TOOL_QT4_BIN_SUFF
TOOL_QT4_BIN_SUFF := $(TOOL_QT4_BIN_SUFF)
endif
- # Try looking for moc-qt4 / moc-$(suffix) first.
+ # Try looking for moc-$(suffix) first, if specified.
ifneq ($(TOOL_QT4_BIN_SUFF),)
PATH_TOOL_QT4_BIN := $(patsubst %/moc$(TOOL_QT4_BIN_SUFF),%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt4/bin/moc$(TOOL_QT4_BIN_SUFF)) \
/usr/lib/qt4/bin/moc$(TOOL_QT4_BIN_SUFF) \
/usr/qt/4/bin/moc$(TOOL_QT4_BIN_SUFF) \
/usr/share/qt4/bin/moc$(TOOL_QT4_BIN_SUFF) \
@@ -251,27 +256,35 @@ ifndef PATH_TOOL_QT4_BIN
/usr/bin/moc$(TOOL_QT4_BIN_SUFF) \
)))
else
- PATH_TOOL_QT4_BIN := $(patsubst %/moc-qt4,%,$(firstword $(wildcard \
+ # No suffix given, so before we check out -qt4 look at qt4 specific locations to avoid choosers and symlinks.
+ PATH_TOOL_QT4_BIN := $(patsubst %/moc,%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt4/bin/moc) \
+ $(if $(intersects $(KBUILD_HOST_ARCH), $(KBUILD_ARCHES_64)),/usr/lib64/qt4/bin/moc,) \
+ /usr/lib/qt4/bin/moc \
+ /usr/local/lib/qt4/bin/moc \
+ /usr/qt/4/bin/moc \
+ /usr/local/qt/4/bin/moc \
+ /usr/share/qt4/bin/moc \
+ /usr/local/share/qt4/bin/moc \
+ )))
+ ifeq ($(PATH_TOOL_QT4_BIN),)
+ PATH_TOOL_QT4_BIN := $(patsubst %/moc-qt4,%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt4/bin/moc-qt4) \
/usr/lib/qt4/bin/moc-qt4 \
/usr/qt/4/bin/moc-qt4 \
/usr/share/qt4/bin/moc-qt4 \
/usr/local/bin/moc-qt4 \
/usr/bin/moc-qt4 \
)))
- ifneq ($(PATH_TOOL_QT4_BIN),)
- TOOL_QT4_BIN_SUFF := -qt4
- else
- # If no luck, try looking for moc in the qt4 specific locations.
- PATH_TOOL_QT4_BIN := $(patsubst %/moc,%,$(firstword $(wildcard \
- /usr/lib/qt4/bin/moc \
- /usr/qt/4/bin/moc \
- /usr/share/qt4/bin/moc \
- )))
+ ifneq ($(PATH_TOOL_QT4_BIN),)
+ TOOL_QT4_BIN_SUFF := -qt4
+ endif
endif
endif
# If still no go, try looking for qt3to4 and rcc.
ifeq ($(PATH_TOOL_QT4_BIN),)
PATH_TOOL_QT4_BIN := $(patsubst %/qt3to4,%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt4/bin/qt3to4) \
/usr/lib/qt4/bin/qt3to4 \
/usr/qt/4/bin/qt3to4 \
/usr/share/qt4/bin/qt3to4 \
@@ -281,6 +294,7 @@ ifndef PATH_TOOL_QT4_BIN
endif
ifeq ($(PATH_TOOL_QT4_BIN),)
PATH_TOOL_QT4_BIN := $(patsubst %/rcc$(TOOL_QT4_BIN_SUFF),%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt4/bin/rcc$(TOOL_QT4_BIN_SUFF)) \
/usr/lib/qt4/bin/rcc$(TOOL_QT4_BIN_SUFF) \
/usr/qt/4/bin/rcc$(TOOL_QT4_BIN_SUFF) \
/usr/share/qt4/bin/rcc$(TOOL_QT4_BIN_SUFF) \
@@ -290,6 +304,7 @@ ifndef PATH_TOOL_QT4_BIN
endif
if "$(PATH_TOOL_QT4_BIN)" == "" && "$(TOOL_QT4_BIN_SUFF)" != ""
PATH_TOOL_QT4_BIN := $(patsubst %/rcc,%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt4/bin/rcc) \
/usr/lib/qt4/bin/rcc \
/usr/qt/4/bin/rcc \
/usr/share/qt4/bin/rcc \
@@ -321,7 +336,7 @@ else
# Pathless, relies on the environment.
TOOL_QT4_MOC ?= moc$(TOOL_QT4_BIN_SUFF)$(HOST_SUFF_EXE)
TOOL_QT4_UIC ?= uic$(TOOL_QT4_BIN_SUFF)$(HOST_SUFF_EXE)
- TOOL_QT4_RCC ?= rcc$(HOST_SUFF_EXE)
+ TOOL_QT4_RCC ?= rcc$(TOOL_QT4_BIN_SUFF)$(HOST_SUFF_EXE)
TOOL_QT4_LRC ?= lrelease$(TOOL_QT4_BIN_SUFF)$(HOST_SUFF_EXE)
TOOL_QT4_LUPDATE ?= lupdate$(TOOL_QT4_BIN_SUFF)$(HOST_SUFF_EXE)
endif
diff --git a/kBuild/units/qt5.kmk b/kBuild/units/qt5.kmk
index 5334910..67e5baf 100644
--- a/kBuild/units/qt5.kmk
+++ b/kBuild/units/qt5.kmk
@@ -1,4 +1,4 @@
-# $Id: qt5.kmk 2807 2016-01-28 13:21:41Z bird $
+# $Id: qt5.kmk 2980 2016-09-27 14:40:53Z bird $
## @file
# Qt 5 unit.
#
@@ -103,6 +103,7 @@ ifndef PATH_SDK_QT5
endif
ifeq ($(PATH_SDK_QT5),)
PATH_SDK_QT5 := $(patsubst %/bin/rcc,%,$(firstword $(wildcard \
+ /usr/lib/*/qt5/bin/rcc \
/usr/bin/rcc \
/usr/local/bin/rcc \
/usr/qt/5/bin/rcc \
@@ -115,10 +116,11 @@ ifndef PATH_SDK_QT5
# Locate the include files.
ifeq ($(PATH_SDK_QT5_INC),)
PATH_SDK_QT5_INC := $(patsubst %/QtCore/qglobal.h,%,$(firstword $(wildcard \
- $(PATH_SDK_QT5)/include/QtCore/qglobal.h \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET_DOT_ARCH)),/usr/include/$(type)/qt5/QtCore/qglobal.h) \
$(PATH_SDK_QT5)/include/qt5/QtCore/qglobal.h \
/usr/include/qt5/QtCore/qtglobal.h \
/usr/local/include/qt5/QtCore/qtglobal.h \
+ $(PATH_SDK_QT5)/include/QtCore/qglobal.h \
)))
ifneq ($(PATH_SDK_QT5_INC),)
export PATH_SDK_QT5_INC
@@ -132,10 +134,10 @@ ifndef PATH_SDK_QT5
$(PATH_SDK_QT5)/lib32/qt5/libQt5Core$(SUFF_DLL) \
/usr/lib32/libQt5Core$(SUFF_DLL) \
/usr/lib32/qt5/libQt5Core$(SUFF_DLL) \
- /usr/lib/i386-linux-gnu/libQt5Core$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET).x86),/usr/lib/$(type)/libQt5Core$(SUFF_DLL)) \
/usr/local/lib32/libQt5Core$(SUFF_DLL) \
/usr/local/lib32/qt5/libQt5Core$(SUFF_DLL) \
- /usr/local/lib/i386-linux-gnu/libQt5Core$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET).x86),/usr/local/lib/$(type)/libQt5Core$(SUFF_DLL)) \
$(PATH_SDK_QT5)/lib/libQt5Core$(SUFF_DLL) \
$(PATH_SDK_QT5)/lib/qt5/libQt5Core$(SUFF_DLL) \
$(PATH_SDK_QT5)/lib/i386-linux-gnu/libQt5Core$(SUFF_DLL) \
@@ -152,11 +154,11 @@ ifndef PATH_SDK_QT5
/usr/lib64/libQt5Core$(SUFF_DLL) \
/usr/lib64/qt5/libQt5Core$(SUFF_DLL) \
/usr/lib/amd64/libQt5Core$(SUFF_DLL) \
- /usr/lib/x86_64-linux-gnu/libQt5Core$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET).amd64),/usr/lib/$(type)/libQt5Core$(SUFF_DLL)) \
/usr/local/lib64/libQt5Core$(SUFF_DLL) \
/usr/local/lib64/qt5/libQt5Core$(SUFF_DLL) \
/usr/local/lib/amd64/libQt5Core$(SUFF_DLL) \
- /usr/local/lib/x86_64-linux-gnu/libQt5Core$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET).amd64),/usr/local/lib/$(type)/libQt5Core$(SUFF_DLL)) \
$(PATH_SDK_QT5)/lib/libQt5Core$(SUFF_DLL) \
$(PATH_SDK_QT5)/lib/qt5/libQt5Core$(SUFF_DLL) \
$(PATH_SDK_QT5)/lib/x86_64-linux-gnu/libQt5Core$(SUFF_DLL) \
@@ -175,8 +177,10 @@ ifndef PATH_SDK_QT5
$(PATH_SDK_QT5)/lib/qt5/libQt5Core$(SUFF_DLL) \
/usr/lib/libQt5Core$(SUFF_DLL) \
/usr/lib/qt5/libQt5Core$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET_DOT_ARCH)),/usr/lib/$(type)/libQt5Core$(SUFF_DLL)) \
/usr/local/lib/libQt5Core$(SUFF_DLL) \
/usr/local/lib/qt5/libQt5Core$(SUFF_DLL) \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET_DOT_ARCH)),/usr/local/lib/$(type)/libQt5Core$(SUFF_DLL)) \
)))
endif
ifneq ($(PATH_SDK_QT5_LIB),)
@@ -244,6 +248,7 @@ ifndef PATH_TOOL_QT5_BIN
# Try looking for moc-qt5 / moc-$(suffix) first.
ifneq ($(TOOL_QT5_BIN_SUFF),)
PATH_TOOL_QT5_BIN := $(patsubst %/moc$(TOOL_QT5_BIN_SUFF),%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt5/bin/moc$(TOOL_QT5_BIN_SUFF)) \
/usr/lib/qt5/bin/moc$(TOOL_QT5_BIN_SUFF) \
/usr/qt/5/bin/moc$(TOOL_QT5_BIN_SUFF) \
/usr/share/qt5/bin/moc$(TOOL_QT5_BIN_SUFF) \
@@ -251,27 +256,35 @@ ifndef PATH_TOOL_QT5_BIN
/usr/bin/moc$(TOOL_QT5_BIN_SUFF) \
)))
else
- PATH_TOOL_QT5_BIN := $(patsubst %/moc-qt5,%,$(firstword $(wildcard \
+ # No suffix given, so before we check out -qt5 look at qt5 specific locations to avoid choosers and symlinks.
+ PATH_TOOL_QT5_BIN := $(patsubst %/moc,%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt5/bin/moc) \
+ $(if $(intersects $(KBUILD_HOST_ARCH), $(KBUILD_ARCHES_64)),/usr/lib64/qt5/bin/moc,) \
+ /usr/lib/qt5/bin/moc \
+ /usr/local/lib/qt5/bin/moc \
+ /usr/qt/5/bin/moc \
+ /usr/local/qt/5/bin/moc \
+ /usr/share/qt5/bin/moc \
+ /usr/local/share/qt5/bin/moc \
+ )))
+ ifeq ($(PATH_TOOL_QT5_BIN),)
+ PATH_TOOL_QT5_BIN := $(patsubst %/moc-qt5,%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt5/bin/moc-qt5) \
/usr/lib/qt5/bin/moc-qt5 \
/usr/qt/5/bin/moc-qt5 \
/usr/share/qt5/bin/moc-qt5 \
/usr/local/bin/moc-qt5 \
/usr/bin/moc-qt5 \
)))
- ifneq ($(PATH_TOOL_QT5_BIN),)
- TOOL_QT5_BIN_SUFF := -qt5
- else
- # If no luck, try looking for moc in the qt5 specific locations.
- PATH_TOOL_QT5_BIN := $(patsubst %/moc,%,$(firstword $(wildcard \
- /usr/lib/qt5/bin/moc \
- /usr/qt/5/bin/moc \
- /usr/share/qt5/bin/moc \
- )))
+ ifneq ($(PATH_TOOL_QT5_BIN),)
+ TOOL_QT5_BIN_SUFF := -qt5
+ endif
endif
endif
# If still no go, try looking for qt4to5 and rcc.
ifeq ($(PATH_TOOL_QT5_BIN),)
PATH_TOOL_QT5_BIN := $(patsubst %/qt4to5,%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt5/bin/qt4to5) \
/usr/lib/qt5/bin/qt4to5 \
/usr/qt/5/bin/qt4to5 \
/usr/share/qt5/bin/qt4to5 \
@@ -281,6 +294,7 @@ ifndef PATH_TOOL_QT5_BIN
endif
ifeq ($(PATH_TOOL_QT5_BIN),)
PATH_TOOL_QT5_BIN := $(patsubst %/rcc$(TOOL_QT5_BIN_SUFF),%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt5/bin/rcc$(TOOL_QT5_BIN_SUFF)) \
/usr/lib/qt5/bin/rcc$(TOOL_QT5_BIN_SUFF) \
/usr/qt/5/bin/rcc$(TOOL_QT5_BIN_SUFF) \
/usr/share/qt5/bin/rcc$(TOOL_QT5_BIN_SUFF) \
@@ -290,6 +304,7 @@ ifndef PATH_TOOL_QT5_BIN
endif
if "$(PATH_TOOL_QT5_BIN)" == "" && "$(TOOL_QT5_BIN_SUFF)" != ""
PATH_TOOL_QT5_BIN := $(patsubst %/rcc,%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_HOST_DOT_ARCH)),/usr/lib/$(type)/qt5/bin/rcc) \
/usr/lib/qt5/bin/rcc \
/usr/qt/5/bin/rcc \
/usr/share/qt5/bin/rcc \
@@ -307,23 +322,28 @@ else
PATH_TOOL_QT5_BIN := $(PATH_TOOL_QT5_BIN)
endif
ifneq ($(PATH_TOOL_QT5_BIN),)
- TOOL_QT5_MOC ?= $(PATH_TOOL_QT5_BIN)/moc$(TOOL_QT5_BIN_SUFF)$(HOST_SUFF_EXE)
- TOOL_QT5_UIC ?= $(PATH_TOOL_QT5_BIN)/uic$(TOOL_QT5_BIN_SUFF)$(HOST_SUFF_EXE)
+ TOOL_QT5_MOC ?= $(PATH_TOOL_QT5_BIN)/moc$(TOOL_QT5_BIN_SUFF)$(HOSTSUFF_EXE)
+ TOOL_QT5_UIC ?= $(PATH_TOOL_QT5_BIN)/uic$(TOOL_QT5_BIN_SUFF)$(HOSTSUFF_EXE)
ifndef TOOL_QT5_RCC
- TOOL_QT5_RCC := $(PATH_TOOL_QT5_BIN)/rcc$(HOST_SUFF_EXE)
+ TOOL_QT5_RCC := $(PATH_TOOL_QT5_BIN)/rcc$(HOSTSUFF_EXE)
ifeq ($(wildcard $(TOOL_QT5_RCC)),)
- TOOL_QT5_RCC := $(PATH_TOOL_QT5_BIN)/rcc$(TOOL_QT5_BIN_SUFF)$(HOST_SUFF_EXE)
+ TOOL_QT5_RCC := $(PATH_TOOL_QT5_BIN)/rcc$(TOOL_QT5_BIN_SUFF)$(HOSTSUFF_EXE)
endif
endif
- TOOL_QT5_LRC ?= $(PATH_TOOL_QT5_BIN)/lrelease$(TOOL_QT5_BIN_SUFF)$(HOST_SUFF_EXE)
- TOOL_QT5_LUPDATE ?= $(PATH_TOOL_QT5_BIN)/lupdate$(TOOL_QT5_BIN_SUFF)$(HOST_SUFF_EXE)
+ TOOL_QT5_LRC ?= $(PATH_TOOL_QT5_BIN)/lrelease$(TOOL_QT5_BIN_SUFF)$(HOSTSUFF_EXE)
+ TOOL_QT5_LUPDATE ?= $(PATH_TOOL_QT5_BIN)/lupdate$(TOOL_QT5_BIN_SUFF)$(HOSTSUFF_EXE)
else
# Pathless, relies on the environment.
- TOOL_QT5_MOC ?= moc$(TOOL_QT5_BIN_SUFF)$(HOST_SUFF_EXE)
- TOOL_QT5_UIC ?= uic$(TOOL_QT5_BIN_SUFF)$(HOST_SUFF_EXE)
- TOOL_QT5_RCC ?= rcc$(HOST_SUFF_EXE)
- TOOL_QT5_LRC ?= lrelease$(TOOL_QT5_BIN_SUFF)$(HOST_SUFF_EXE)
- TOOL_QT5_LUPDATE ?= lupdate$(TOOL_QT5_BIN_SUFF)$(HOST_SUFF_EXE)
+ TOOL_QT5_MOC ?= moc$(TOOL_QT5_BIN_SUFF)$(HOSTSUFF_EXE)
+ TOOL_QT5_UIC ?= uic$(TOOL_QT5_BIN_SUFF)$(HOSTSUFF_EXE)
+ TOOL_QT5_RCC ?= rcc$(TOOL_QT5_BIN_SUFF)$(HOSTSUFF_EXE)
+ TOOL_QT5_LRC ?= lrelease$(TOOL_QT5_BIN_SUFF)$(HOSTSUFF_EXE)
+ TOOL_QT5_LUPDATE ?= lupdate$(TOOL_QT5_BIN_SUFF)$(HOSTSUFF_EXE)
+endif
+ifdef TOOL_QT5_USE_KSUBMIT
+ ifeq ($(KBUILD_HOST),win)
+ TOOL_QT5_MOC_KSUBMIT ?= kmk_builtin_kSubmit --$(SP)
+ endif
endif
# General Properties used by kBuild and/or units/qt.kmk
@@ -351,7 +371,7 @@ TOOL_QT5_MOC_CPP_DEPORD =
TOOL_QT5_MOC_CPP_OUTPUT =
TOOL_QT5_MOC_CPP_OUTPUT_MAYBE =
define TOOL_QT5_MOC_CPP_CMDS
- $(QUIET)$(TOOL_QT5_MOC)\
+ $(QUIET)$(TOOL_QT5_MOC_KSUBMIT)$(TOOL_QT5_MOC)\
$(flags)\
$(addprefix -I, $(incs))\
$(addprefix -D, $(defs))\
@@ -374,7 +394,7 @@ TOOL_QT5_MOC_HPP_DEPORD =
TOOL_QT5_MOC_HPP_OUTPUT =
TOOL_QT5_MOC_HPP_OUTPUT_MAYBE =
define TOOL_QT5_MOC_HPP_CMDS
- $(QUIET)$(TOOL_QT5_MOC)\
+ $(QUIET)$(TOOL_QT5_MOC_KSUBMIT)$(TOOL_QT5_MOC)\
$(flags)\
$(addprefix -I, $(incs))\
$(addprefix -D, $(defs))\
diff --git a/kBuild/units/vccprecomp.kmk b/kBuild/units/vccprecomp.kmk
new file mode 100644
index 0000000..290dcad
--- /dev/null
+++ b/kBuild/units/vccprecomp.kmk
@@ -0,0 +1,66 @@
+# $Id: vccprecomp.kmk 2956 2016-09-21 19:37:20Z bird $
+## @file
+# kBuild Unit - Target Level Precompiled Headers for Visual C++.
+#
+
+#
+# Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spam-xiv at anduin.net>
+#
+# This file is part of kBuild.
+#
+# kBuild is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# kBuild is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with kBuild; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+#
+# As a special exception you are granted permission to include this file, via
+# the kmk include directive, as you wish without this in itself causing the
+# resulting makefile, program or whatever to be covered by the GPL license.
+# This exception does not however invalidate any other reasons why the makefile,
+# program, whatever should not be covered the GPL.
+#
+#
+
+
+UNIT_vccprecomp = Target level precompiled Headers for Visual C++
+
+#
+# Early target processing pass #1.
+#
+# This set the internal _VCC_PCH_FILE and VCC_COMMON_OBJ_PDB properties,
+# which will be picked up by the VCCxxx tool.
+#
+define def_unit_vccprecomp_target_pre
+ $(target)_1_VCC_PCH_FILE := $(outbase)-pch.pch
+ $(target)_1_VCC_COMMON_OBJ_PDB := $(outbase)-common-obj.pdb
+endef
+
+#
+# Early target processing pass #2.
+#
+# This sets up a rule for creating the .pch file after qt5 and similar units
+# are done modifying INCS, DEFS and company. The 'tool' variable is defined by
+# footer-pass2-compiling-targets.kmk and is really the LD tool, but that'll
+# have to do for now. The '-PCH' variant of the VCC tool, is defined together
+# with $(tool) and allow us to bypass the options and dependencies triggered
+# by _1_VCC_PCH_FILE, _1_VCC_COMMON_OBJ_PDB and _PCH_HDR, and also make sure we
+# don't get circular dependencies by way of kDepObj and the debug info.
+#
+define def_unit_vccprecomp_target_pre_2
+ local source := $($(target)_PCH_HDR)
+ $(source)_TOOL := $(tool)-PCH
+ local suff := $(suffix $(source))
+ local type := CXX
+ $(kb-src-one 2)
+endef
+
diff --git a/src/kDepPre/kDepPre.c b/src/kDepPre/kDepPre.c
index 0b40e51..e677b5b 100644
--- a/src/kDepPre/kDepPre.c
+++ b/src/kDepPre/kDepPre.c
@@ -1,4 +1,4 @@
-/* $Id: kDepPre.c 2413 2010-09-11 17:43:04Z bird $ */
+/* $Id: kDepPre.c 2955 2016-09-21 19:05:53Z bird $ */
/** @file
* kDepPre - Dependency Generator using Precompiler output.
*/
@@ -464,7 +464,7 @@ int main(int argc, char *argv[])
*/
if (!i)
{
- depOptimize(fFixCase, 0 /* fQuiet */);
+ depOptimize(fFixCase, 0 /* fQuiet */, NULL /*pszIgnoredExt*/);
fprintf(pOutput, "%s:", pszTarget);
depPrint(pOutput);
if (fStubs)
diff --git a/src/kObjCache/kObjCache.c b/src/kObjCache/kObjCache.c
index d6badb7..fdb0697 100644
--- a/src/kObjCache/kObjCache.c
+++ b/src/kObjCache/kObjCache.c
@@ -1,4 +1,4 @@
-/* $Id: kObjCache.c 2627 2012-08-09 14:12:12Z bird $ */
+/* $Id: kObjCache.c 2955 2016-09-21 19:05:53Z bird $ */
/** @file
* kObjCache - Object Cache.
*/
@@ -1100,7 +1100,7 @@ static void kOCDepWriteToFile(PKOCDEP pDepState, const char *pszFilename, const
if (!pFile)
FatalMsg("Failed to open dependency file '%s': %s\n", pszFilename, strerror(errno));
- depOptimize(fFixCase, fQuiet);
+ depOptimize(fFixCase, fQuiet, NULL /*pszIgnoredExt*/);
/* Make object file name with unix slashes. */
pszObjFileAbs = MakePathFromDirAndFile(pszObjFile, pszObjDir);
@@ -5087,7 +5087,7 @@ int main(int argc, char **argv)
}
else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
{
- printf("kObjCache - kBuild version %d.%d.%d ($Revision: 2627 $)\n"
+ printf("kObjCache - kBuild version %d.%d.%d ($Revision: 2955 $)\n"
"Copyright (c) 2007-2012 knut st. osmundsen\n",
KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH);
return 0;
diff --git a/src/kWorker/Makefile.kmk b/src/kWorker/Makefile.kmk
index 2016bb8..2f0331b 100644
--- a/src/kWorker/Makefile.kmk
+++ b/src/kWorker/Makefile.kmk
@@ -1,4 +1,4 @@
-# $Id: Makefile.kmk 2894 2016-09-08 13:27:56Z bird $
+# $Id: Makefile.kmk 2968 2016-09-26 18:14:50Z bird $
## @file
# Sub-makefile for kWorker.
#
@@ -30,6 +30,7 @@ include $(PATH_KBUILD)/subheader.kmk
PROGRAMS += kWorker
kWorker_TEMPLATE = BIN-STATIC-THREADED
+kWorker_DEFS := KWORKER
kWorker_DEFS.debug = K_STRICT
kWorker_DEFS.release = NASSERT
kWorker_SOURCES = \
@@ -47,7 +48,9 @@ kWorker_LIBS.win = \
$(PATH_SDK_WINDDK71_LIB_WNET)/ntdll.lib \
$(PATH_SDK_WINDDK71_LIB_WNET)/psapi.lib
kWorker_LDFLAGS.win = \
- /BASE:0x10000 /DYNAMICBASE:NO /FIXED /SECTION:DefLdBuf,EWR
+ /BASE:0x10000 /DYNAMICBASE:NO /FIXED
+#kWorker_LDFLAGS.win.x86 = \
+# /SAFESEH:NO - doesn't help anyone.
#
@@ -57,6 +60,7 @@ LIBRARIES += kWorkerLib
kWorkerLib_TEMPLATE = LIB-STATIC-THREADED
kWorkerLib_DEFPATH = ../lib # Need fix from r2837.
kWorkerLib_DEFPATH := $(PATH_SUB_CURRENT)/../lib
+kWorkerLib_DEFS := KWORKER
kWorkerLib_SOURCES = \
crc32.c \
md5.c \
@@ -72,7 +76,10 @@ kWorkerLib_SOURCES.win = \
nt/ntstat.c \
nt/ntunlink.c \
nt/kFsCache.c \
- quote_argv.c
+ quote_argv.c \
+ maybe_con_write.c \
+ maybe_con_fwrite.c \
+ msc_buffered_printf.c
kbuild_version.c_DEFS = KBUILD_SVN_REV=$(KBUILD_SVN_REV)
#
diff --git a/src/kWorker/kWorker.c b/src/kWorker/kWorker.c
index 6962738..13e7d94 100644
--- a/src/kWorker/kWorker.c
+++ b/src/kWorker/kWorker.c
@@ -1,4 +1,4 @@
-/* $Id: kWorker.c 2906 2016-09-09 22:15:57Z bird $ */
+/* $Id: kWorker.c 2987 2016-11-01 18:27:39Z bird $ */
/** @file
* kWorker - experimental process reuse worker for Windows.
*
@@ -31,6 +31,9 @@
* Header Files *
*********************************************************************************************************************************/
//#undef NDEBUG
+//#define K_STRICT 1
+//#define KW_LOG_ENABLED
+
#define PSAPI_VERSION 1
#include <k/kHlp.h>
#include <k/kLdr.h>
@@ -40,16 +43,16 @@
#include <setjmp.h>
#include <ctype.h>
#include <errno.h>
+#include <process.h>
#include "nt/ntstat.h"
#include "kbuild_version.h"
-/* lib/nt_fullpath.c */
-extern void nt_fullpath(const char *pszPath, char *pszFull, size_t cchFull);
#include "nt/ntstuff.h"
#include <psapi.h>
#include "nt/kFsCache.h"
+#include "nt_fullpath.h"
#include "quote_argv.h"
#include "md5.h"
@@ -69,37 +72,91 @@ extern void nt_fullpath(const char *pszPath, char *pszFull, size_t cchFull);
* they are included. */
#define WITH_HASH_MD5_CACHE
+/** @def WITH_CRYPT_CTX_REUSE
+ * Enables reusing crypt contexts. The Visual C++ compiler always creates a
+ * context which is only used for MD5 and maybe some random bytes (VS 2010).
+ * So, only create it once and add a reference to it instead of creating new
+ * ones. Saves registry access among other things. */
+#define WITH_CRYPT_CTX_REUSE
+
/** @def WITH_CONSOLE_OUTPUT_BUFFERING
* Enables buffering of all console output as well as removal of annoying
* source file echo by cl.exe. */
#define WITH_CONSOLE_OUTPUT_BUFFERING
+/** @def WITH_STD_OUT_ERR_BUFFERING
+ * Enables buffering of standard output and standard error buffer as well as
+ * removal of annoying source file echo by cl.exe. */
+#define WITH_STD_OUT_ERR_BUFFERING
-/** String constant comma length. */
-#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
+/** @def WITH_LOG_FILE
+ * Log to file instead of stderr. */
+#define WITH_LOG_FILE
+
+/** @def WITH_HISTORY
+ * Keep history of the last jobs. For debugging. */
+#define WITH_HISTORY
+
+/** @def WITH_FIXED_VIRTUAL_ALLOCS
+ * Whether to pre allocate memory for known fixed VirtualAlloc calls (currently
+ * there is only one, but an important one, from cl.exe).
+ */
+#if K_ARCH == K_ARCH_X86_32
+# define WITH_FIXED_VIRTUAL_ALLOCS
+#endif
+
+/** @def WITH_PCH_CACHING
+ * Enables read caching of precompiled header files. */
+#if K_ARCH_BITS >= 64
+# define WITH_PCH_CACHING
+#endif
+
+
+#ifndef NDEBUG
+# define KW_LOG_ENABLED
+#endif
/** @def KW_LOG
* Generic logging.
* @param a Argument list for kwDbgPrintf */
-#ifndef NDEBUG
+#ifdef KW_LOG_ENABLED
# define KW_LOG(a) kwDbgPrintf a
#else
# define KW_LOG(a) do { } while (0)
#endif
+/** @def KWLDR_LOG
+ * Loader related logging.
+ * @param a Argument list for kwDbgPrintf */
+#ifdef KW_LOG_ENABLED
+# define KWLDR_LOG(a) kwDbgPrintf a
+#else
+# define KWLDR_LOG(a) do { } while (0)
+#endif
+
+
/** @def KWFS_LOG
* FS cache logging.
* @param a Argument list for kwDbgPrintf */
-#ifndef NDEBUG
+#ifdef KW_LOG_ENABLED
# define KWFS_LOG(a) kwDbgPrintf a
#else
# define KWFS_LOG(a) do { } while (0)
#endif
+/** @def KWOUT_LOG
+ * Output related logging.
+ * @param a Argument list for kwDbgPrintf */
+#ifdef KW_LOG_ENABLED
+# define KWOUT_LOG(a) kwDbgPrintf a
+#else
+# define KWOUT_LOG(a) do { } while (0)
+#endif
+
/** @def KWCRYPT_LOG
* FS cache logging.
* @param a Argument list for kwDbgPrintf */
-#ifndef NDEBUG
+#ifdef KW_LOG_ENABLED
# define KWCRYPT_LOG(a) kwDbgPrintf a
#else
# define KWCRYPT_LOG(a) do { } while (0)
@@ -122,9 +179,9 @@ extern void nt_fullpath(const char *pszPath, char *pszFull, size_t cchFull);
/** Marks unfinished code. */
#if 1
-# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); __debugbreak(); } while (0)
+# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); __debugbreak(); } while (0)
#else
-# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); } while (0)
+# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); } while (0)
#endif
/** User data key for tools. */
@@ -132,6 +189,9 @@ extern void nt_fullpath(const char *pszPath, char *pszFull, size_t cchFull);
/** User data key for a cached file. */
#define KW_DATA_KEY_CACHED_FILE (~(KUPTR)65521)
+/** String constant comma length. */
+#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
+
/*********************************************************************************************************************************
* Structures and Typedefs *
@@ -188,9 +248,9 @@ typedef struct KWMODULE
struct
{
/** Where we load the image. */
- void *pvLoad;
+ KU8 *pbLoad;
/** Virgin copy of the image. */
- void *pvCopy;
+ KU8 *pbCopy;
/** Ldr pvBits argument. This is NULL till we've successfully resolved
* the imports. */
void *pvBits;
@@ -206,6 +266,32 @@ typedef struct KWMODULE
#endif
/** Set if we share memory with other executables. */
KBOOL fUseLdBuf;
+ /** Set after the first whole image copy is done. */
+ KBOOL fCanDoQuick;
+ /** Number of quick copy chunks. */
+ KU8 cQuickCopyChunks;
+ /** Number of quick zero chunks. */
+ KU8 cQuickZeroChunks;
+ /** Quicker image copy instructions that skips non-writable parts when
+ * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
+ * image. */
+ struct
+ {
+ /** The copy destination. */
+ KU8 *pbDst;
+ /** The copy source. */
+ KU8 const *pbSrc;
+ /** How much to copy. */
+ KSIZE cbToCopy;
+ } aQuickCopyChunks[3];
+ /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
+ struct
+ {
+ /** Where to start zeroing. */
+ KU8 *pbDst;
+ /** How much to zero. */
+ KSIZE cbToZero;
+ } aQuickZeroChunks[3];
/** Number of imported modules. */
KSIZE cImpMods;
/** Import array (variable size). */
@@ -260,6 +346,8 @@ typedef struct KFSWCACHEDFILE
/** Cached file handle. */
HANDLE hCached;
+ /** Cached file section handle. */
+ HANDLE hSection;
/** Cached file content. */
KU8 *pbCached;
/** The file size. */
@@ -353,25 +441,49 @@ typedef struct KWFSTEMPFILE
#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
/**
- * Console line buffer.
+ * Console line buffer or output full buffer.
*/
-typedef struct KWCONSOLEOUTPUTLINE
+typedef struct KWOUTPUTSTREAMBUF
{
/** The main output handle. */
HANDLE hOutput;
/** Our backup handle. */
HANDLE hBackup;
- /** Set if this is a console handle. */
+ /** Set if this is a console handle and we're in line buffered mode.
+ * When clear, we may buffer multiple lines, though try flush on line
+ * boundraries when ever possible. */
KBOOL fIsConsole;
- /** Amount of pending console output in wchar_t's. */
- KU32 cwcBuf;
- /** The allocated buffer size. */
- KU32 cwcBufAlloc;
- /** Pending console output. */
- wchar_t *pwcBuf;
-} KWCONSOLEOUTPUTLINE;
+ /** Compressed GetFileType result. */
+ KU8 fFileType;
+ KU8 abPadding[2];
+ union
+ {
+ /** Line buffer mode (fIsConsole == K_TRUE). */
+ struct
+ {
+ /** Amount of pending console output in wchar_t's. */
+ KU32 cwcBuf;
+ /** The allocated buffer size. */
+ KU32 cwcBufAlloc;
+ /** Pending console output. */
+ wchar_t *pwcBuf;
+ } Con;
+ /** Fully buffered mode (fIsConsole == K_FALSE). */
+ struct
+ {
+ /** Amount of pending output (in chars). */
+ KU32 cchBuf;
+#ifdef WITH_STD_OUT_ERR_BUFFERING
+ /** The allocated buffer size (in chars). */
+ KU32 cchBufAlloc;
+ /** Pending output. */
+ char *pchBuf;
+#endif
+ } Fully;
+ } u;
+} KWOUTPUTSTREAMBUF;
/** Pointer to a console line buffer. */
-typedef KWCONSOLEOUTPUTLINE *PKWCONSOLEOUTPUTLINE;
+typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
/**
* Combined console buffer of complete lines.
@@ -401,15 +513,18 @@ typedef enum KWHANDLETYPE
{
KWHANDLETYPE_INVALID = 0,
KWHANDLETYPE_FSOBJ_READ_CACHE,
+ KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
KWHANDLETYPE_TEMP_FILE,
- KWHANDLETYPE_TEMP_FILE_MAPPING
- //KWHANDLETYPE_CONSOLE_CACHE
+ KWHANDLETYPE_TEMP_FILE_MAPPING,
+ KWHANDLETYPE_OUTPUT_BUF
} KWHANDLETYPE;
/** Handle data. */
typedef struct KWHANDLE
{
KWHANDLETYPE enmType;
+ /** Number of references */
+ KU32 cRefs;
/** The current file offset. */
KU32 offFile;
/** Handle access. */
@@ -424,10 +539,38 @@ typedef struct KWHANDLE
PKFSWCACHEDFILE pCachedFile;
/** Temporary file handle or mapping handle. */
PKWFSTEMPFILE pTempFile;
+#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
+ /** Buffered output stream. */
+ PKWOUTPUTSTREAMBUF pOutBuf;
+#endif
} u;
} KWHANDLE;
typedef KWHANDLE *PKWHANDLE;
+/**
+ * Tracking one of our memory mappings.
+ */
+typedef struct KWMEMMAPPING
+{
+ /** Number of references. */
+ KU32 cRefs;
+ /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
+ * KWHANDLETYPE_TEMP_FILE_MAPPING). */
+ KWHANDLETYPE enmType;
+ /** The mapping address. */
+ PVOID pvMapping;
+ /** Type specific data. */
+ union
+ {
+ /** The file system object. */
+ PKFSWCACHEDFILE pCachedFile;
+ /** Temporary file handle or mapping handle. */
+ PKWFSTEMPFILE pTempFile;
+ } u;
+} KWMEMMAPPING;
+/** Pointer to a memory mapping tracker. */
+typedef KWMEMMAPPING *PKWMEMMAPPING;
+
/** Pointer to a VirtualAlloc tracker entry. */
typedef struct KWVIRTALLOC *PKWVIRTALLOC;
@@ -439,6 +582,8 @@ typedef struct KWVIRTALLOC
PKWVIRTALLOC pNext;
void *pvAlloc;
KSIZE cbAlloc;
+ /** This is KU32_MAX if not a preallocated chunk. */
+ KU32 idxPreAllocated;
} KWVIRTALLOC;
@@ -557,6 +702,8 @@ typedef struct KWSANDBOX
int rcExitCode;
/** Set if we're running. */
KBOOL fRunning;
+ /** Whether to disable caching of ".pch" files. */
+ KBOOL fNoPchCaching;
/** The command line. */
char *pszCmdLine;
@@ -597,6 +744,17 @@ typedef struct KWSANDBOX
KU32 cHandles;
/** Number of active handles in the table. */
KU32 cActiveHandles;
+ /** Number of handles in the handle table that will not be freed. */
+ KU32 cFixedHandles;
+ /** Total number of leaked handles. */
+ KU32 cLeakedHandles;
+
+ /** Number of active memory mappings in paMemMappings. */
+ KU32 cMemMappings;
+ /** The allocated size of paMemMappings. */
+ KU32 cMemMappingsAlloc;
+ /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
+ PKWMEMMAPPING paMemMappings;
/** Head of the list of temporary file. */
PKWFSTEMPFILE pTempFileHead;
@@ -615,7 +773,7 @@ typedef struct KWSANDBOX
* This is only done from images we forcibly restore. */
PKWEXITCALLACK pExitCallbackHead;
- UNICODE_STRING SavedCommandLine;
+ MY_UNICODE_STRING SavedCommandLine;
#ifdef WITH_HASH_MD5_CACHE
/** The special MD5 hash instance. */
@@ -639,11 +797,39 @@ typedef struct KWSANDBOX
} LastHashRead;
#endif
+#ifdef WITH_CRYPT_CTX_REUSE
+ /** Reusable crypt contexts. */
+ struct
+ {
+ /** The creation provider type. */
+ KU32 dwProvType;
+ /** The creation flags. */
+ KU32 dwFlags;
+ /** The length of the container name. */
+ KU32 cwcContainer;
+ /** The length of the provider name. */
+ KU32 cwcProvider;
+ /** The container name string. */
+ wchar_t *pwszContainer;
+ /** The provider name string. */
+ wchar_t *pwszProvider;
+ /** The context handle. */
+ HCRYPTPROV hProv;
+ } aCryptCtxs[4];
+ /** Number of reusable crypt conexts in aCryptCtxs. */
+ KU32 cCryptCtxs;
+#endif
+
+
#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
- /** Standard output (and whatever else) line buffer. */
- KWCONSOLEOUTPUTLINE StdOut;
- /** Standard error line buffer. */
- KWCONSOLEOUTPUTLINE StdErr;
+ /** The internal standard output handle. */
+ KWHANDLE HandleStdOut;
+ /** The internal standard error handle. */
+ KWHANDLE HandleStdErr;
+ /** Standard output (and whatever else) buffer. */
+ KWOUTPUTSTREAMBUF StdOut;
+ /** Standard error buffer. */
+ KWOUTPUTSTREAMBUF StdErr;
/** Combined buffer of completed lines. */
KWCONSOLEOUTPUT Combined;
#endif
@@ -692,6 +878,9 @@ static KWSANDBOX g_Sandbox;
/** The module currently occupying g_abDefLdBuf. */
static PKWMODULE g_pModInLdBuf = NULL;
+/** The module that previuosly occupied g_abDefLdBuf. */
+static PKWMODULE g_pModPrevInLdBuf = NULL;
+
/** Module hash table. */
static PKWMODULE g_apModules[127];
@@ -708,6 +897,10 @@ static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
static PKFSCACHE g_pFsCache;
/** The current directory (referenced). */
static PKFSOBJ g_pCurDirObj = NULL;
+#ifdef KBUILD_OS_WINDOWS
+/** The windows system32 directory (referenced). */
+static PKFSDIR g_pWinSys32 = NULL;
+#endif
/** Verbosity level. */
static int g_cVerbose = 2;
@@ -715,6 +908,9 @@ static int g_cVerbose = 2;
/** Whether we should restart the worker. */
static KBOOL g_fRestart = K_FALSE;
+/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
+static KBOOL volatile g_fCtrlC = K_FALSE;
+
/* Further down. */
extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
extern KU32 const g_cSandboxReplacements;
@@ -722,12 +918,99 @@ extern KU32 const g_cSandboxReplacements;
extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
extern KU32 const g_cSandboxNativeReplacements;
+extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
+extern KU32 const g_cSandboxGetProcReplacements;
+
+
/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
- * cover the default executable link address of 0x400000. */
-#pragma section("DefLdBuf", write, execute, read)
-__declspec(allocate("DefLdBuf"))
+ * cover the default executable link address of 0x400000.
+ * @remarks Early main() makes it read+write+executable. Attempts as having
+ * it as a separate section failed because the linker insists on
+ * writing out every zero in the uninitialized section, resulting in
+ * really big binaries. */
+__declspec(align(0x1000))
static KU8 g_abDefLdBuf[16*1024*1024];
+#ifdef WITH_LOG_FILE
+/** Log file handle. */
+static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
+#endif
+
+
+#ifdef WITH_FIXED_VIRTUAL_ALLOCS
+/** Virtual address space reserved for CL.EXE heap manager.
+ *
+ * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
+ * address. It's among other things used for precompiled headers, which
+ * seemingly have addresses hardcoded into them and won't work if mapped
+ * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
+ * for it. (The /Zm option may affect this allocation.)
+ */
+static struct
+{
+ /** The memory address we need. */
+ KUPTR const uFixed;
+ /** How much we need to fix. */
+ KSIZE const cbFixed;
+ /** What we actually got, NULL if given back. */
+ void *pvReserved;
+ /** Whether it is in use or not. */
+ KBOOL fInUse;
+} g_aFixedVirtualAllocs[] =
+{
+# if K_ARCH == K_ARCH_X86_32
+ /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
+ 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
+ { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
+# else
+ { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
+# endif
+};
+#endif
+
+
+#ifdef WITH_HISTORY
+/** The job history. */
+static char *g_apszHistory[32];
+/** Index of the next history entry. */
+static unsigned g_iHistoryNext = 0;
+#endif
+
+
+/** Number of jobs executed. */
+static KU32 g_cJobs;
+/** Number of tools. */
+static KU32 g_cTools;
+/** Number of modules. */
+static KU32 g_cModules;
+/** Number of non-native modules. */
+static KU32 g_cNonNativeModules;
+/** Number of read-cached files. */
+static KU32 g_cReadCachedFiles;
+/** Total size of read-cached files. */
+static KSIZE g_cbReadCachedFiles;
+
+/** Total number of ReadFile calls. */
+static KSIZE g_cReadFileCalls;
+/** Total bytes read via ReadFile. */
+static KSIZE g_cbReadFileTotal;
+/** Total number of read from read-cached files. */
+static KSIZE g_cReadFileFromReadCached;
+/** Total bytes read from read-cached files. */
+static KSIZE g_cbReadFileFromReadCached;
+/** Total number of read from in-memory temporary files. */
+static KSIZE g_cReadFileFromInMemTemp;
+/** Total bytes read from in-memory temporary files. */
+static KSIZE g_cbReadFileFromInMemTemp;
+
+/** Total number of WriteFile calls. */
+static KSIZE g_cWriteFileCalls;
+/** Total bytes written via WriteFile. */
+static KSIZE g_cbWriteFileTotal;
+/** Total number of written to from in-memory temporary files. */
+static KSIZE g_cWriteFileToInMemTemp;
+/** Total bytes written to in-memory temporary files. */
+static KSIZE g_cbWriteFileToInMemTemp;
/*********************************************************************************************************************************
@@ -735,13 +1018,14 @@ static KU8 g_abDefLdBuf[16*1024*1024];
*********************************************************************************************************************************/
static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter, PKWMODULE *ppMod);
-static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle);
+static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
-static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLineBuf, const char *pchBuffer, KU32 cchToWrite);
+static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
#endif
+
/**
* Debug printing.
* @param pszFormat Debug format string.
@@ -752,9 +1036,32 @@ static void kwDbgPrintfV(const char *pszFormat, va_list va)
if (g_cVerbose >= 2)
{
DWORD const dwSavedErr = GetLastError();
+#ifdef WITH_LOG_FILE
+ DWORD dwIgnored;
+ char szTmp[2048];
+ int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
+ int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
+ if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
+ cch += cchPrefix;
+ else
+ {
+ cch = sizeof(szTmp) - 1;
+ szTmp[cch] = '\0';
+ }
+
+ if (g_hLogFile == INVALID_HANDLE_VALUE)
+ {
+ wchar_t wszFilename[128];
+ _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
+ g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
+ }
+ WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
+#else
fprintf(stderr, "debug: ");
vfprintf(stderr, pszFormat, va);
+#endif
SetLastError(dwSavedErr);
}
@@ -1326,8 +1633,8 @@ static void kwLdrModuleRelease(PKWMODULE pMod)
if (!pMod->fNative)
{
- kHlpPageFree(pMod->u.Manual.pvCopy, pMod->cbImage);
- kHlpPageFree(pMod->u.Manual.pvLoad, pMod->cbImage);
+ kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
+ kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
}
kHlpFree(pMod);
@@ -1547,6 +1854,7 @@ static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const ch
KW_LOG(("New module: %p LB %#010x %s (native)\n",
(KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath));
+ g_cModules++;
return kwLdrModuleLink(pMod);
}
return NULL;
@@ -1583,6 +1891,116 @@ static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KB
/**
+ * Sets up the quick zero & copy tables for the non-native module.
+ *
+ * This is a worker for kwLdrModuleCreateNonNative.
+ *
+ * @param pMod The module.
+ */
+static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
+{
+ PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
+ KU32 cSegs = pMod->pLdrMod->cSegments;
+ KU32 iSeg;
+
+ KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
+ pMod->u.Manual.cQuickCopyChunks = 0;
+ pMod->u.Manual.cQuickZeroChunks = 0;
+
+ for (iSeg = 0; iSeg < cSegs; iSeg++)
+ switch (paSegs[iSeg].enmProt)
+ {
+ case KPROT_READWRITE:
+ case KPROT_WRITECOPY:
+ case KPROT_EXECUTE_READWRITE:
+ case KPROT_EXECUTE_WRITECOPY:
+ if (paSegs[iSeg].cbMapped)
+ {
+ KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
+ if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
+ {
+ /*
+ * Check for trailing zero words.
+ */
+ KSIZE cbTrailingZeros;
+ if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
+ && (paSegs[iSeg].cbMapped & 7) == 0
+ && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
+ {
+ KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
+ KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
+ KSIZE idxFirstZero = cNatural;
+ while (idxFirstZero > 0)
+ if (pauNatural[--idxFirstZero] == 0)
+ { /* likely */ }
+ else
+ {
+ idxFirstZero++;
+ break;
+ }
+ cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
+ if (cbTrailingZeros < 128)
+ cbTrailingZeros = 0;
+ }
+ else
+ cbTrailingZeros = 0;
+
+ /*
+ * Add quick copy entry.
+ */
+ if (cbTrailingZeros < paSegs[iSeg].cbMapped)
+ {
+ pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
+ pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
+ pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
+ pMod->u.Manual.cQuickCopyChunks = iChunk + 1;
+ KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
+ pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
+ pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
+ pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
+ paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
+ }
+
+ /*
+ * Add quick zero entry.
+ */
+ if (cbTrailingZeros)
+ {
+ KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
+ pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
+ + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
+ pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
+ pMod->u.Manual.cQuickZeroChunks = iZero + 1;
+ KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
+ pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
+ pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
+ paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
+ }
+ }
+ else
+ {
+ /*
+ * We're out of quick copy table entries, so just copy the whole darn thing.
+ * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
+ */
+ kHlpAssertFailed();
+ pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
+ pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
+ pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
+ pMod->u.Manual.cQuickCopyChunks = 1;
+ KWLDR_LOG(("Quick copy not possible!\n"));
+ return;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/**
* Creates a module using the our own loader.
*
* @returns Module w/ 1 reference on success, NULL on failure.
@@ -1647,10 +2065,13 @@ static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath,
pMod->fNative = K_FALSE;
pMod->pLdrMod = pLdrMod;
pMod->u.Manual.cImpMods = (KU32)cImports;
- pMod->u.Manual.fUseLdBuf = K_FALSE;
#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
#endif
+ pMod->u.Manual.fUseLdBuf = K_FALSE;
+ pMod->u.Manual.fCanDoQuick = K_FALSE;
+ pMod->u.Manual.cQuickZeroChunks = 0;
+ pMod->u.Manual.cQuickCopyChunks = 0;
pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
@@ -1660,32 +2081,31 @@ static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath,
*/
fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
|| pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
- pMod->u.Manual.pvLoad = fFixed ? (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
+ pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
if ( !fFixed
|| pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
- || (KUPTR)pMod->u.Manual.pvLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
- || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pvLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
- rc = kHlpPageAlloc(&pMod->u.Manual.pvLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
+ || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
+ || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
+ rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
else
pMod->u.Manual.fUseLdBuf = K_TRUE;
if (rc == 0)
{
- rc = kHlpPageAlloc(&pMod->u.Manual.pvCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
+ rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
if (rc == 0)
{
-
KI32 iImp;
/*
* Link the module (unless it's an executable image) and process the imports.
*/
- pMod->hOurMod = (HMODULE)pMod->u.Manual.pvLoad;
+ pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
if (!fExe)
kwLdrModuleLink(pMod);
KW_LOG(("New module: %p LB %#010x %s (kLdr)\n",
- pMod->u.Manual.pvLoad, pMod->cbImage, pMod->pszPath));
- kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pvLoad);
+ pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
+ kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
for (iImp = 0; iImp < cImports; iImp++)
{
@@ -1702,7 +2122,7 @@ static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath,
if (rc == 0)
{
- rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pvCopy, (KUPTR)pMod->u.Manual.pvLoad,
+ rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
kwLdrModuleGetImportCallback, pMod);
if (rc == 0)
{
@@ -1711,7 +2131,7 @@ static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath,
* Find the function table. No validation here because the
* loader did that already, right...
*/
- KU8 *pbImg = (KU8 *)pMod->u.Manual.pvCopy;
+ KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
IMAGE_NT_HEADERS const *pNtHdrs;
IMAGE_DATA_DIRECTORY const *pXcptDir;
if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
@@ -1734,11 +2154,15 @@ static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath,
}
#endif
+ kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
+
/*
* Final finish.
*/
- pMod->u.Manual.pvBits = pMod->u.Manual.pvCopy;
+ pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
+ g_cModules++;
+ g_cNonNativeModules++;
return pMod;
}
}
@@ -1747,7 +2171,7 @@ static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath,
return NULL;
}
- kHlpPageFree(pMod->u.Manual.pvLoad, pMod->cbImage);
+ kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
}
else if (fFixed)
@@ -1781,7 +2205,7 @@ static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbo
NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
puValue, pfKind);
else
- rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pvLoad,
+ rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
iSymbol, pchSymbol, cchSymbol, pszVersion,
NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
puValue, pfKind);
@@ -1822,7 +2246,7 @@ static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbo
static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
{
KLDRADDR uLdrAddrMain;
- int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pvLoad, &uLdrAddrMain);
+ int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
if (rc == 0)
{
*puAddrMain = (KUPTR)uLdrAddrMain;
@@ -1850,18 +2274,75 @@ static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLO
/**
+ * Lazily initializes the g_pWinSys32 variable.
+ */
+static PKFSDIR kwLdrResolveWinSys32(void)
+{
+ KFSLOOKUPERROR enmError;
+ PKFSDIR pWinSys32;
+
+ /* Get the path first. */
+ char szSystem32[MAX_PATH];
+ if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
+ {
+ kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
+ strcpy(szSystem32, "C:\\Windows\\System32");
+ }
+
+ /* Look it up and verify it. */
+ pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
+ if (pWinSys32)
+ {
+ if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
+ {
+ g_pWinSys32 = pWinSys32;
+ return pWinSys32;
+ }
+
+ kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
+ }
+ else
+ kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
+ return NULL;
+}
+
+
+/**
* Whether we can load this DLL natively or not.
*
* @returns K_TRUE/K_FALSE.
* @param pszFilename The filename (no path).
* @param enmLocation The location.
+ * @param pszFullPath The full filename and path.
*/
-static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation)
+static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
{
if (enmLocation == KWLOCATION_SYSTEM32)
return K_TRUE;
if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
return K_TRUE;
+
+ /* If the location is unknown, we must check if it's some dynamic loading
+ of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
+ if (enmLocation == KWLOCATION_UNKNOWN)
+ {
+ PKFSDIR pWinSys32 = g_pWinSys32;
+ if (!pWinSys32)
+ pWinSys32 = kwLdrResolveWinSys32();
+ if (pWinSys32)
+ {
+ KFSLOOKUPERROR enmError;
+ PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
+ if (pFsObj)
+ {
+ KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
+ kFsCacheObjRelease(g_pFsCache, pFsObj);
+ if (fInWinSys32)
+ return K_TRUE;
+ }
+ }
+ }
+
return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
|| kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
|| kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
@@ -1954,7 +2435,7 @@ static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocati
* Not in the hash table, so we have to load it from scratch.
*/
pszName = kHlpGetFilename(szNormPath);
- if (kwLdrModuleCanLoadNatively(pszName, enmLocation))
+ if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
else
@@ -2054,7 +2535,9 @@ static int kwLdrModuleInitTree(PKWMODULE pMod)
int rc = 0;
if (!pMod->fNative)
{
- /* Need to copy bits? */
+ /*
+ * Need to copy bits?
+ */
if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
{
if (pMod->u.Manual.fUseLdBuf)
@@ -2066,29 +2549,69 @@ static int kwLdrModuleInitTree(PKWMODULE pMod)
kHlpAssert(fRc); K_NOREF(fRc);
}
#endif
+ g_pModPrevInLdBuf = g_pModInLdBuf;
g_pModInLdBuf = pMod;
}
- kHlpMemCopy(pMod->u.Manual.pvLoad, pMod->u.Manual.pvCopy, pMod->cbImage);
+ /* Do quick zeroing and copying when we can. */
+ pMod->u.Manual.fCanDoQuick = K_FALSE;
+ if ( pMod->u.Manual.fCanDoQuick
+ && ( !pMod->u.Manual.fUseLdBuf
+ || g_pModPrevInLdBuf == pMod))
+ {
+ /* Zero first. */
+ kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
+ switch (pMod->u.Manual.cQuickZeroChunks)
+ {
+ case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
+ case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
+ case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
+ case 0: break;
+ }
+
+ /* Then copy. */
+ kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
+ kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
+ switch (pMod->u.Manual.cQuickCopyChunks)
+ {
+ case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
+ pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
+ case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
+ pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
+ case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
+ pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
+ case 0: break;
+ }
+ }
+ /* Must copy the whole image. */
+ else
+ {
+ kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
+ pMod->u.Manual.fCanDoQuick = K_TRUE;
+ }
pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
}
#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
- /* Need to register function table? */
+ /*
+ * Need to register function table?
+ */
if ( !pMod->u.Manual.fRegisteredFunctionTable
&& pMod->u.Manual.cFunctions > 0)
{
pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
pMod->u.Manual.cFunctions,
- (KUPTR)pMod->u.Manual.pvLoad) != FALSE;
+ (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
}
#endif
if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
{
- /* Must do imports first, but mark our module as being initialized to avoid
- endless recursion should there be a dependency loop. */
+ /*
+ * Must do imports first, but mark our module as being initialized to avoid
+ * endless recursion should there be a dependency loop.
+ */
KSIZE iImp;
pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
@@ -2099,7 +2622,7 @@ static int kwLdrModuleInitTree(PKWMODULE pMod)
return rc;
}
- rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pvLoad, (KUPTR)pMod->u.Manual.pvLoad);
+ rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
if (rc == 0)
pMod->u.Manual.enmState = KWMODSTATE_READY;
else
@@ -2350,6 +2873,7 @@ static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj)
pTool->enmType = KWTOOLTYPE_EXEC;
kFsCacheObjRelease(g_pFsCache, pToolFsObj);
+ g_cTools++;
return pTool;
}
kFsCacheObjRelease(g_pFsCache, pToolFsObj);
@@ -2402,6 +2926,76 @@ static PKWTOOL kwToolLookup(const char *pszExe)
*/
+/**
+ * This is for kDep.
+ */
+int kwFsPathExists(const char *pszPath)
+{
+ BirdTimeSpec_T TsIgnored;
+ KFSLOOKUPERROR enmError;
+ PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
+ if (pFsObj)
+ {
+ kFsCacheObjRelease(g_pFsCache, pFsObj);
+ return 1;
+ }
+ return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
+}
+
+
+/* duplicated in dir-nt-bird.c */
+void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
+{
+ KFSLOOKUPERROR enmError;
+ PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
+ if (pPathObj)
+ {
+ KSIZE off = pPathObj->cchParent;
+ if (off > 0)
+ {
+ KSIZE offEnd = off + pPathObj->cchName;
+ if (offEnd < cbFull)
+ {
+ PKFSDIR pAncestor;
+
+ pszFull[off + pPathObj->cchName] = '\0';
+ memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
+
+ for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
+ {
+ kHlpAssert(off > 1);
+ kHlpAssert(pAncestor != NULL);
+ kHlpAssert(pAncestor->Obj.cchName > 0);
+ pszFull[--off] = '/';
+ off -= pAncestor->Obj.cchName;
+ kHlpAssert(pAncestor->Obj.cchParent == off);
+ memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
+ }
+ kFsCacheObjRelease(g_pFsCache, pPathObj);
+ return;
+ }
+ }
+ else
+ {
+ if ((size_t)pPathObj->cchName + 1 < cbFull)
+ {
+ memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
+ pszFull[pPathObj->cchName] = '/';
+ pszFull[pPathObj->cchName + 1] = '\0';
+
+ kFsCacheObjRelease(g_pFsCache, pPathObj);
+ return;
+ }
+ }
+
+ /* do fallback. */
+ kHlpAssertFailed();
+ kFsCacheObjRelease(g_pFsCache, pPathObj);
+ }
+
+ nt_fullpath(pszPath, pszFull, cbFull);
+}
+
/**
* Helper for getting the extension of a UTF-16 path.
@@ -2637,7 +3231,7 @@ static void __cdecl kwSandbox_msvcrt_terminate(void)
/** CRT - _onexit */
static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
{
- if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
+ //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
{
PKWEXITCALLACK pCallback;
KW_LOG(("_onexit(%p)\n", pfnFunc));
@@ -2662,7 +3256,7 @@ static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
/** CRT - atexit */
static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
{
- if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
+ //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
{
PKWEXITCALLACK pCallback;
kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
@@ -3026,7 +3620,6 @@ static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
while (cNew < cMin)
cNew += 256;
-
pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
if (pvNew)
{
@@ -3968,6 +4561,69 @@ static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3,
}
+#ifndef NDEBUG
+/*
+ * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
+ */
+# if K_ARCH == K_ARCH_X86_32
+static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW at 16";
+# else
+static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
+# endif
+typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
+typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
+typedef struct KWCXINTERCEPTORENTRY
+{
+ PFNINVOKECOMPILERPASSW pfnOrg;
+ PKWMODULE pModule;
+ PFNINVOKECOMPILERPASSW pfnWrap;
+} KWCXINTERCEPTORENTRY;
+
+static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
+ KWCXINTERCEPTORENTRY *pEntry)
+{
+ int i;
+ KIPTR rcExit;
+ KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
+ &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
+ for (i = 0; i < cArgs; i++)
+ KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
+
+ rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
+
+ KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
+ return rcExit;
+}
+
+static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
+static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
+static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
+
+static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
+{
+ { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
+ { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
+ { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
+};
+
+static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
+{
+ return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
+}
+
+static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
+{
+ return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
+}
+
+static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
+{
+ return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
+}
+
+#endif /* !NDEBUG */
+
+
/** Kernel32 - GetProcAddress() */
static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
{
@@ -3984,7 +4640,7 @@ static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR psz
KLDRADDR uValue;
int rc = kLdrModQuerySymbol(pMod->pLdrMod,
pMod->fNative ? NULL : pMod->u.Manual.pvBits,
- pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pvLoad,
+ pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
KU32_MAX /*iSymbol*/,
pszProc,
kHlpStrLen(pszProc),
@@ -3994,15 +4650,60 @@ static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR psz
NULL /*pfKind*/);
if (rc == 0)
{
- static int s_cDbgGets = 0;
- s_cDbgGets++;
- KW_LOG(("GetProcAddress(%s, %s) -> %p [%d]\n", pMod->pszPath, pszProc, (KUPTR)uValue, s_cDbgGets));
- kwLdrModuleRelease(pMod);
- //if (s_cGets >= 3)
- // return (FARPROC)kwSandbox_BreakIntoDebugger;
- return (FARPROC)(KUPTR)uValue;
- }
-
+ //static int s_cDbgGets = 0;
+ KU32 cchProc = (KU32)kHlpStrLen(pszProc);
+ KU32 i = g_cSandboxGetProcReplacements;
+ while (i-- > 0)
+ if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
+ && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
+ {
+ if ( !g_aSandboxGetProcReplacements[i].pszModule
+ || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
+ {
+ if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
+ || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
+ < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
+ {
+ uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
+ KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
+ }
+ kwLdrModuleRelease(pMod);
+ return (FARPROC)(KUPTR)uValue;
+ }
+ }
+
+#ifndef NDEBUG
+ /* Intercept the compiler pass method, dumping arguments. */
+ if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
+ {
+ KU32 i;
+ for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
+ if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
+ {
+ uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
+ KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
+ break;
+ }
+ if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
+ while (i-- > 0)
+ if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
+ {
+ g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
+ g_aCxInterceptorEntries[i].pModule = pMod;
+ uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
+ KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
+ break;
+ }
+ }
+#endif
+ KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
+ kwLdrModuleRelease(pMod);
+ //s_cDbgGets++;
+ //if (s_cGets >= 3)
+ // return (FARPROC)kwSandbox_BreakIntoDebugger;
+ return (FARPROC)(KUPTR)uValue;
+ }
+
KWFS_TODO();
SetLastError(ERROR_PROC_NOT_FOUND);
kwLdrModuleRelease(pMod);
@@ -4244,11 +4945,12 @@ static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredA
if (pHandle)
{
pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
+ pHandle->cRefs = 1;
pHandle->offFile = 0;
pHandle->hHandle = hFile;
pHandle->dwDesiredAccess = dwDesiredAccess;
pHandle->u.pTempFile = pTempFile;
- if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle))
+ if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
{
pTempFile->cActiveHandles++;
kHlpAssert(pTempFile->cActiveHandles >= 1);
@@ -4386,104 +5088,147 @@ static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAc
#endif /* WITH_TEMP_MEMORY_FILES */
-
/**
- * Checks if the file extension indicates that the file/dir is something we
- * ought to cache.
+ * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
*
- * @returns K_TRUE if cachable, K_FALSE if not.
- * @param pszExt The kHlpGetExt result.
+ * @returns K_TRUE if cacheable, K_FALSE if not.
+ * @param wcFirst The first extension character.
+ * @param wcSecond The second extension character.
+ * @param wcThird The third extension character.
* @param fAttrQuery Set if it's for an attribute query, clear if for
* file creation.
*/
-static KBOOL kwFsIsCachableExtensionA(const char *pszExt, KBOOL fAttrQuery)
+static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
{
- char const chFirst = *pszExt;
-
/* C++ header without an extension or a directory. */
- if (chFirst == '\0')
+ if (wcFirst == '\0')
{
/** @todo exclude temporary files... */
return K_TRUE;
}
/* C Header: .h */
- if (chFirst == 'h' || chFirst == 'H')
+ if (wcFirst == 'h' || wcFirst == 'H')
{
- char chThird;
- char const chSecond = pszExt[1];
- if (chSecond == '\0')
+ if (wcSecond == '\0')
return K_TRUE;
- chThird = pszExt[2];
/* C++ Header: .hpp, .hxx */
- if ( (chSecond == 'p' || chSecond == 'P')
- && (chThird == 'p' || chThird == 'P')
- && pszExt[3] == '\0')
+ if ( (wcSecond == 'p' || wcSecond == 'P')
+ && (wcThird == 'p' || wcThird == 'P'))
return K_TRUE;
- if ( (chSecond == 'x' || chSecond == 'X')
- && (chThird == 'x' || chThird == 'X')
- && pszExt[3] == '\0')
+ if ( (wcSecond == 'x' || wcSecond == 'X')
+ && (wcThird == 'x' || wcThird == 'X'))
return K_TRUE;
}
/* Misc starting with i. */
- else if (chFirst == 'i' || chFirst == 'I')
+ else if (wcFirst == 'i' || wcFirst == 'I')
{
- char const chSecond = pszExt[1];
- if (chSecond != '\0')
+ if (wcSecond != '\0')
{
- if (chSecond == 'n' || chSecond == 'N')
+ if (wcSecond == 'n' || wcSecond == 'N')
{
- char const chThird = pszExt[2];
-
/* C++ inline header: .inl */
- if ( (chThird == 'l' || chThird == 'L')
- && pszExt[3] == '\0')
+ if (wcThird == 'l' || wcThird == 'L')
return K_TRUE;
/* Assembly include file: .inc */
- if ( (chThird == 'c' || chThird == 'C')
- && pszExt[3] == '\0')
+ if (wcThird == 'c' || wcThird == 'C')
return K_TRUE;
}
}
}
/* Assembly header: .mac */
- else if (chFirst == 'm' || chFirst == 'M')
+ else if (wcFirst == 'm' || wcFirst == 'M')
+ {
+ if (wcSecond == 'a' || wcSecond == 'A')
+ {
+ if (wcThird == 'c' || wcThird == 'C')
+ return K_TRUE;
+ }
+ }
+#ifdef WITH_PCH_CACHING
+ /* Precompiled header: .pch */
+ else if (wcFirst == 'p' || wcFirst == 'P')
+ {
+ if (wcSecond == 'c' || wcSecond == 'C')
+ {
+ if (wcThird == 'h' || wcThird == 'H')
+ return !g_Sandbox.fNoPchCaching;
+ }
+ }
+#endif
+#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
+ /* Linker - Object file: .obj */
+ if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
{
- char const chSecond = pszExt[1];
- if (chSecond == 'a' || chSecond == 'A')
+ if (wcSecond == 'b' || wcSecond == 'B')
{
- char const chThird = pszExt[2];
- if ( (chThird == 'c' || chThird == 'C')
- && pszExt[3] == '\0')
+ if (wcThird == 'j' || wcThird == 'J')
return K_TRUE;
}
}
+#endif
else if (fAttrQuery)
{
/* Dynamic link library: .dll */
- if (chFirst == 'd' || chFirst == 'D')
+ if (wcFirst == 'd' || wcFirst == 'D')
{
- char const chSecond = pszExt[1];
- if (chSecond == 'l' || chSecond == 'L')
+ if (wcSecond == 'l' || wcSecond == 'L')
{
- char const chThird = pszExt[2];
- if (chThird == 'l' || chThird == 'L')
+ if (wcThird == 'l' || wcThird == 'L')
return K_TRUE;
}
}
/* Executable file: .exe */
- else if (chFirst == 'e' || chFirst == 'E')
+ else if (wcFirst == 'e' || wcFirst == 'E')
{
- char const chSecond = pszExt[1];
- if (chSecond == 'x' || chSecond == 'X')
+ if (wcSecond == 'x' || wcSecond == 'X')
{
- char const chThird = pszExt[2];
- if (chThird == 'e' || chThird == 'e')
+ if (wcThird == 'e' || wcThird == 'E')
return K_TRUE;
}
}
+ /* Response file: .rsp */
+ else if (wcFirst == 'r' || wcFirst == 'R')
+ {
+ if (wcSecond == 's' || wcSecond == 'S')
+ {
+ if (wcThird == 'p' || wcThird == 'P')
+ return !g_Sandbox.fNoPchCaching;
+ }
+ }
+ /* Linker: */
+ if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
+ {
+ /* Object file: .obj */
+ if (wcFirst == 'o' || wcFirst == 'O')
+ {
+ if (wcSecond == 'b' || wcSecond == 'B')
+ {
+ if (wcThird == 'j' || wcThird == 'J')
+ return K_TRUE;
+ }
+ }
+ /* Library file: .lib */
+ else if (wcFirst == 'l' || wcFirst == 'L')
+ {
+ if (wcSecond == 'i' || wcSecond == 'I')
+ {
+ if (wcThird == 'b' || wcThird == 'B')
+ return K_TRUE;
+ }
+ }
+ /* Linker definition file: .def */
+ else if (wcFirst == 'd' || wcFirst == 'D')
+ {
+ if (wcSecond == 'e' || wcSecond == 'E')
+ {
+ if (wcThird == 'f' || wcThird == 'F')
+ return K_TRUE;
+ }
+ }
+ }
}
return K_FALSE;
@@ -4491,6 +5236,34 @@ static KBOOL kwFsIsCachableExtensionA(const char *pszExt, KBOOL fAttrQuery)
/**
+ * Checks if the file extension indicates that the file/dir is something we
+ * ought to cache.
+ *
+ * @returns K_TRUE if cachable, K_FALSE if not.
+ * @param pszExt The kHlpGetExt result.
+ * @param fAttrQuery Set if it's for an attribute query, clear if for
+ * file creation.
+ */
+static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
+{
+ wchar_t const wcFirst = *pszExt;
+ if (wcFirst)
+ {
+ wchar_t const wcSecond = pszExt[1];
+ if (wcSecond)
+ {
+ wchar_t const wcThird = pszExt[2];
+ if (pszExt[3] == '\0')
+ return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
+ return K_FALSE;
+ }
+ return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
+ }
+ return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
+}
+
+
+/**
* Checks if the extension of the given UTF-16 path indicates that the file/dir
* should be cached.
*
@@ -4499,25 +5272,16 @@ static KBOOL kwFsIsCachableExtensionA(const char *pszExt, KBOOL fAttrQuery)
* @param fAttrQuery Set if it's for an attribute query, clear if for
* file creation.
*/
-static KBOOL kwFsIsCachablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
+static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
{
- /*
- * Extract the extension, check that it's in the applicable range, roughly
- * convert it to ASCII/ANSI, and feed it to kwFsIsCachableExtensionA for
- * the actual check. This avoids a lot of code duplication.
- */
- wchar_t wc;
- char szExt[4];
KSIZE cwcExt;
wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
switch (cwcExt)
{
- case 3: if ((wchar_t)(szExt[2] = (char)(wc = pwszExt[2])) == wc) { /*likely*/ } else break;
- case 2: if ((wchar_t)(szExt[1] = (char)(wc = pwszExt[1])) == wc) { /*likely*/ } else break;
- case 1: if ((wchar_t)(szExt[0] = (char)(wc = pwszExt[0])) == wc) { /*likely*/ } else break;
- case 0:
- szExt[cwcExt] = '\0';
- return kwFsIsCachableExtensionA(szExt, fAttrQuery);
+ case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
+ case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
+ case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
+ case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
}
return K_FALSE;
}
@@ -4576,50 +5340,55 @@ static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
if (GetFileSizeEx(hFile, &cbFile))
{
if ( cbFile.QuadPart >= 0
- && cbFile.QuadPart < 16*1024*1024)
+#ifdef WITH_PCH_CACHING
+ && ( cbFile.QuadPart < 16*1024*1024
+ || ( cbFile.QuadPart < 96*1024*1024
+ && pFsObj->cchName > 4
+ && !g_Sandbox.fNoPchCaching
+ && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
+#endif
+ )
{
KU32 cbCache = (KU32)cbFile.QuadPart;
- KU8 *pbCache = (KU8 *)kHlpAlloc(cbCache);
- if (pbCache)
+ HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
+ 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
+ if (hMapping != NULL)
{
- DWORD cbActually = 0;
- if ( ReadFile(hFile, pbCache, cbCache, &cbActually, NULL)
- && cbActually == cbCache)
+ KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
+ if (pbCache)
{
- LARGE_INTEGER offZero;
- offZero.QuadPart = 0;
- if (SetFilePointerEx(hFile, offZero, NULL /*poffNew*/, FILE_BEGIN))
+ /*
+ * Create the cached file object.
+ */
+ PKFSWCACHEDFILE pCachedFile;
+ KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
+ pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
+ sizeof(*pCachedFile) + cbPath);
+ if (pCachedFile)
{
- /*
- * Create the cached file object.
- */
- PKFSWCACHEDFILE pCachedFile;
- KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
- pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
- sizeof(*pCachedFile) + cbPath);
- if (pCachedFile)
- {
- pCachedFile->hCached = hFile;
- pCachedFile->cbCached = cbCache;
- pCachedFile->pbCached = pbCache;
- pCachedFile->pFsObj = pFsObj;
- kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
- kFsCacheObjRetain(pFsObj);
- return pCachedFile;
- }
-
- KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
+ pCachedFile->hCached = hFile;
+ pCachedFile->hSection = hMapping;
+ pCachedFile->cbCached = cbCache;
+ pCachedFile->pbCached = pbCache;
+ pCachedFile->pFsObj = pFsObj;
+ kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
+ kFsCacheObjRetain(pFsObj);
+
+ g_cReadCachedFiles++;
+ g_cbReadCachedFiles += cbCache;
+
+ KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
+ return pCachedFile;
}
- else
- KWFS_LOG(("Failed to seek to start of cached file! err=%u\n", GetLastError()));
+
+ KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
}
else
- KWFS_LOG(("Failed to read %#x bytes into cache! err=%u cbActually=%#x\n",
- cbCache, GetLastError(), cbActually));
- kHlpFree(pbCache);
+ KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
+ CloseHandle(hMapping);
}
else
- KWFS_LOG(("Failed to allocate %#x bytes for cache!\n", cbCache));
+ KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
}
else
KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
@@ -4635,12 +5404,52 @@ static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
/**
+ * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
+ */
+static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
+ KBOOL fIsFileHandle, HANDLE *phFile)
+{
+ HANDLE hProcSelf = GetCurrentProcess();
+ if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
+ hProcSelf, phFile,
+ dwDesiredAccess, fInheritHandle,
+ 0 /*dwOptions*/))
+ {
+ /*
+ * Create handle table entry for the duplicate handle.
+ */
+ PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
+ if (pHandle)
+ {
+ pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
+ pHandle->cRefs = 1;
+ pHandle->offFile = 0;
+ pHandle->hHandle = *phFile;
+ pHandle->dwDesiredAccess = dwDesiredAccess;
+ pHandle->u.pCachedFile = pCachedFile;
+ if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
+ return K_TRUE;
+
+ kHlpFree(pHandle);
+ }
+ else
+ KWFS_LOG(("Out of memory for handle!\n"));
+
+ CloseHandle(*phFile);
+ *phFile = INVALID_HANDLE_VALUE;
+ }
+ else
+ KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
+ return K_FALSE;
+}
+
+
+/**
* Kernel32 - Common code for CreateFileW and CreateFileA.
*/
static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
{
*phFile = INVALID_HANDLE_VALUE;
- kHlpAssert(pFsObj->fHaveStats);
/*
* At the moment we only handle existing files.
@@ -4648,39 +5457,12 @@ static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL
if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
{
PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
+ kHlpAssert(pFsObj->fHaveStats);
if ( pCachedFile != NULL
|| (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
{
- HANDLE hProcSelf = GetCurrentProcess();
- if (DuplicateHandle(hProcSelf, pCachedFile->hCached,
- hProcSelf, phFile,
- dwDesiredAccess, fInheritHandle,
- 0 /*dwOptions*/))
- {
- /*
- * Create handle table entry for the duplicate handle.
- */
- PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
- if (pHandle)
- {
- pHandle->enmType = KWHANDLETYPE_FSOBJ_READ_CACHE;
- pHandle->offFile = 0;
- pHandle->hHandle = *phFile;
- pHandle->dwDesiredAccess = dwDesiredAccess;
- pHandle->u.pCachedFile = pCachedFile;
- if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle))
- return K_TRUE;
-
- kHlpFree(pHandle);
- }
- else
- KWFS_LOG(("Out of memory for handle!\n"));
-
- CloseHandle(*phFile);
- *phFile = INVALID_HANDLE_VALUE;
- }
- else
- KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
+ if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
+ return K_TRUE;
}
}
/** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
@@ -4696,6 +5478,10 @@ static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dw
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
HANDLE hFile;
+
+ /*
+ * Check for include files and similar that we do read-only caching of.
+ */
if (dwCreationDisposition == FILE_OPEN_IF)
{
if ( dwDesiredAccess == GENERIC_READ
@@ -4708,7 +5494,7 @@ static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dw
&& pSecAttrs->lpSecurityDescriptor == NULL ) )
{
const char *pszExt = kHlpGetExt(pszFilename);
- if (kwFsIsCachableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
+ if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
{
KFSLOOKUPERROR enmError;
PKFSOBJ pFsObj;
@@ -4752,8 +5538,16 @@ static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dw
}
}
+ /*
+ * Okay, normal.
+ */
hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
+ || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
+ }
KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
return hFile;
}
@@ -4767,7 +5561,9 @@ static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD
HANDLE hFile;
#ifdef WITH_TEMP_MEMORY_FILES
- /* First check for temporary files (cl.exe only). */
+ /*
+ * Check for temporary files (cl.exe only).
+ */
if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
&& !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
&& !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
@@ -4779,7 +5575,9 @@ static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD
}
#endif
- /* Then check for include files and similar. */
+ /*
+ * Check for include files and similar that we do read-only caching of.
+ */
if (dwCreationDisposition == FILE_OPEN_IF)
{
if ( dwDesiredAccess == GENERIC_READ
@@ -4791,14 +5589,44 @@ static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD
|| ( pSecAttrs->nLength == sizeof(*pSecAttrs)
&& pSecAttrs->lpSecurityDescriptor == NULL ) )
{
- if (kwFsIsCachablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
+ if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
{
- /** @todo rewrite in pure UTF-16. */
- char szTmp[2048];
- KSIZE cch = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
- if (cch < sizeof(szTmp))
- return kwSandbox_Kernel32_CreateFileA(szTmp, dwDesiredAccess, dwShareMode, pSecAttrs,
- dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+ KFSLOOKUPERROR enmError;
+ PKFSOBJ pFsObj;
+ kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
+
+ pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
+ if (pFsObj)
+ {
+ KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
+ &hFile);
+ kFsCacheObjRelease(g_pFsCache, pFsObj);
+ if (fRc)
+ {
+ KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
+ return hFile;
+ }
+ }
+ /* These are for nasm and yasm style header searching. Cache will
+ already have checked the directories for the file, no need to call
+ CreateFile to do it again. */
+ else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
+ {
+ KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
+ return INVALID_HANDLE_VALUE;
+ }
+ else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
+ || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
+ {
+ KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /* fallback */
+ hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
+ dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+ KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
+ return hFile;
}
}
else
@@ -4813,8 +5641,17 @@ static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD
}
else
KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
+
+ /*
+ * Okay, normal.
+ */
hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
+ || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
+ }
KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
return hFile;
}
@@ -4841,8 +5678,9 @@ static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove,
case KWHANDLETYPE_TEMP_FILE:
cbFile = pHandle->u.pTempFile->cbFile;
break;
- case KWHANDLETYPE_TEMP_FILE_MAPPING:
#endif
+ case KWHANDLETYPE_TEMP_FILE_MAPPING:
+ case KWHANDLETYPE_OUTPUT_BUF:
default:
kHlpAssertFailed();
SetLastError(ERROR_INVALID_FUNCTION);
@@ -4890,12 +5728,13 @@ static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove,
}
if (pcbMoveHi)
*pcbMoveHi = (KU64)offMove >> 32;
- KWFS_LOG(("SetFilePointer(%p) -> %#llx [cached]\n", hFile, offMove));
+ KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
+ pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
SetLastError(NO_ERROR);
return (KU32)offMove;
}
}
- KWFS_LOG(("SetFilePointer(%p)\n", hFile));
+ KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
}
@@ -4922,8 +5761,9 @@ static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEG
case KWHANDLETYPE_TEMP_FILE:
cbFile = pHandle->u.pTempFile->cbFile;
break;
- case KWHANDLETYPE_TEMP_FILE_MAPPING:
#endif
+ case KWHANDLETYPE_TEMP_FILE_MAPPING:
+ case KWHANDLETYPE_OUTPUT_BUF:
default:
kHlpAssertFailed();
SetLastError(ERROR_INVALID_FUNCTION);
@@ -4971,7 +5811,8 @@ static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEG
}
if (poffNew)
poffNew->QuadPart = offMyMove;
- KWFS_LOG(("SetFilePointerEx(%p) -> TRUE, %#llx [cached]\n", hFile, offMyMove));
+ KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
+ pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
return TRUE;
}
}
@@ -4984,7 +5825,9 @@ static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEG
static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
LPOVERLAPPED pOverlapped)
{
+ BOOL fRet;
KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
+ g_cReadFileCalls++;
kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
if (idxHandle < g_Sandbox.cHandles)
{
@@ -4999,8 +5842,6 @@ static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DW
KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
if (cbActually > cbToRead)
cbActually = cbToRead;
- else if (cbActually < cbToRead) // debug debug debug
- kHlpMemSet((KU8 *)pvBuffer + cbActually, '\0', cbToRead - cbActually); // debug debug debug
#ifdef WITH_HASH_MD5_CACHE
if (g_Sandbox.pHashHead)
@@ -5018,6 +5859,10 @@ static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DW
kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
*pcbActuallyRead = cbActually;
+ g_cbReadFileFromReadCached += cbActually;
+ g_cbReadFileTotal += cbActually;
+ g_cReadFileFromReadCached++;
+
KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
return TRUE;
}
@@ -5076,12 +5921,17 @@ static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DW
kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
*pcbActuallyRead = cbActually;
+ g_cbReadFileTotal += cbActually;
+ g_cbReadFileFromInMemTemp += cbActually;
+ g_cReadFileFromInMemTemp++;
+
KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
return TRUE;
}
+#endif /* WITH_TEMP_MEMORY_FILES */
case KWHANDLETYPE_TEMP_FILE_MAPPING:
-#endif /* WITH_TEMP_MEMORY_FILES */
+ case KWHANDLETYPE_OUTPUT_BUF:
default:
kHlpAssertFailed();
SetLastError(ERROR_INVALID_FUNCTION);
@@ -5091,8 +5941,11 @@ static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DW
}
}
- KWFS_LOG(("ReadFile(%p)\n", hFile));
- return ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
+ fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
+ if (fRet && pcbActuallyRead)
+ g_cbReadFileTotal += *pcbActuallyRead;
+ KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
+ return fRet;
}
@@ -5115,8 +5968,139 @@ static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer,
return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
}
-#ifdef WITH_TEMP_MEMORY_FILES
+#ifdef WITH_STD_OUT_ERR_BUFFERING
+
+/**
+ * Write something to a handle, making sure everything is actually written.
+ *
+ * @param hHandle Where to write it to.
+ * @param pchBuf What to write
+ * @param cchToWrite How much to write.
+ */
+static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
+{
+ if (cchToWrite > 0)
+ {
+ DWORD cchWritten = 0;
+ if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
+ {
+ if (cchWritten == cchToWrite)
+ { /* likely */ }
+ else
+ {
+ do
+ {
+ pchBuf += cchWritten;
+ cchToWrite -= cchWritten;
+ cchWritten = 0;
+ } while ( cchToWrite > 0
+ && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
+ }
+ }
+ else
+ kHlpAssertFailed();
+ }
+}
+
+
+/**
+ * Worker for WriteFile when the output isn't going to the console.
+ *
+ * @param pSandbox The sandbox.
+ * @param pOutBuf The output buffer.
+ * @param pchBuffer What to write.
+ * @param cchToWrite How much to write.
+ */
+static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
+{
+ if (pOutBuf->u.Fully.cchBufAlloc > 0)
+ { /* likely */ }
+ else
+ {
+ /* No realloc, max size is 64KB. */
+ pOutBuf->u.Fully.cchBufAlloc = 0x10000;
+ pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
+ if (!pOutBuf->u.Fully.pchBuf)
+ {
+ while ( !pOutBuf->u.Fully.pchBuf
+ && pOutBuf->u.Fully.cchBufAlloc > 64)
+ {
+ pOutBuf->u.Fully.cchBufAlloc /= 2;
+ pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
+ }
+ if (!pOutBuf->u.Fully.pchBuf)
+ {
+ pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
+ pOutBuf->u.Fully.pchBuf = pOutBuf->abPadding;
+ }
+ }
+ }
+
+ /*
+ * Special case: Output ends with newline and fits in the buffer.
+ */
+ if ( cchToWrite > 1
+ && pchBuffer[cchToWrite - 1] == '\n'
+ && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
+ {
+ kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
+ pOutBuf->u.Fully.cchBuf += cchToWrite;
+ }
+ else
+ {
+ /*
+ * Work thru the text line by line, flushing the buffer when
+ * appropriate. The buffer is not a line buffer here, it's a
+ * full buffer.
+ */
+ while (cchToWrite > 0)
+ {
+ char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
+ KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
+ if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
+ {
+ kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
+ pOutBuf->u.Fully.cchBuf += cchLine;
+ }
+ /*
+ * Option one: Flush the buffer and the current line.
+ *
+ * We choose this one when the line won't ever fit, or when we have
+ * an incomplete line in the buffer.
+ */
+ else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
+ || pOutBuf->u.Fully.cchBuf == 0
+ || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
+ {
+ KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
+ if (pOutBuf->u.Fully.cchBuf > 0)
+ {
+ kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
+ pOutBuf->u.Fully.cchBuf = 0;
+ }
+ kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
+ }
+ /*
+ * Option two: Only flush the lines in the buffer.
+ */
+ else
+ {
+ KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
+ kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
+ kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
+ pOutBuf->u.Fully.cchBuf = cchLine;
+ }
+ /* advance */
+ pchBuffer += cchLine;
+ cchToWrite -= cchLine;
+ }
+ }
+}
+
+#endif /* WITH_STD_OUT_ERR_BUFFERING */
+
+#ifdef WITH_TEMP_MEMORY_FILES
static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
{
KU32 cbMinFile = offFile + cbNeeded;
@@ -5171,14 +6155,18 @@ static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32
kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
return K_FALSE;
}
+#endif /* WITH_TEMP_MEMORY_FILES */
+#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
/** Kernel32 - WriteFile */
static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
LPOVERLAPPED pOverlapped)
{
+ BOOL fRet;
KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
- kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
+ g_cWriteFileCalls++;
+ kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_fCtrlC);
if (idxHandle < g_Sandbox.cHandles)
{
PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
@@ -5186,6 +6174,7 @@ static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer,
{
switch (pHandle->enmType)
{
+# ifdef WITH_TEMP_MEMORY_FILES
case KWHANDLETYPE_TEMP_FILE:
{
PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
@@ -5231,6 +6220,11 @@ static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer,
pTempFile->cbFile = pHandle->offFile;
*pcbActuallyWritten = cbToWrite;
+
+ g_cbWriteFileTotal += cbToWrite;
+ g_cbWriteFileToInMemTemp += cbToWrite;
+ g_cWriteFileToInMemTemp++;
+
KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
return TRUE;
}
@@ -5240,6 +6234,7 @@ static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer,
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
+# endif
case KWHANDLETYPE_FSOBJ_READ_CACHE:
kHlpAssertFailed();
@@ -5247,6 +6242,35 @@ static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer,
*pcbActuallyWritten = 0;
return FALSE;
+# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
+ /*
+ * Standard output & error.
+ */
+ case KWHANDLETYPE_OUTPUT_BUF:
+ {
+ PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
+ if (pOutBuf->fIsConsole)
+ {
+ kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
+ KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
+ }
+ else
+ {
+# ifdef WITH_STD_OUT_ERR_BUFFERING
+ kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
+ KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
+ pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
+# else
+ kHlpAssertFailed();
+# endif
+ }
+ if (pcbActuallyWritten)
+ *pcbActuallyWritten = cbToWrite;
+ g_cbWriteFileTotal += cbToWrite;
+ return TRUE;
+ }
+# endif
+
default:
case KWHANDLETYPE_TEMP_FILE_MAPPING:
kHlpAssertFailed();
@@ -5257,25 +6281,11 @@ static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer,
}
}
-#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
- /*
- * Check for stdout and stderr.
- */
- if ( g_Sandbox.StdErr.hOutput == hFile
- || g_Sandbox.StdOut.hOutput == hFile)
- {
- PKWCONSOLEOUTPUTLINE pLineBuf = g_Sandbox.StdErr.hOutput == hFile ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
- if (pLineBuf->fIsConsole)
- {
- kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (const char *)pvBuffer, cbToWrite);
- KWFS_LOG(("WriteFile(console) -> TRUE\n", hFile));
- return TRUE;
- }
- }
-#endif
-
- KWFS_LOG(("WriteFile(%p)\n", hFile));
- return WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
+ fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
+ if (fRet && pcbActuallyWritten)
+ g_cbWriteFileTotal += *pcbActuallyWritten;
+ KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
+ return fRet;
}
@@ -5298,6 +6308,9 @@ static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer
return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
}
+#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
+
+#ifdef WITH_TEMP_MEMORY_FILES
/** Kernel32 - SetEndOfFile; */
static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
@@ -5332,6 +6345,11 @@ static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
SetLastError(ERROR_ACCESS_DENIED);
return FALSE;
+ case KWHANDLETYPE_OUTPUT_BUF:
+ kHlpAssertFailed();
+ SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
+ return FALSE;
+
default:
case KWHANDLETYPE_TEMP_FILE_MAPPING:
kHlpAssertFailed();
@@ -5365,6 +6383,24 @@ static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
case KWHANDLETYPE_TEMP_FILE:
KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
return FILE_TYPE_DISK;
+
+ case KWHANDLETYPE_OUTPUT_BUF:
+ {
+ PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
+ DWORD fRet;
+ if (pOutBuf->fFileType != KU8_MAX)
+ {
+ fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
+ KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
+ }
+ else
+ {
+ fRet = GetFileType(hFile);
+ KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
+ }
+ return fRet;
+ }
+
}
}
}
@@ -5397,6 +6433,10 @@ static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHigh
KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
return pHandle->u.pTempFile->cbFile;
+ case KWHANDLETYPE_OUTPUT_BUF:
+ /* do default */
+ break;
+
default:
kHlpAssertFailed();
SetLastError(ERROR_INVALID_FUNCTION);
@@ -5432,6 +6472,10 @@ static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER
pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
return TRUE;
+ case KWHANDLETYPE_OUTPUT_BUF:
+ /* do default */
+ break;
+
default:
kHlpAssertFailed();
SetLastError(ERROR_INVALID_FUNCTION);
@@ -5445,11 +6489,12 @@ static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER
}
-/** Kernel32 - CreateFileMapping */
+/** Kernel32 - CreateFileMappingW */
static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
DWORD fProtect, DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow, LPCWSTR pwszName)
{
+ HANDLE hMapping;
KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
if (idxHandle < g_Sandbox.cHandles)
@@ -5469,7 +6514,7 @@ static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECUR
|| dwMaximumSizeLow == pTempFile->cbFile)
&& pwszName == NULL)
{
- HANDLE hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
+ hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
return hMapping;
}
@@ -5478,18 +6523,55 @@ static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECUR
SetLastError(ERROR_ACCESS_DENIED);
return INVALID_HANDLE_VALUE;
}
+
+ /* moc.exe benefits from this. */
+ case KWHANDLETYPE_FSOBJ_READ_CACHE:
+ {
+ PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
+ if ( ( fProtect == PAGE_READONLY
+ || fProtect == PAGE_EXECUTE_READ)
+ && dwMaximumSizeHigh == 0
+ && ( dwMaximumSizeLow == 0
+ || dwMaximumSizeLow == pCachedFile->cbCached)
+ && pwszName == NULL)
+ {
+ if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
+ K_FALSE /*fIsFileHandle*/, &hMapping))
+ { /* likely */ }
+ else
+ hMapping = NULL;
+ KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
+ return hMapping;
+ }
+
+ /* Do fallback (for .pch). */
+ kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
+ ("fProtect=%#x cb=%#x'%08x name=%p\n",
+ fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
+
+ hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
+ KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
+ hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
+ return hMapping;
+ }
+
+ /** @todo read cached memory mapped files too for moc. */
}
}
}
- KWFS_LOG(("CreateFileMappingW(%p)\n", hFile));
- return CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
+ hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
+ KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
+ hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
+ return hMapping;
}
+
/** Kernel32 - MapViewOfFile */
-static HANDLE WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
- DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
+static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
+ DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
{
+ PVOID pvRet;
KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
if (idxHandle < g_Sandbox.cHandles)
@@ -5497,12 +6579,45 @@ static HANDLE WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwD
PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
if (pHandle != NULL)
{
+ KU32 idxMapping;
+
+ /*
+ * Ensure one free entry in the mapping tracking table first,
+ * since this is common to both temporary and cached files.
+ */
+ if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
+ { /* likely */ }
+ else
+ {
+ void *pvNew;
+ KU32 cNew = g_Sandbox.cMemMappingsAlloc;
+ if (cNew)
+ cNew *= 2;
+ else
+ cNew = 32;
+ pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings));
+ if (pvNew)
+ g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
+ else
+ {
+ kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+ }
+ g_Sandbox.cMemMappingsAlloc = cNew;
+ }
+
+ /*
+ * Type specific work.
+ */
switch (pHandle->enmType)
{
case KWHANDLETYPE_FSOBJ_READ_CACHE:
case KWHANDLETYPE_TEMP_FILE:
+ case KWHANDLETYPE_OUTPUT_BUF:
+ default:
kHlpAssertFailed();
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ SetLastError(ERROR_INVALID_OPERATION);
return NULL;
case KWHANDLETYPE_TEMP_FILE_MAPPING:
@@ -5553,8 +6668,9 @@ static HANDLE WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwD
pTempFile->cMappings++;
kHlpAssert(pTempFile->cMappings == 1);
- KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pTempFile->paSegs[0].pbData));
- return pTempFile->paSegs[0].pbData;
+ pvRet = pTempFile->paSegs[0].pbData;
+ KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
+ break;
}
kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
@@ -5562,33 +6678,136 @@ static HANDLE WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwD
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
+
+ /*
+ * This is simple in comparison to the above temporary file code.
+ */
+ case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
+ {
+ PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
+ if ( dwDesiredAccess == FILE_MAP_READ
+ && offFileHigh == 0
+ && offFileLow == 0
+ && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
+ {
+ pvRet = pCachedFile->pbCached;
+ KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
+ break;
+ }
+ kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
+ dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+ }
}
+
+ /*
+ * Insert into the mapping tracking table. This is common
+ * and we should only get here with a non-NULL pvRet.
+ *
+ * Note! We could look for duplicates and do ref counting, but it's
+ * easier to just append for now.
+ */
+ kHlpAssert(pvRet != NULL);
+ idxMapping = g_Sandbox.cMemMappings;
+ kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
+
+ g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
+ g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
+ g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
+ g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
+ g_Sandbox.cMemMappings++;
+
+ return pvRet;
}
}
- KWFS_LOG(("MapViewOfFile(%p)\n", hSection));
- return MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
+ pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
+ KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
+ hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
+ return pvRet;
}
-/** @todo MapViewOfFileEx */
+/** Kernel32 - MapViewOfFileEx */
+static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
+ DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
+{
+ PVOID pvRet;
+ KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
+ kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
+ if (idxHandle < g_Sandbox.cHandles)
+ {
+ PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
+ if (pHandle != NULL)
+ {
+ switch (pHandle->enmType)
+ {
+ case KWHANDLETYPE_TEMP_FILE_MAPPING:
+ KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
+ hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
+ if (!pvMapAddr)
+ return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
+ kHlpAssertFailed();
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+
+ case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
+ KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
+ hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
+ if (!pvMapAddr)
+ return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
+ /* We can use fallback here as the handle is an actual section handle. */
+ break;
+
+ case KWHANDLETYPE_FSOBJ_READ_CACHE:
+ case KWHANDLETYPE_TEMP_FILE:
+ case KWHANDLETYPE_OUTPUT_BUF:
+ default:
+ kHlpAssertFailed();
+ SetLastError(ERROR_INVALID_OPERATION);
+ return NULL;
+ }
+ }
+ }
+
+ pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
+ KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
+ hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
+ return pvRet;
+
+}
+
/** Kernel32 - UnmapViewOfFile */
static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
{
- /* Is this one of our temporary mappings? */
- PKWFSTEMPFILE pCur = g_Sandbox.pTempFileHead;
+ /*
+ * Consult the memory mapping tracker.
+ */
+ PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
+ KU32 idxMapping = g_Sandbox.cMemMappings;
kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
- while (pCur)
- {
- if ( pCur->cMappings > 0
- && pCur->paSegs[0].pbData == (KU8 *)pvBase)
+ while (idxMapping-- > 0)
+ if (paMemMappings[idxMapping].pvMapping == pvBase)
{
- pCur->cMappings--;
- KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
+ /* Type specific stuff. */
+ if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
+ {
+ KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
+ paMemMappings[idxMapping].u.pTempFile->cMappings--;
+ }
+ else
+ KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
+
+ /* Deref and probably free it. */
+ if (--paMemMappings[idxMapping].cRefs == 0)
+ {
+ g_Sandbox.cMemMappings--;
+ if (idxMapping != g_Sandbox.cMemMappings)
+ paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
+ }
return TRUE;
}
- pCur = pCur->pNext;
- }
KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
return UnmapViewOfFile(pvBase);
@@ -5596,42 +6815,115 @@ static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
/** @todo UnmapViewOfFileEx */
-
#endif /* WITH_TEMP_MEMORY_FILES */
+
+/** Kernel32 - DuplicateHandle */
+static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
+ DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
+{
+ BOOL fRet;
+
+ /*
+ * We must catch our handles being duplicated.
+ */
+ if (hSrcProc == GetCurrentProcess())
+ {
+ KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
+ kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
+ if (idxHandle < g_Sandbox.cHandles)
+ {
+ PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
+ if (pHandle)
+ {
+ fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
+ if (fRet)
+ {
+ if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
+ {
+ pHandle->cRefs++;
+ KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
+ hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
+ pHandle->enmType, pHandle->cRefs));
+ }
+ else
+ {
+ fRet = FALSE;
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
+ hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
+ }
+ }
+ else
+ KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
+ hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
+ return fRet;
+ }
+ }
+ }
+
+ /*
+ * Not one of ours, just do what the caller asks and log it.
+ */
+ fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
+ KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
+ fInheritHandle, dwOptions, fRet, *phNew));
+ return fRet;
+}
+
+
/** Kernel32 - CloseHandle */
static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
{
BOOL fRet;
KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
- kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
- if ( idxHandle < g_Sandbox.cHandles
- && g_Sandbox.papHandles[idxHandle] != NULL)
+ kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_fCtrlC);
+ if (idxHandle < g_Sandbox.cHandles)
{
- fRet = CloseHandle(hObject);
- if (fRet)
+ PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
+ if (pHandle)
{
- PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
- g_Sandbox.papHandles[idxHandle] = NULL;
- g_Sandbox.cActiveHandles--;
+ /* Prevent the closing of the standard output and error handles. */
+ if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
+ || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
+ {
+ fRet = CloseHandle(hObject);
+ if (fRet)
+ {
+ PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
+ g_Sandbox.papHandles[idxHandle] = NULL;
+ g_Sandbox.cActiveHandles--;
+ kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
+ if (--pHandle->cRefs == 0)
+ {
#ifdef WITH_TEMP_MEMORY_FILES
- if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
+ if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
+ {
+ kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
+ pHandle->u.pTempFile->cActiveHandles--;
+ }
+#endif
+ kHlpFree(pHandle);
+ KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
+ }
+ else
+ KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
+ }
+ else
+ KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
+ }
+ else
{
- kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
- pHandle->u.pTempFile->cActiveHandles--;
+ KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
+ hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
+ fRet = TRUE;
}
-#endif
- kHlpFree(pHandle);
- KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle]\n", hObject));
+ return fRet;
}
- else
- KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
- }
- else
- {
- KWFS_LOG(("CloseHandle(%p)\n", hObject));
- fRet = CloseHandle(hObject);
}
+
+ fRet = CloseHandle(hObject);
+ KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
return fRet;
}
@@ -5641,7 +6933,7 @@ static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
{
DWORD fRet;
const char *pszExt = kHlpGetExt(pszFilename);
- if (kwFsIsCachableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
+ if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
{
KFSLOOKUPERROR enmError;
PKFSOBJ pFsObj;
@@ -5674,7 +6966,7 @@ static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
{
DWORD fRet;
- if (kwFsIsCachablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
+ if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
{
KFSLOOKUPERROR enmError;
PKFSOBJ pFsObj;
@@ -5709,7 +7001,7 @@ static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
{
DWORD cwcRet;
- if (kwFsIsCachablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
+ if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
{
KFSLOOKUPERROR enmError;
PKFSOBJ pObj;
@@ -5814,9 +7106,8 @@ static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf,
{
off += cwcWritten;
cwcWritten = 0;
- }
- while ( off < cwcToWrite
- && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
+ } while ( off < cwcToWrite
+ && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
kHlpAssert(off == cwcWritten);
}
}
@@ -5836,6 +7127,7 @@ static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
{
if (pSandbox->Combined.cwcBuf > 0)
{
+ KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
pSandbox->Combined.cwcBuf = 0;
}
@@ -5862,7 +7154,7 @@ static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pw
}
else
{
- memcpy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
+ kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
pSandbox->Combined.cwcBuf += cwcBuf;
}
}
@@ -5873,30 +7165,38 @@ static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pw
*
* @param pSandbox The sandbox.
* @param pLineBuf The line buffer.
+ * @param pszName The line buffer name (for logging)
*/
-static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLineBuf)
+static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
{
- if (pLineBuf->cwcBuf > 0)
+ if (pLineBuf->fIsConsole)
{
- if (pLineBuf->fIsConsole)
+ if (pLineBuf->u.Con.cwcBuf > 0)
{
- if (pLineBuf->cwcBuf < pLineBuf->cwcBufAlloc)
+ KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
+
+ if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
{
- pLineBuf->pwcBuf[pLineBuf->cwcBuf++] = '\n';
- kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->pwcBuf, pLineBuf->cwcBuf, K_FALSE /*fBrokenLine*/);
+ pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
+ kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
}
else
{
- kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->pwcBuf, pLineBuf->cwcBuf, K_TRUE /*fBrokenLine*/);
+ kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
}
- pLineBuf->cwcBuf = 0;
- }
- else
- {
- kHlpAssertFailed();
+ pLineBuf->u.Con.cwcBuf = 0;
}
}
+#ifdef WITH_STD_OUT_ERR_BUFFERING
+ else if (pLineBuf->u.Fully.cchBuf > 0)
+ {
+ KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
+
+ kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
+ pLineBuf->u.Fully.cchBuf = 0;
+ }
+#endif
}
@@ -5909,41 +7209,92 @@ static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
{
/*
* First do the cl.exe source file supression trick, if applicable.
+ * The output ends up on CONOUT$ if either StdOut or StdErr is a console
+ * handle.
*/
- if ( pSandbox->Combined.cwcBuf >= 3
- && pSandbox->StdOut.cwcBuf == 0
- && pSandbox->StdErr.cwcBuf == 0
- && pSandbox->Combined.cFlushes == 0
- && pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
+ if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
+ && pSandbox->Combined.cFlushes == 0)
{
- KI32 off = pSandbox->Combined.cwcBuf - 1;
- if (pSandbox->Combined.wszBuf[off] == '\n')
+ if ( pSandbox->StdOut.fIsConsole
+ || pSandbox->StdErr.fIsConsole)
{
- KBOOL fOk = K_TRUE;
- while (off-- > 0)
+ if ( pSandbox->Combined.cwcBuf >= 3
+ && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
+ && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
{
- wchar_t const wc = pSandbox->Combined.wszBuf[off];
- if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
- { /* likely */ }
- else
+ KI32 off = pSandbox->Combined.cwcBuf - 1;
+ if (pSandbox->Combined.wszBuf[off] == '\n')
{
- fOk = K_FALSE;
- break;
+ KBOOL fOk = K_TRUE;
+ while (off-- > 0)
+ {
+ wchar_t const wc = pSandbox->Combined.wszBuf[off];
+ if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
+ { /* likely */ }
+ else
+ {
+ fOk = K_FALSE;
+ break;
+ }
+ }
+ if (fOk)
+ {
+ KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
+ pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
+ pSandbox->Combined.cwcBuf = 0;
+ return;
+ }
}
+ KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
+ pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
}
- if (fOk)
+ }
+#ifdef WITH_STD_OUT_ERR_BUFFERING
+ /*
+ * Otherwise, it goes to standard output (redirected).
+ */
+ else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
+ && pSandbox->StdOut.u.Fully.cchBuf >= 3)
+ {
+ char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
+ KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
+ kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
+
+ if (pchBuf[off] == '\n')
{
- pSandbox->Combined.cwcBuf = 0;
- return;
+ KBOOL fOk = K_TRUE;
+ if (pchBuf[off - 1] == '\r')
+ off--;
+ while (off-- > 0)
+ {
+ char const ch = pchBuf[off];
+ if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
+ { /* likely */ }
+ else
+ {
+ fOk = K_FALSE;
+ break;
+ }
+ }
+ if (fOk)
+ {
+ KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
+ pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
+ pSandbox->StdOut.u.Fully.cchBuf = 0;
+ return;
+ }
}
+ KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
+ pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
}
+#endif
}
/*
* Flush the two line buffer, the the combined buffer.
*/
- kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr);
- kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut);
+ kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
+ kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
kwSandboxConsoleFlushCombined(pSandbox);
}
@@ -5956,8 +7307,9 @@ static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
* @param pwcBuffer The buffer to write.
* @param cwcToWrite The number of wchar_t's in the buffer.
*/
-static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
+static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
{
+ kHlpAssert(pLineBuf->fIsConsole);
if (cwcToWrite > 0)
{
/*
@@ -5975,34 +7327,37 @@ static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLi
if (offLastIncompleteLine < cwcToWrite)
{
/* Need to grow the line buffer? */
- KU32 cwcNeeded = offLastIncompleteLine != 0 ? offLastIncompleteLine : cchLastIncompleteLine + pLineBuf->cwcBuf;
- if (cwcNeeded > pLineBuf->cwcBufAlloc)
+ KU32 cwcNeeded = offLastIncompleteLine == 0
+ ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
+ : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
+ if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
{
void *pvNew;
- KU32 cwcNew = !pLineBuf->cwcBufAlloc ? 1024 : pLineBuf->cwcBufAlloc * 2;
+ KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
while (cwcNew < cwcNeeded)
cwcNew *= 2;
- pvNew = kHlpRealloc(pLineBuf->pwcBuf, cwcNew * sizeof(wchar_t));
+ pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
if (pvNew)
{
- pLineBuf->pwcBuf = (wchar_t *)pvNew;
- pLineBuf->cwcBufAlloc = cwcNew;
+ pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
+ pLineBuf->u.Con.cwcBufAlloc = cwcNew;
}
else
{
- pvNew = kHlpRealloc(pLineBuf->pwcBuf, cwcNeeded * sizeof(wchar_t));
+ pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
if (pvNew)
{
- pLineBuf->pwcBuf = (wchar_t *)pvNew;
- pLineBuf->cwcBufAlloc = cwcNeeded;
+ pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
+ pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
}
else
{
/* This isn't perfect, but it will have to do for now. */
- if (pLineBuf->cwcBuf > 0)
+ if (pLineBuf->u.Con.cwcBuf > 0)
{
- kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->pwcBuf, pLineBuf->cwcBuf, K_TRUE /*fBrokenLine*/);
- pLineBuf->cwcBuf = 0;
+ kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
+ K_TRUE /*fBrokenLine*/);
+ pLineBuf->u.Con.cwcBuf = 0;
}
kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
return;
@@ -6015,27 +7370,28 @@ static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLi
*/
if (offLastIncompleteLine == 0)
{
- memcpy(&pLineBuf->pwcBuf[pLineBuf->cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
- pLineBuf->cwcBuf += cwcToWrite;
+ kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
+ pLineBuf->u.Con.cwcBuf += cwcToWrite;
return;
}
}
/*
- * If there is sufficient combined buffer to handle this request, this are rather simple.
+ * If there is sufficient combined buffer to handle this request, this is rather simple.
*/
- if (pLineBuf->cwcBuf + cchLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
+ kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
+ if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
{
- if (pLineBuf->cwcBuf > 0)
+ if (pLineBuf->u.Con.cwcBuf > 0)
{
- memcpy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
- pLineBuf->pwcBuf, pLineBuf->cwcBuf * sizeof(wchar_t));
- pSandbox->Combined.cwcBuf += pLineBuf->cwcBuf;
- pLineBuf->cwcBuf = 0;
+ kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
+ pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
+ pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
+ pLineBuf->u.Con.cwcBuf = 0;
}
- memcpy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
- pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
+ kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
+ pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
pSandbox->Combined.cwcBuf += offLastIncompleteLine;
}
else
@@ -6047,39 +7403,40 @@ static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLi
KU32 off = 0;
KU32 offNextLine = 0;
- /* If there is buffered chars, we handle the first line outside the
+ /* If there are buffered chars, we handle the first line outside the
main loop. We must try our best outputting it as a complete line. */
- if (pLineBuf->cwcBuf > 0)
+ if (pLineBuf->u.Con.cwcBuf > 0)
{
while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
offNextLine++;
offNextLine++;
kHlpAssert(offNextLine <= offLastIncompleteLine);
- if (pLineBuf->cwcBuf + offNextLine + pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf))
+ if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
{
- memcpy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
- pLineBuf->pwcBuf, pLineBuf->cwcBuf * sizeof(wchar_t));
- pSandbox->Combined.cwcBuf += pLineBuf->cwcBuf;
- pLineBuf->cwcBuf = 0;
+ kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
+ pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
+ pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
+ pLineBuf->u.Con.cwcBuf = 0;
- memcpy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
+ kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
pSandbox->Combined.cwcBuf += offNextLine;
}
else
{
- KU32 cwcLeft = pLineBuf->cwcBufAlloc - pLineBuf->cwcBuf;
+ KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
if (cwcLeft > 0)
{
KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
- memcpy(&pLineBuf->pwcBuf[pLineBuf->cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
- pLineBuf->cwcBuf += cwcCopy;
+ kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
+ pLineBuf->u.Con.cwcBuf += cwcCopy;
off += cwcCopy;
}
- if (pLineBuf->cwcBuf > 0)
+ if (pLineBuf->u.Con.cwcBuf > 0)
{
- kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->pwcBuf, pLineBuf->cwcBuf, K_TRUE /*fBrokenLine*/);
- pLineBuf->cwcBuf = 0;
+ kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
+ K_TRUE /*fBrokenLine*/);
+ pLineBuf->u.Con.cwcBuf = 0;
}
if (off < offNextLine)
kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
@@ -6102,10 +7459,10 @@ static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLi
/*
* Buffer any remaining incomplete line chars.
*/
- if (offLastIncompleteLine < cwcToWrite)
+ if (cchLastIncompleteLine)
{
- memcpy(&pLineBuf->pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
- pLineBuf->cwcBuf = cchLastIncompleteLine;
+ kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
+ pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
}
}
}
@@ -6119,7 +7476,7 @@ static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLi
* @param pchBuffer What to write.
* @param cchToWrite How much to write.
*/
-static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLineBuf, const char *pchBuffer, KU32 cchToWrite)
+static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
{
/*
* Convert it to wide char and use the 'W' to do the work.
@@ -6128,6 +7485,7 @@ static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLi
KU32 cwcBuf = cchToWrite * 2 + 1;
wchar_t *pwcBufFree = NULL;
wchar_t *pwcBuf;
+ kHlpAssert(pLineBuf->fIsConsole);
if (cwcBuf <= 4096)
pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
@@ -6143,10 +7501,10 @@ static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLi
kHlpAssertFailed();
/* Flush the line buffer and combined buffer before calling WriteConsoleA. */
- if (pLineBuf->cwcBuf > 0)
+ if (pLineBuf->u.Con.cwcBuf > 0)
{
- kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->pwcBuf, pLineBuf->cwcBuf, K_TRUE /*fBroken*/);
- pLineBuf->cwcBuf = 0;
+ kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
+ pLineBuf->u.Con.cwcBuf = 0;
}
kwSandboxConsoleFlushCombined(pSandbox);
@@ -6176,8 +7534,8 @@ static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWCONSOLEOUTPUTLINE pLi
BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
PVOID pvReserved)
{
- BOOL fRc;
- PKWCONSOLEOUTPUTLINE pLineBuf;
+ BOOL fRc;
+ PKWOUTPUTSTREAMBUF pLineBuf;
kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
if (hConOutput == g_Sandbox.StdErr.hOutput)
@@ -6188,8 +7546,8 @@ BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBu
{
kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
- KWFS_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
- hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
+ KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
+ hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
if (pcbWritten)
*pcbWritten = cbToWrite;
fRc = TRUE;
@@ -6197,8 +7555,8 @@ BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBu
else
{
fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
- KWFS_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
- hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
+ KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
+ hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
}
return fRc;
}
@@ -6208,20 +7566,22 @@ BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBu
BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
PVOID pvReserved)
{
- BOOL fRc;
- PKWCONSOLEOUTPUTLINE pLineBuf;
+ BOOL fRc;
+ PKWOUTPUTSTREAMBUF pLineBuf;
kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
if (hConOutput == g_Sandbox.StdErr.hOutput)
pLineBuf = &g_Sandbox.StdErr;
- else
+ else if (hConOutput == g_Sandbox.StdOut.hOutput)
pLineBuf = &g_Sandbox.StdOut;
+ else
+ pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
if (pLineBuf->fIsConsole)
{
kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
- KWFS_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
- hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
+ KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
+ hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
if (pcwcWritten)
*pcwcWritten = cwcToWrite;
fRc = TRUE;
@@ -6229,8 +7589,8 @@ BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBu
else
{
fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
- KWFS_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
- hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
+ KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
+ hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
}
return fRc;
}
@@ -6247,36 +7607,133 @@ BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBu
*
*/
-/** Kernel32 - VirtualAlloc - for c1[xx].dll 78GB leaks. */
+#ifdef WITH_FIXED_VIRTUAL_ALLOCS
+
+/** For debug logging. */
+# ifndef NDEBUG
+static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
+{
+ MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
+ SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
+ kHlpAssert(cbMemInfo == sizeof(MemInfo));
+ if (cbMemInfo != 0)
+ KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
+ pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
+ MemInfo.BaseAddress,
+ MemInfo.AllocationBase,
+ MemInfo.RegionSize,
+ MemInfo.State,
+ MemInfo.Protect,
+ MemInfo.Type));
+}
+# else
+# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
+# endif
+
+/**
+ * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
+ *
+ * @param idxFixed The fixed allocation index to "free".
+ */
+static void kwSandboxResetFixedAllocation(KU32 idxFixed)
+{
+ BOOL fRc;
+ kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
+ fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
+ kHlpAssert(fRc); K_NOREF(fRc);
+ kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
+ g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
+}
+
+#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
+
+
+/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
+ * location (~78MB in 32-bit 2010 compiler). */
static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
{
- PVOID pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
- KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
- pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
- if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
- && pvMem)
+ PVOID pvMem;
+ if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
{
- PKWVIRTALLOC pTracker;
- kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
+ KU32 idxPreAllocated = KU32_MAX;
+
+#ifdef WITH_FIXED_VIRTUAL_ALLOCS
+ /*
+ * Look for a pre-reserved CL.exe heap allocation.
+ */
+ pvMem = NULL;
+ if ( pvAddr != 0
+ && (fAllocType & MEM_RESERVE))
+ {
+ KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
+ kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
+ while (idxFixed-- > 0)
+ if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
+ && g_aFixedVirtualAllocs[idxFixed].pvReserved)
+ {
+ if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
+ {
+ if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
+ {
+ g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
+ pvMem = pvAddr;
+ idxPreAllocated = idxFixed;
+ KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
+ pvAddr, cb, fAllocType, fProt, pvMem));
+ kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
+ SetLastError(NO_ERROR);
+ break;
+ }
+ kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
+ }
+ else
+ kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
+ pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
+ }
+ }
+ if (!pvMem)
+#endif
+ {
+ pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
+ KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
+ pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
+ if (pvAddr && pvAddr != pvMem)
+ kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
+ pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
+ }
- pTracker = g_Sandbox.pVirtualAllocHead;
- while ( pTracker
- && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
- pTracker = pTracker->pNext;
- if (!pTracker)
+ if (pvMem)
{
- DWORD dwErr = GetLastError();
- PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
- if (pTracker)
+ /*
+ * Track it.
+ */
+ PKWVIRTALLOC pTracker;
+ kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
+
+ pTracker = g_Sandbox.pVirtualAllocHead;
+ while ( pTracker
+ && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
+ pTracker = pTracker->pNext;
+ if (!pTracker)
{
- pTracker->pvAlloc = pvMem;
- pTracker->cbAlloc = cb;
- pTracker->pNext = g_Sandbox.pVirtualAllocHead;
- g_Sandbox.pVirtualAllocHead = pTracker;
+ DWORD dwErr = GetLastError();
+ PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
+ if (pTracker)
+ {
+ pTracker->pvAlloc = pvMem;
+ pTracker->cbAlloc = cb;
+ pTracker->idxPreAllocated = idxPreAllocated;
+ pTracker->pNext = g_Sandbox.pVirtualAllocHead;
+ g_Sandbox.pVirtualAllocHead = pTracker;
+ }
+ SetLastError(dwErr);
}
- SetLastError(dwErr);
}
}
+ else
+ pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
+ KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
+ pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
return pvMem;
}
@@ -6284,8 +7741,7 @@ static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWO
/** Kernel32 - VirtualFree. */
static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
{
- BOOL fRc = VirtualFree(pvAddr, cb, dwFreeType);
- KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
+ BOOL fRc;
if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
{
kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
@@ -6308,12 +7764,57 @@ static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD
pPrev->pNext = pTracker->pNext;
}
if (pTracker)
- kHlpFree(pTracker);
- else
- KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
+ {
+#ifdef WITH_FIXED_VIRTUAL_ALLOCS
+ if (pTracker->idxPreAllocated != KU32_MAX)
+ {
+ kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
+ KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
+ pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
+ kHlpFree(pTracker);
+ return TRUE;
+ }
+#endif
+
+ fRc = VirtualFree(pvAddr, cb, dwFreeType);
+ if (fRc)
+ kHlpFree(pTracker);
+ else
+ {
+ pTracker->pNext = g_Sandbox.pVirtualAllocHead;
+ g_Sandbox.pVirtualAllocHead = pTracker;
+ }
+ KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
+ return fRc;
+ }
+
+ KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
}
}
}
+
+#ifdef WITH_FIXED_VIRTUAL_ALLOCS
+ /*
+ * Protect our fixed allocations (this isn't just paranoia, btw.).
+ */
+ if (dwFreeType & MEM_RELEASE)
+ {
+ KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
+ while (idxFixed-- > 0)
+ if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
+ {
+ KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
+ idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
+ return TRUE;
+ }
+ }
+#endif
+
+ /*
+ * Not tracker or not actually free the virtual range.
+ */
+ fRc = VirtualFree(pvAddr, cb, dwFreeType);
+ KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
return fRc;
}
@@ -6385,9 +7886,9 @@ BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
* Thread/Fiber local storage leak prevention.
* Thread/Fiber local storage leak prevention.
*
- * Note! The FlsAlloc/Free causes problems for statically linked VS2010
- * code like VBoxBs3ObjConverter.exe. One thing is that we're
- * leaking these indexes, but more importantely we crash during
+ * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
+ * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
+ * we're leaking these indexes, but more importantely we crash during
* worker exit since the callback is triggered multiple times.
*/
@@ -6450,6 +7951,64 @@ BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
}
+/** Kernel32 - TlsAlloc */
+DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
+{
+ DWORD idxTls = TlsAlloc();
+ KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
+ if (idxTls != TLS_OUT_OF_INDEXES)
+ {
+ PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
+ if (pTracker)
+ {
+ kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
+ pTracker->idx = idxTls;
+ pTracker->pNext = g_Sandbox.pTlsAllocHead;
+ g_Sandbox.pTlsAllocHead = pTracker;
+ }
+ }
+
+ return idxTls;
+}
+
+/** Kernel32 - TlsFree */
+BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
+{
+ BOOL fRc = TlsFree(idxTls);
+ KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
+ if (fRc)
+ {
+ PKWLOCALSTORAGE pTracker;
+ kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
+
+ pTracker = g_Sandbox.pTlsAllocHead;
+ if (pTracker)
+ {
+ if (pTracker->idx == idxTls)
+ g_Sandbox.pTlsAllocHead = pTracker->pNext;
+ else
+ {
+ PKWLOCALSTORAGE pPrev;
+ do
+ {
+ pPrev = pTracker;
+ pTracker = pTracker->pNext;
+ } while (pTracker && pTracker->idx != idxTls);
+ if (pTracker)
+ pPrev->pNext = pTracker->pNext;
+ }
+ if (pTracker)
+ {
+ pTracker->idx = TLS_OUT_OF_INDEXES;
+ pTracker->pNext = NULL;
+ kHlpFree(pTracker);
+ }
+ }
+ }
+ return fRc;
+}
+
+
/*
*
@@ -6465,7 +8024,7 @@ BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
#ifdef WITH_HASH_MD5_CACHE
-/** Advapi32 - CryptCreateHash */
+/** AdvApi32 - CryptCreateHash */
static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
HCRYPTHASH *phHash)
{
@@ -6524,7 +8083,7 @@ static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID i
}
-/** Advapi32 - CryptHashData */
+/** AdvApi32 - CryptHashData */
static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
{
BOOL fRc;
@@ -6639,7 +8198,7 @@ static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE
}
-/** Advapi32 - CryptGetHashParam */
+/** AdvApi32 - CryptGetHashParam */
static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
{
@@ -6797,7 +8356,7 @@ static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD
}
-/** Advapi32 - CryptDestroyHash */
+/** AdvApi32 - CryptDestroyHash */
static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
{
BOOL fRc;
@@ -6846,6 +8405,341 @@ static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
/*
*
+ * Reuse crypt context.
+ * Reuse crypt context.
+ * Reuse crypt context.
+ *
+ *
+ * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
+ *
+ */
+
+#ifdef WITH_CRYPT_CTX_REUSE
+
+/** AdvApi32 - CryptAcquireContextW. */
+static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
+ DWORD dwProvType, DWORD dwFlags)
+{
+ BOOL fRet;
+
+ /*
+ * Lookup reusable context based on the input.
+ */
+ KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
+ KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
+ KU32 iCtx = g_Sandbox.cCryptCtxs;
+ while (iCtx-- > 0)
+ {
+ if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
+ && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
+ && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
+ && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
+ && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
+ && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
+ {
+ if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
+ {
+ *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
+ KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
+ pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
+ return TRUE;
+ }
+ }
+ }
+
+ /*
+ * Create it and enter it into the reused array if possible.
+ */
+ fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
+ if (fRet)
+ {
+ iCtx = g_Sandbox.cCryptCtxs;
+ if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
+ {
+ /* Try duplicate the input strings. */
+ g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
+ (cwcContainer + 1) * sizeof(wchar_t));
+ if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
+ {
+ g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
+ (cwcProvider + 1) * sizeof(wchar_t));
+ if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
+ {
+ /* Add a couple of references just to be on the safe side and all that. */
+ HCRYPTPROV hProv = *phProv;
+ if (CryptContextAddRef(hProv, NULL, 0))
+ {
+ if (CryptContextAddRef(hProv, NULL, 0))
+ {
+ /* Okay, finish the entry and return success */
+ g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
+ g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
+ g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
+ g_Sandbox.cCryptCtxs = iCtx + 1;
+
+ KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
+ pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
+ return TRUE;
+ }
+ CryptReleaseContext(hProv, 0);
+ }
+ KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
+
+ kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
+ g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
+ }
+ kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
+ g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
+ }
+ }
+ else
+ KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
+ }
+
+ KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
+ pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
+ return fRet;
+}
+
+
+/** AdvApi32 - CryptReleaseContext */
+static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
+{
+ BOOL fRet = CryptReleaseContext(hProv, dwFlags);
+ KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
+ return fRet;
+}
+
+
+/** AdvApi32 - CryptContextAddRef */
+static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
+{
+ BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
+ KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
+ return fRet;
+}
+
+#endif /* WITH_CRYPT_CTX_REUSE */
+
+/*
+ *
+ * Structured exception handling.
+ * Structured exception handling.
+ * Structured exception handling.
+ *
+ */
+#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
+
+# define EH_NONCONTINUABLE KU32_C(0x00000001)
+# define EH_UNWINDING KU32_C(0x00000002)
+# define EH_EXIT_UNWIND KU32_C(0x00000004)
+# define EH_STACK_INVALID KU32_C(0x00000008)
+# define EH_NESTED_CALL KU32_C(0x00000010)
+
+typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
+ struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
+typedef struct _EXCEPTION_REGISTRATION_RECORD
+{
+ struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
+ PFNXCPTHANDLER pfnXcptHandler;
+};
+
+
+/**
+ * Calls @a pfnHandler.
+ */
+static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
+ PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
+ PFNXCPTHANDLER pfnHandler)
+{
+# if 1
+ /* This is a more robust version that isn't subject to calling
+ convension cleanup disputes and such. */
+ KU32 uSavedEdi;
+ KU32 uSavedEsi;
+ KU32 uSavedEbx;
+ KU32 rcHandler;
+
+ __asm
+ {
+ mov [uSavedEdi], edi
+ mov [uSavedEsi], esi
+ mov [uSavedEbx], ebx
+ mov esi, esp
+ mov edi, esp
+ mov edi, [pXcptRec]
+ mov edx, [pRegRec]
+ mov eax, [pXcptCtx]
+ mov ebx, [ppRegRec]
+ mov ecx, [pfnHandler]
+ sub esp, 16
+ and esp, 0fffffff0h
+ mov [esp ], edi
+ mov [esp + 4], edx
+ mov [esp + 8], eax
+ mov [esp + 12], ebx
+ mov edi, esi
+ call ecx
+ mov esp, esi
+ cmp esp, edi
+ je stack_ok
+ int 3
+ stack_ok:
+ mov edi, [uSavedEdi]
+ mov esi, [uSavedEsi]
+ mov ebx, [uSavedEbx]
+ mov [rcHandler], eax
+ }
+ return rcHandler;
+# else
+ return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
+# endif
+}
+
+
+/**
+ * Vectored exception handler that emulates x86 chained exception handler.
+ *
+ * This is necessary because the RtlIsValidHandler check fails for self loaded
+ * code and prevents cl.exe from working. (On AMD64 we can register function
+ * tables, but on X86 cooking your own handling seems to be the only viabke
+ * alternative.)
+ *
+ * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
+ * @param pXcptPtrs The exception details.
+ */
+static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
+{
+ PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
+ KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
+ if (g_Sandbox.fRunning)
+ {
+ HANDLE const hCurProc = GetCurrentProcess();
+ PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
+ PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
+ struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
+ while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
+ {
+ /* Read the exception record in a safe manner. */
+ struct _EXCEPTION_REGISTRATION_RECORD RegRec;
+ DWORD cbActuallyRead = 0;
+ if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
+ && cbActuallyRead == sizeof(RegRec))
+ {
+ struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
+ KU32 rcHandler;
+ KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
+ RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
+ rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
+ KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
+ if (rcHandler == ExceptionContinueExecution)
+ {
+ kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
+ KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+
+ if (rcHandler == ExceptionContinueSearch)
+ kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
+ else if (rcHandler == ExceptionNestedException)
+ kHlpAssertMsgFailed(("Nested exceptions.\n"));
+ else
+ kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
+ }
+ else
+ {
+ KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
+ break;
+ }
+
+ /*
+ * Next.
+ */
+ pRegRec = RegRec.pPrevRegRec;
+ }
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+
+/** NtDll,Kernel32 - RtlUnwind */
+static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
+ PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
+{
+ PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
+ KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
+ pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
+ if (g_Sandbox.fRunning)
+ {
+ HANDLE const hCurProc = GetCurrentProcess();
+ PCONTEXT pXcptCtx = NULL;
+ struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
+
+ /*
+ * Update / create an exception record.
+ */
+ if (pXcptRec)
+ pXcptRec->ExceptionFlags |= EH_UNWINDING;
+ else
+ {
+ pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
+ kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
+ pXcptRec->ExceptionCode = STATUS_UNWIND;
+ pXcptRec->ExceptionFlags = EH_UNWINDING;
+ }
+ if (!pStopXcptRec)
+ pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
+
+ /*
+ * Walk the chain till we find pStopXctpRec.
+ */
+ while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
+ && pRegRec != NULL
+ && pRegRec != pStopXcptRec)
+ {
+ /* Read the exception record in a safe manner. */
+ struct _EXCEPTION_REGISTRATION_RECORD RegRec;
+ DWORD cbActuallyRead = 0;
+ if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
+ && cbActuallyRead == sizeof(RegRec))
+ {
+ struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
+ KU32 rcHandler;
+ KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
+ RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
+ rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
+ KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
+
+ if (rcHandler == ExceptionContinueSearch)
+ kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
+ else if (rcHandler == ExceptionCollidedUnwind)
+ kHlpAssertMsgFailed(("Implement collided unwind!\n"));
+ else
+ kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
+ }
+ else
+ {
+ KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
+ break;
+ }
+
+ /*
+ * Pop next.
+ */
+ pTib->ExceptionList = RegRec.pPrevRegRec;
+ pRegRec = RegRec.pPrevRegRec;
+ }
+ return;
+ }
+
+ RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
+}
+
+#endif /* WINDOWS + X86 */
+
+
+/*
+ *
* Misc function only intercepted while debugging.
* Misc function only intercepted while debugging.
* Misc function only intercepted while debugging.
@@ -6924,10 +8818,12 @@ KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
{ TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
{ TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
{ TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
+ { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
{ TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
#endif
{ TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
{ TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
+ { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
{ TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
{ TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
{ TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
@@ -6945,11 +8841,17 @@ KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
{ TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
{ TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
- { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc },
- { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree },
+ { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
+ { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
+ { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
+ { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
{ TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
+#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
+ { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
+#endif
+
#ifdef WITH_HASH_MD5_CACHE
{ TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
{ TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
@@ -6957,6 +8859,12 @@ KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
{ TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
#endif
+#ifdef WITH_CRYPT_CTX_REUSE
+ { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
+ { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
+ { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
+#endif
+
/*
* MS Visual C++ CRTs.
*/
@@ -7047,10 +8955,12 @@ KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
{ TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
{ TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
{ TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
+ { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
{ TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
#endif
{ TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
{ TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
+ { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
{ TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
{ TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
{ TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
@@ -7072,6 +8982,9 @@ KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
{ TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
+#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
+ { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
+#endif
/*
* MS Visual C++ CRTs.
@@ -7093,6 +9006,23 @@ KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandbox
/**
+ * Functions that needs replacing when queried by GetProcAddress.
+ */
+KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
+{
+ /*
+ * Kernel32.dll and friends.
+ */
+ { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
+ { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
+ { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
+ { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
+};
+/** Number of entries in g_aSandboxGetProcReplacements. */
+KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
+
+
+/**
* Control handler.
*
* @returns TRUE if handled, FALSE if not.
@@ -7104,26 +9034,31 @@ static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
{
case CTRL_C_EVENT:
fprintf(stderr, "kWorker: Ctrl-C\n");
+ g_fCtrlC = K_TRUE;
exit(9);
break;
case CTRL_BREAK_EVENT:
fprintf(stderr, "kWorker: Ctrl-Break\n");
+ g_fCtrlC = K_TRUE;
exit(10);
break;
case CTRL_CLOSE_EVENT:
fprintf(stderr, "kWorker: console closed\n");
+ g_fCtrlC = K_TRUE;
exit(11);
break;
case CTRL_LOGOFF_EVENT:
fprintf(stderr, "kWorker: logoff event\n");
+ g_fCtrlC = K_TRUE;
exit(11);
break;
case CTRL_SHUTDOWN_EVENT:
fprintf(stderr, "kWorker: shutdown event\n");
+ g_fCtrlC = K_TRUE;
exit(11);
break;
@@ -7167,109 +9102,18 @@ static PPEB kwSandboxGetProcessEnvironmentBlock(void)
}
-#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
-typedef struct _EXCEPTION_REGISTRATION_RECORD
-{
- struct _EXCEPTION_REGISTRATION_RECORD * volatile PrevStructure;
- KU32 (__cdecl * volatile ExceptionHandler)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
- struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
-};
-
-/**
- * Vectored exception handler that emulates x86 chained exception handler.
- *
- * This is necessary because the RtlIsValidHandler check fails for self loaded
- * code and prevents cl.exe from working. (On AMD64 we can register function
- * tables, but on X86 cooking your own handling seems to be the only viabke
- * alternative.)
- *
- * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
- * @param pXcptPtrs The exception details.
- */
-static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
-{
- PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
- KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
- if (g_Sandbox.fRunning)
- {
- PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
- PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
- struct _EXCEPTION_REGISTRATION_RECORD * volatile *ppRegRec = &pTib->ExceptionList;
- struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = *ppRegRec;
- while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
- {
-#if 1
- /* This is a more robust version that isn't subject to calling
- convension cleanup disputes and such. */
- KU32 uSavedEdi;
- KU32 uSavedEsi;
- KU32 uSavedEbx;
- KU32 rcHandler;
- __asm
- {
- mov [uSavedEdi], edi
- mov [uSavedEsi], esi
- mov [uSavedEbx], ebx
- mov esi, esp
- mov edi, esp
- mov ecx, [pXcptRec]
- mov edx, [pRegRec]
- mov eax, [pXcptCtx]
- mov ebx, [ppRegRec]
- sub esp, 16
- and esp, 0fffffff0h
- mov [esp ], ecx
- mov [esp + 4], edx
- mov [esp + 8], eax
- mov [esp + 12], ebx
- call dword ptr [edx + 4]
- mov esp, esi
- cmp esp, edi
- je stack_ok
- int 3
- stack_ok:
- mov edi, [uSavedEdi]
- mov esi, [uSavedEsi]
- mov ebx, [uSavedEbx]
- mov [rcHandler], eax
- }
-#else
- KU32 rcHandler = pRegRec->ExceptionHandler(pXcptPtrs->ExceptionRecord, pRegRec, pXcptPtrs->ContextRecord, ppRegRec);
-#endif
- if (rcHandler == ExceptionContinueExecution)
- {
- kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
- return EXCEPTION_CONTINUE_EXECUTION;
- }
- if (rcHandler == ExceptionContinueSearch)
- kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
- else if (rcHandler == ExceptionNestedException)
- kHlpAssertMsgFailed(("Nested exceptions.\n"));
- else
- kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
-
- /*
- * Next.
- */
- ppRegRec = &pRegRec->PrevStructure;
- pRegRec = pRegRec->PrevStructure;
- }
- }
- return EXCEPTION_CONTINUE_SEARCH;
-}
-#endif /* WINDOWS + X86 */
-
-
/**
* Enters the given handle into the handle table.
*
* @returns K_TRUE on success, K_FALSE on failure.
* @param pSandbox The sandbox.
* @param pHandle The handle.
+ * @param hHandle The handle value to enter it under (for the
+ * duplicate handle API).
*/
-static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle)
+static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
{
- KUPTR const idxHandle = KW_HANDLE_TO_INDEX(pHandle->hHandle);
+ KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
/*
@@ -7359,15 +9203,15 @@ static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KB
static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
- KU32 cEnvVars, const char **papszEnvVars)
+ KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
{
PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
+ PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
wchar_t *pwcPool;
KSIZE cbStrings;
KSIZE cwc;
KSIZE cbCmdLine;
KU32 i;
- int rc;
/* Simple stuff. */
pSandbox->rcExitCode = 256;
@@ -7376,11 +9220,18 @@ static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
pSandbox->pgmptr = (char *)pTool->pszPath;
pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
- pSandbox->StdOut.cwcBuf = 0;
- pSandbox->StdErr.cwcBuf = 0;
- pSandbox->Combined.cwcBuf = 0;
+ if (pSandbox->StdOut.fIsConsole)
+ pSandbox->StdOut.u.Con.cwcBuf = 0;
+ else
+ pSandbox->StdOut.u.Fully.cchBuf = 0;
+ if (pSandbox->StdErr.fIsConsole)
+ pSandbox->StdErr.u.Con.cwcBuf = 0;
+ else
+ pSandbox->StdErr.u.Fully.cchBuf = 0;
+ pSandbox->Combined.cwcBuf = 0;
pSandbox->Combined.cFlushes = 0;
#endif
+ pSandbox->fNoPchCaching = fNoPchCaching;
pSandbox->cArgs = cArgs;
pSandbox->papszArgs = (char **)papszArgs;
pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
@@ -7414,15 +9265,15 @@ static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
return KERR_NO_MEMORY;
cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
- pSandbox->SavedCommandLine = pPeb->ProcessParameters->CommandLine;
- pPeb->ProcessParameters->CommandLine.Buffer = pSandbox->pwszCmdLine;
- pPeb->ProcessParameters->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
+ pSandbox->SavedCommandLine = pProcParams->CommandLine;
+ pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
+ pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
/*
- * Setup the enviornment.
+ * Setup the environment.
*/
- rc = kwSandboxGrowEnv(pSandbox, cEnvVars + 2);
- if (rc == 0)
+ if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
+ || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
{
KU32 iDst = 0;
for (i = 0; i < cEnvVars; i++)
@@ -7458,13 +9309,24 @@ static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
pSandbox->wenviron[iDst] = NULL;
}
else
- return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: %d\n", rc);
+ return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
+
/*
* Invalidate the volatile parts of cache (kBuild output directory,
* temporary directory, whatever).
*/
kFsCacheInvalidateCustomBoth(g_pFsCache);
+
+#ifdef WITH_HISTORY
+ /*
+ * Record command line in debug history.
+ */
+ kHlpFree(g_apszHistory[g_iHistoryNext]);
+ g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
+ g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
+#endif
+
return 0;
}
@@ -7491,6 +9353,10 @@ static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
#endif
PKWEXITCALLACK pExitCallback;
+ /*
+ * First stuff that may cause code to run.
+ */
+
/* Do exit callback first. */
pExitCallback = g_Sandbox.pExitCallbackHead;
g_Sandbox.pExitCallbackHead = NULL;
@@ -7513,6 +9379,89 @@ static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
pExitCallback = pNext;
}
+ /* Free left behind FlsAlloc leaks. */
+ pLocalStorage = g_Sandbox.pFlsAllocHead;
+ g_Sandbox.pFlsAllocHead = NULL;
+ while (pLocalStorage)
+ {
+ PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
+ KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
+ FlsFree(pLocalStorage->idx);
+ kHlpFree(pLocalStorage);
+ pLocalStorage = pNext;
+ }
+
+ /* Free left behind TlsAlloc leaks. */
+ pLocalStorage = g_Sandbox.pTlsAllocHead;
+ g_Sandbox.pTlsAllocHead = NULL;
+ while (pLocalStorage)
+ {
+ PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
+ KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
+ TlsFree(pLocalStorage->idx);
+ kHlpFree(pLocalStorage);
+ pLocalStorage = pNext;
+ }
+
+
+ /*
+ * Then free resources associated with the sandbox run.
+ */
+
+ /* Open handles, except fixed handles (stdout and stderr). */
+ if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
+ {
+ KU32 idxHandle = pSandbox->cHandles;
+ while (idxHandle-- > 0)
+ if (pSandbox->papHandles[idxHandle] == NULL)
+ { /* likely */ }
+ else
+ {
+ PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
+ if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
+ || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
+ {
+ pSandbox->papHandles[idxHandle] = NULL;
+ pSandbox->cLeakedHandles++;
+
+ switch (pHandle->enmType)
+ {
+ case KWHANDLETYPE_FSOBJ_READ_CACHE:
+ KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
+ idxHandle, pHandle->hHandle, pHandle->cRefs));
+ break;
+ case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
+ KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
+ idxHandle, pHandle->hHandle, pHandle->cRefs));
+ break;
+ case KWHANDLETYPE_OUTPUT_BUF:
+ KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
+ idxHandle, pHandle->hHandle, pHandle->cRefs));
+ break;
+ case KWHANDLETYPE_TEMP_FILE:
+ KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
+ idxHandle, pHandle->hHandle, pHandle->cRefs));
+ pHandle->u.pTempFile->cActiveHandles--;
+ break;
+ case KWHANDLETYPE_TEMP_FILE_MAPPING:
+ KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
+ idxHandle, pHandle->hHandle, pHandle->cRefs));
+ pHandle->u.pTempFile->cActiveHandles--;
+ break;
+ default:
+ kHlpAssertFailed();
+ }
+ if (--pHandle->cRefs == 0)
+ kHlpFree(pHandle);
+ if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
+ break;
+ }
+ }
+ kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
+ }
+
+ /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
+ g_Sandbox.cMemMappings = 0;
#ifdef WITH_TEMP_MEMORY_FILES
/* The temporary files aren't externally visible, they're all in memory. */
@@ -7532,18 +9481,6 @@ static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
}
#endif
- /* Free left behind VirtualAlloc leaks. */
- pTracker = g_Sandbox.pVirtualAllocHead;
- g_Sandbox.pVirtualAllocHead = NULL;
- while (pTracker)
- {
- PKWVIRTALLOC pNext = pTracker->pNext;
- KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
- VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
- kHlpFree(pTracker);
- pTracker = pNext;
- }
-
/* Free left behind HeapCreate leaks. */
pHeap = g_Sandbox.pHeapHead;
g_Sandbox.pHeapHead = NULL;
@@ -7555,31 +9492,24 @@ static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
pHeap = pNext;
}
- /* Free left behind FlsAlloc leaks. */
- pLocalStorage = g_Sandbox.pFlsAllocHead;
- g_Sandbox.pFlsAllocHead = NULL;
- while (pLocalStorage)
+ /* Free left behind VirtualAlloc leaks. */
+ pTracker = g_Sandbox.pVirtualAllocHead;
+ g_Sandbox.pVirtualAllocHead = NULL;
+ while (pTracker)
{
- PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
- KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
- FlsFree(pLocalStorage->idx);
- kHlpFree(pLocalStorage);
- pLocalStorage = pNext;
- }
+ PKWVIRTALLOC pNext = pTracker->pNext;
+ KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
- /* Free left behind TlsAlloc leaks. */
- pLocalStorage = g_Sandbox.pTlsAllocHead;
- g_Sandbox.pTlsAllocHead = NULL;
- while (pLocalStorage)
- {
- PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
- KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
- TlsFree(pLocalStorage->idx);
- kHlpFree(pLocalStorage);
- pLocalStorage = pNext;
+#ifdef WITH_FIXED_VIRTUAL_ALLOCS
+ if (pTracker->idxPreAllocated != KU32_MAX)
+ kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
+ else
+#endif
+ VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
+ kHlpFree(pTracker);
+ pTracker = pNext;
}
-
/* Free the environment. */
if (pSandbox->papszEnvVars)
{
@@ -7617,62 +9547,110 @@ static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
MemInfo.WorkingSetSize = 0;
if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
{
+ /* The first time thru, we figure out approximately when to restart
+ based on installed RAM and CPU threads. */
+ static KU64 s_cbMaxWorkingSet = 0;
+ if (s_cbMaxWorkingSet != 0)
+ { /* likely */ }
+ else
+ {
+ SYSTEM_INFO SysInfo;
+ MEMORYSTATUSEX GlobalMemInfo;
+ const char *pszValue;
+
+ /* Calculate a reasonable estimate. */
+ kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
+ GetNativeSystemInfo(&SysInfo);
+
+ kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
+ GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
+ if (!GlobalMemoryStatusEx(&GlobalMemInfo))
#if K_ARCH_BITS >= 64
- if (MemInfo.WorkingSetSize >= 512*1024*1024)
+ GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
#else
- if (MemInfo.WorkingSetSize >= 384*1024*1024)
+ GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
+#endif
+ s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
+ KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
+
+ /* User limit. */
+ pszValue = getenv("KWORKER_MEMORY_LIMIT");
+ if (pszValue != NULL)
+ {
+ char *pszNext;
+ unsigned long ulValue = strtol(pszValue, &pszNext, 0);
+ if (*pszNext == '\0' || *pszNext == 'M')
+ s_cbMaxWorkingSet = ulValue * (KU64)1048576;
+ else if (*pszNext == 'K')
+ s_cbMaxWorkingSet = ulValue * (KU64)1024;
+ else if (*pszNext == 'G')
+ s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
+ else
+ kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
+ KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
+ }
+
+ /* Clamp it a little. */
+ if (s_cbMaxWorkingSet < 168*1024*1024)
+ s_cbMaxWorkingSet = 168*1024*1024;
+#if K_ARCH_BITS < 64
+ else
+ s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
+ SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
+ ? 512*1024*1024 /* Only got 2 or 3 GB VA */
+ : 1536*1024*1024 /* got 4GB VA */);
#endif
+ if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
+ s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
+ KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
+ }
+
+ /* Finally the check. */
+ if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
{
KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
- //fprintf(stderr, "WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize);
g_fRestart = K_TRUE;
}
}
+
+ /*
+ * The CRT has a max of 8192 handles, so we better restart after a while if
+ * someone is leaking handles or we risk running out of descriptors.
+ *
+ * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
+ * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
+ */
+ if (pSandbox->cLeakedHandles > 6000)
+ {
+ KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
+ g_fRestart = K_TRUE;
+ }
}
+/**
+ * Does essential cleanups and restoring, anything externally visible.
+ *
+ * All cleanups that aren't externally visible are postponed till after we've
+ * informed kmk of the result, so it can be done in the dead time between jobs.
+ *
+ * @param pSandbox The sandbox.
+ */
static void kwSandboxCleanup(PKWSANDBOX pSandbox)
{
/*
* Restore the parent command line string.
*/
PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
- pPeb->ProcessParameters->CommandLine = pSandbox->SavedCommandLine;
-
- /*
- * Kill all open handles.
- */
- if (pSandbox->cActiveHandles > 0)
- {
- KU32 i = pSandbox->cHandles;
- while (i-- > 0)
- if (pSandbox->papHandles[i] == NULL)
- { /* likely */ }
- else
- {
- PKWHANDLE pHandle = pSandbox->papHandles[i];
- pSandbox->papHandles[i] = NULL;
- switch (pHandle->enmType)
- {
- case KWHANDLETYPE_FSOBJ_READ_CACHE:
- break;
- case KWHANDLETYPE_TEMP_FILE:
- case KWHANDLETYPE_TEMP_FILE_MAPPING:
- pHandle->u.pTempFile->cActiveHandles--;
- break;
- default:
- kHlpAssertFailed();
- }
- kHlpFree(pHandle);
- if (--pSandbox->cActiveHandles == 0)
- break;
- }
- }
+ PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
+ pProcParams->CommandLine = pSandbox->SavedCommandLine;
+ pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
+ pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
}
static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
- KU32 cEnvVars, const char **papszEnvVars)
+ KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
{
int rcExit = 42;
int rc;
@@ -7680,7 +9658,7 @@ static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const c
/*
* Initialize the sandbox environment.
*/
- rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars);
+ rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
if (rc == 0)
{
/*
@@ -7741,6 +9719,18 @@ static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const c
#endif
__except (EXCEPTION_EXECUTE_HANDLER)
{
+ kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
+#ifdef WITH_HISTORY
+ {
+ KU32 cPrinted = 0;
+ while (cPrinted++ < 5)
+ {
+ KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
+ if (g_apszHistory[idx])
+ kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
+ }
+ }
+#endif
rcExit = 512;
}
pSandbox->fRunning = K_FALSE;
@@ -7801,18 +9791,33 @@ static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdA
* @param papszArgs The argument vector.
* @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
* @param cEnvVars The number of environment variables.
- * @param papszEnvVars The enviornment vector.
+ * @param papszEnvVars The environment vector.
+ * @param fNoPchCaching Whether to disable precompiled header file
+ * caching. Avoid trouble when creating them.
* @param cPostCmdArgs Number of post command arguments (includes cmd).
* @param papszPostCmdArgs The post command and its argument.
*/
static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
- KU32 cEnvVars, const char **papszEnvVars,
+ KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching,
KU32 cPostCmdArgs, const char **papszPostCmdArgs)
{
int rcExit;
PKWTOOL pTool;
+ KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
+ pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
+#ifdef KW_LOG_ENABLED
+ {
+ KU32 i;
+ for (i = 0; i < cArgs; i++)
+ KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
+ for (i = 0; i < cPostCmdArgs; i++)
+ KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
+ }
+#endif
+ g_cJobs++;
+
/*
* Lookup the tool.
*/
@@ -7851,7 +9856,8 @@ static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCw
if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
{
KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
- rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars);
+ rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
+ cEnvVars, papszEnvVars, fNoPchCaching);
}
else
{
@@ -7985,11 +9991,12 @@ static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
}
papszEnvVars[cEnvVars] = 0;
- /* Flags (currently just watcom argument brain damanage). */
- if (cbMsg >= sizeof(KU8))
+ /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
+ if (cbMsg >= sizeof(KU8) * 2)
{
KBOOL fWatcomBrainDamange = *pszMsg++;
- cbMsg--;
+ KBOOL fNoPchCaching = *pszMsg++;
+ cbMsg -= 2;
/* Post command argument count (can be zero). */
if (cbMsg >= sizeof(KU32))
@@ -8025,7 +10032,7 @@ static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
*/
rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
cArgs, papszArgs, fWatcomBrainDamange,
- cEnvVars, papszEnvVars,
+ cEnvVars, papszEnvVars, fNoPchCaching,
cPostCmdArgs, apszPostCmdArgs);
}
else if (cbMsg == KSIZE_MAX)
@@ -8150,6 +10157,176 @@ static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShu
/**
+ * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
+ *
+ * @returns pszBuf
+ * @param pszBuf The buffer (sufficiently large).
+ * @param uValue The value.
+ */
+static const char *kwFmtU64(char *pszBuf, KU64 uValue)
+{
+ char szTmp[64];
+ char *psz = &szTmp[63];
+ int cch = 4;
+
+ *psz-- = '\0';
+ do
+ {
+ if (--cch == 0)
+ {
+ *psz-- = ' ';
+ cch = 3;
+ }
+ *psz-- = (uValue % 10) + '0';
+ uValue /= 10;
+ } while (uValue != 0);
+
+ return strcpy(pszBuf, psz + 1);
+}
+
+
+/**
+ * Prints statistics.
+ */
+static void kwPrintStats(void)
+{
+ PROCESS_MEMORY_COUNTERS_EX MemInfo;
+ MEMORYSTATUSEX MemStatus;
+ IO_COUNTERS IoCounters;
+ DWORD cHandles;
+ KSIZE cMisses;
+ char szBuf[16*1024];
+ int off = 0;
+ char szPrf[24];
+ char sz1[64];
+ char sz2[64];
+ char sz3[64];
+ char sz4[64];
+ extern size_t maybe_con_fwrite(void const *pvBuf, size_t cbUnit, size_t cUnits, FILE *pFile);
+
+ sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
+
+ szBuf[off++] = '\n';
+
+ off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
+ kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
+ off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
+ kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
+ kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
+
+ off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
+ szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
+
+ off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
+ kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
+ kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
+
+ off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
+ kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
+ kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
+
+ off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
+ kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
+ off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
+ kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
+ (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
+ kwFmtU64(sz2, g_cWriteFileToInMemTemp),
+ (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
+
+ off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
+ kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
+ off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
+ kwFmtU64(sz1, g_pFsCache->cObjects),
+ kwFmtU64(sz2, g_pFsCache->cbObjects),
+ kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
+ off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
+ kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
+ kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
+ kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
+ kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
+#ifdef KFSCACHE_CFG_UTF16
+ off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
+ kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
+ kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
+ kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
+ kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
+#endif
+ off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
+ kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
+ kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
+ kwFmtU64(sz3, g_pFsCache->cChildHashed),
+ kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
+
+ cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
+ off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
+ kwFmtU64(sz1, g_pFsCache->cLookups),
+ kwFmtU64(sz2, g_pFsCache->cPathHashHits),
+ (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
+ kwFmtU64(sz3, g_pFsCache->cWalkHits),
+ (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
+ kwFmtU64(sz4, cMisses),
+ (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
+
+ off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
+ kwFmtU64(sz1, g_pFsCache->cChildSearches),
+ kwFmtU64(sz2, g_pFsCache->cChildHashHits),
+ (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
+ off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
+ kwFmtU64(sz1, g_pFsCache->cNameChanges),
+ kwFmtU64(sz2, g_pFsCache->cNameGrowths),
+ (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
+
+
+ /*
+ * Process & Memory details.
+ */
+ if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
+ cHandles = 0;
+ MemInfo.cb = sizeof(MemInfo);
+ if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
+ memset(&MemInfo, 0, sizeof(MemInfo));
+ off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
+ kwFmtU64(sz1, cHandles),
+ kwFmtU64(sz2, MemInfo.PageFaultCount),
+ kwFmtU64(sz3, MemInfo.PagefileUsage),
+ kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
+ off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
+ kwFmtU64(sz1, MemInfo.WorkingSetSize),
+ kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
+ kwFmtU64(sz3, MemInfo.PrivateUsage));
+ off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
+ szPrf,
+ kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
+ kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
+ kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
+ kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
+
+ if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
+ memset(&IoCounters, 0, sizeof(IoCounters));
+ off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
+ kwFmtU64(sz1, IoCounters.ReadTransferCount),
+ kwFmtU64(sz2, IoCounters.ReadOperationCount));
+ off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
+ kwFmtU64(sz1, IoCounters.WriteTransferCount),
+ kwFmtU64(sz2, IoCounters.WriteOperationCount));
+ off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
+ kwFmtU64(sz1, IoCounters.OtherTransferCount),
+ kwFmtU64(sz2, IoCounters.OtherOperationCount));
+
+ MemStatus.dwLength = sizeof(MemStatus);
+ if (!GlobalMemoryStatusEx(&MemStatus))
+ memset(&MemStatus, 0, sizeof(MemStatus));
+ off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
+ kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
+ MemStatus.ullAvailVirtual);
+ off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
+
+ maybe_con_fwrite(szBuf, off, 1, stdout);
+ fflush(stdout);
+}
+
+
+/**
* Handles what comes after --test.
*
* @returns Exit code.
@@ -8166,6 +10343,7 @@ static int kwTestRun(int argc, char **argv)
const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
KU32 cEnvVars;
KBOOL fWatcomBrainDamange = K_FALSE;
+ KBOOL fNoPchCaching = K_FALSE;
/*
* Parse arguments.
@@ -8183,7 +10361,8 @@ static int kwTestRun(int argc, char **argv)
/* Optional directory change. */
if ( i < argc
- && strcmp(argv[i], "--chdir") == 0)
+ && ( strcmp(argv[i], "--chdir") == 0
+ || strcmp(argv[i], "-C") == 0 ) )
{
i++;
if (i >= argc)
@@ -8200,6 +10379,22 @@ static int kwTestRun(int argc, char **argv)
i++;
}
+ /* Optional watcom flag directory change. */
+ if ( i < argc
+ && strcmp(argv[i], "--no-pch-caching") == 0)
+ {
+ fNoPchCaching = K_TRUE;
+ i++;
+ }
+
+ /* Trigger breakpoint */
+ if ( i < argc
+ && strcmp(argv[i], "--breakpoint") == 0)
+ {
+ __debugbreak();
+ i++;
+ }
+
/* Check for '--'. */
if (i >= argc)
return kwErrPrintfRc(2, "Missing '--'\n");
@@ -8226,32 +10421,65 @@ static int kwTestRun(int argc, char **argv)
{
rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
argc - i, &argv[i], fWatcomBrainDamange,
- cEnvVars, environ,
+ cEnvVars, environ, fNoPchCaching,
0, NULL);
KW_LOG(("rcExit=%d\n", rcExit));
kwSandboxCleanupLate(&g_Sandbox);
}
+ if (getenv("KWORKER_STATS") != NULL)
+ kwPrintStats();
+
+# ifdef WITH_LOG_FILE
+ if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
+ CloseHandle(g_hLogFile);
+# endif
return rcExit;
}
-#if 1
int main(int argc, char **argv)
{
- KSIZE cbMsgBuf = 0;
- KU8 *pbMsgBuf = NULL;
- int i;
- HANDLE hPipe = INVALID_HANDLE_VALUE;
- const char *pszTmp;
- KFSLOOKUPERROR enmIgnored;
+ KSIZE cbMsgBuf = 0;
+ KU8 *pbMsgBuf = NULL;
+ int i;
+ HANDLE hPipe = INVALID_HANDLE_VALUE;
+ const char *pszTmp;
+ KFSLOOKUPERROR enmIgnored;
#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
- PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/, kwSandboxVecXcptEmulateChained);
+ PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
+ kwSandboxVecXcptEmulateChained);
#endif
#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
HANDLE hCurProc = GetCurrentProcess();
PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
+ DWORD dwType;
+#endif
+
+
+#ifdef WITH_FIXED_VIRTUAL_ALLOCS
+ /*
+ * Reserve memory for cl.exe
+ */
+ for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
+ {
+ g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
+ g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
+ g_aFixedVirtualAllocs[i].cbFixed,
+ MEM_RESERVE, PAGE_READWRITE);
+ if ( !g_aFixedVirtualAllocs[i].pvReserved
+ || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
+ {
+ kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
+ GetLastError());
+ if (g_aFixedVirtualAllocs[i].pvReserved)
+ {
+ VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
+ g_aFixedVirtualAllocs[i].pvReserved = NULL;
+ }
+ }
+ }
#endif
/*
@@ -8277,22 +10505,67 @@ int main(int argc, char **argv)
if (pszTmp && *pszTmp != '\0')
kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
+ /*
+ * Make g_abDefLdBuf executable.
+ */
+ if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
+ return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
+ g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
+
#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
/*
* Get and duplicate the console handles.
*/
+ /* Standard output. */
g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
- g_Sandbox.StdOut.fIsConsole = GetFileType(g_Sandbox.StdOut.hOutput) == FILE_TYPE_CHAR;
+ dwType = GetFileType(g_Sandbox.StdOut.hOutput);
+ g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
+ g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
+ ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
+ g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
+ g_Sandbox.HandleStdOut.cRefs = 0x10001;
+ g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
+ g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
+ g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
+ if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
+ {
+ if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
+ g_Sandbox.cFixedHandles++;
+ else
+ return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
+ }
+ KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
+ g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
+ /* Standard error. */
g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
- g_Sandbox.StdErr.fIsConsole = GetFileType(g_Sandbox.StdErr.hOutput) == FILE_TYPE_CHAR;
+ dwType = GetFileType(g_Sandbox.StdErr.hOutput);
+ g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
+ g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
+ ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
+ g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
+ g_Sandbox.HandleStdErr.cRefs = 0x10001;
+ g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
+ g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
+ g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
+ if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
+ && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
+ {
+ if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
+ g_Sandbox.cFixedHandles++;
+ else
+ return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
+ }
+ KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
+ g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
+ /* Combined console buffer. */
if (g_Sandbox.StdErr.fIsConsole)
{
g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
@@ -8308,7 +10581,8 @@ int main(int argc, char **argv)
g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
g_Sandbox.Combined.uCodepage = CP_ACP;
}
-#endif
+ KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
+#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
/*
@@ -8353,7 +10627,7 @@ int main(int argc, char **argv)
printf("usage: kWorker [--volatile dir] --pipe <pipe-handle>\n"
"usage: kWorker <--help|-h>\n"
"usage: kWorker <--version|-V>\n"
- "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>]] -- args\n"
+ "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
"\n"
"This is an internal kmk program that is used via the builtin_kSubmit.\n");
return 0;
@@ -8451,75 +10725,228 @@ int main(int argc, char **argv)
}
CloseHandle(hPipe);
+#ifdef WITH_LOG_FILE
+ if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
+ CloseHandle(g_hLogFile);
+#endif
+ if (getenv("KWORKER_STATS") != NULL)
+ kwPrintStats();
return rc > 0 ? 0 : 1;
}
}
-#else
-
-static int kwExecCmdLine(const char *pszExe, const char *pszCmdLine)
-{
- int rc;
- PKWTOOL pTool = kwToolLookup(pszExe);
- if (pTool)
- {
- int rcExitCode;
- switch (pTool->enmType)
- {
- case KWTOOLTYPE_SANDBOXED:
- KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
- rc = kwSandboxExec(&g_Sandbox, pTool, pszCmdLine, &rcExitCode);
- break;
- default:
- kHlpAssertFailed();
- KW_LOG(("TODO: Direct exec tool %s\n", pTool->pszPath));
- rc = rcExitCode = 2;
- break;
- }
- KW_LOG(("rcExitCode=%d (rc=%d)\n", rcExitCode, rc));
- }
- else
- rc = 1;
- return rc;
-}
-
-int main(int argc, char **argv)
-{
- int rc = 0;
- int i;
- argv[2] = "\"E:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/bin/amd64/cl.exe\" -c -c -TP -nologo -Zi -Zi -Zl -GR- -EHsc -GF -Zc:wchar_t- -Oy- -MT -W4 -Wall -wd4065 -wd4996 -wd4127 -wd4706 -wd4201 -wd4214 -wd4510 -wd4512 -wd4610 -wd4514 -wd4820 -wd4365 -wd4987 -wd4710 -wd4061 -wd4986 -wd4191 -wd4574 -wd4917 -wd4711 -wd4611 -wd4571 -wd4324 -wd4505 -wd4263 -wd4264 -wd4738 -wd4242 -wd4244 -WX -RTCsu -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/include -IE:/vbox/svn/trunk/tools/win.x86/vcc/v1 [...]
-# if 0
- rc = kwExecCmdLine(argv[1], argv[2]);
- rc = kwExecCmdLine(argv[1], argv[2]);
- K_NOREF(i);
-# else
-// Skylake (W10/amd64, only stdandard MS defender):
-// cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
-// kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
-// run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
-// run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
-// run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
-// run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
-// run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
-// r2881 building src/VBox/Runtime:
-// without: 2m01.016388s = 120.016388 s
-// with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
-// r2884 building vbox/debug (r110512):
-// without: 11m14.446609s = 674.446609 s
-// with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
-// r2896 building vbox/debug (r110577):
-// with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
-//
-// Dell (W7/amd64, infected by mcafee):
-// kmk 1: 285.278/1024 = 0x0 (0.278591796875)
-// run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
-// run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
- g_cVerbose = 0;
- for (i = 0; i < 1024 && rc == 0; i++)
- rc = kwExecCmdLine(argv[1], argv[2]);
-# endif
- return rc;
-}
-#endif
+/** @page pg_kWorker kSubmit / kWorker
+ *
+ * @section sec_kWorker_Motivation Motivation / Inspiration
+ *
+ * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
+ * builds on machines "infested" by Anti Virus protection and disk encryption
+ * software. Build times jumping from 35-40 min to 77-82 min after the machine
+ * got "infected".
+ *
+ * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
+ * mainly a bunch of tiny assembly and C files being compiler a million times.
+ * As some of us OS/2 users maybe recalls, the Watcom make program can run its
+ * own toolchain from within the same process, saving a lot of process creation
+ * and teardown overhead.
+ *
+ *
+ * @section sec_kWorker_kSubmit About kSubmit
+ *
+ * When wanting to execute a job in a kWorker instance, it must be submitted
+ * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
+ * built into kmk and does not exist as an external program. The reason for
+ * this is that it keep track of the kWorker instances.
+ *
+ * The kSubmit command has the --32-bit and --64-bit options for selecting
+ * between 32-bit and 64-bit worker instance. We generally assume the user of
+ * the command knows which bit count the executable has, so kSubmit is spared
+ * the extra work of finding out.
+ *
+ * The kSubmit command shares a environment and current directory manipulation
+ * with the kRedirect command, but not the file redirection. So long no file
+ * operation is involed, kSubmit is a drop in kRedirect replacement. This is
+ * hand for tools like OpenWatcom, NASM and YASM which all require environment
+ * and/or current directory changes to work.
+ *
+ * Unlike the kRedirect command, the kSubmit command can also specify an
+ * internall post command to be executed after the main command succeeds.
+ * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
+ * information from Microsoft COFF object files and Watcom OMF object files and
+ * is scheduled to replace kDepIDB.
+ *
+ *
+ * @section sec_kWorker_Interaction kSubmit / kWorker interaction
+ *
+ * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
+ * A job request is written by kSubmit and kWorker read, unpacks it and executes
+ * it. When the job is completed, kWorker writes a short reply with the exit
+ * code and an internal status indicating whether it is going to restart.
+ *
+ * The kWorker intance will reply to kSubmit before completing all the internal
+ * cleanup work, so as not to delay the next job execution unnecessarily. This
+ * includes checking its own memory consumption and checking whether it needs
+ * restarting. So, a decision to restart unfortunately have to wait till after
+ * the next job has completed. This is a little bit unfortunate if the next job
+ * requires a lot of memory and kWorker has already leaked/used a lot.
+ *
+ *
+ * @section sec_kWorker_How_Works How kWorker Works
+ *
+ * kWorker will load the executable specified by kSubmit into memory and call
+ * it's entrypoint in a lightly sandbox'ed environment.
+ *
+ *
+ * @subsection ssec_kWorker_Loaing Image loading
+ *
+ * kWorker will manually load all the executable images into memory, fix them
+ * up, and make a copy of the virgin image so it can be restored using memcpy
+ * the next time it is used.
+ *
+ * Imported functions are monitored and replacements used for a few of them.
+ * These replacements are serve the following purposes:
+ * - Provide a different command line.
+ * - Provide a different environment.
+ * - Intercept process termination.
+ * - Intercept thread creation (only linker is allowed to create threads).
+ * - Intercept file reading for caching (header files, ++) as file system
+ * access is made even slower by anti-virus software.
+ * - Intercept crypto hash APIs to cache MD5 digests of header files
+ * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
+ * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
+ * in memory as writing files grows expensive with encryption and
+ * anti-virus software active.
+ * - Intercept some file system queries to use the kFsCache instead of
+ * going to the kernel and slowly worm thru the AV filter driver.
+ * - Intercept standard output/error and console writes to aggressivly
+ * buffer the output. The MS CRT does not buffer either when it goes to
+ * the console, resulting in terrible performance and mixing up output
+ * with other compile jobs.
+ * This also allows us to filter out the annoying source file announcements
+ * by cl.exe.
+ * - Intercept VirtualAlloc and VirtualFree to prevent
+ * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
+ * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
+ * the callbacks run after each job.
+ * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
+ * executables and tools using custom heaps (like the microsoft linker).
+ * [exectuable images only]
+ * - Intercept atexit and _onexit registration to be able run them after
+ * each job instead of crashing as kWorker exits. This also helps avoid
+ * some leaks. [executable image only]
+ *
+ * DLLs falls into two categories, system DLLs which we always load using the
+ * native loader, and tool DLLs which can be handled like the executable or
+ * optionally using the native loader. We maintain a hardcoded white listing of
+ * tool DLLs we trust to load using the native loader.
+ *
+ * Imports of natively loaded DLLs are processed too, but we only replace a
+ * subset of the functions compared to natively loaded excutable and DLL images.
+ *
+ * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
+ * This is to speed up job execution.
+ *
+ * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
+ * for each job run, but so far this hasn't been necessary.
+ *
+ *
+ * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
+ *
+ * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
+ * files and uses a whole bunch of temporary files (in %TEMP%) for passing
+ * intermediate representation between the first (c1/c1xx.dll) and second pass
+ * (c2.dll).
+ *
+ * kWorker helps the compiler as best as it can. Given a little knowledge about
+ * stable and volatile file system areas, it can do a lot of caching that a
+ * normal compiler driver cannot easily do when given a single file.
+ *
+ *
+ * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
+ *
+ * The preprocessor part will open and process header files exactly as they are
+ * encountered in the source files. If string.h is included by the main source
+ * and five other header files, it will be searched for (include path), opened,
+ * read, MD5-summed, and pre-processed six times. The last five times is just a
+ * waste of time because of the guards or \#pragma once. A smart compiler would
+ * make a little extra effort and realize this.
+ *
+ * kWorker will cache help the preprocessor by remembering places where the
+ * header was not found with help of kFsCache, and cache the file in memory when
+ * found. The first part is taken care of by intercepting GetFileAttributesW,
+ * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
+ * cached, the file is kept open and the CreateFileW call returns a duplicate of
+ * that handle. An internal handle table is used by ReadFile and CloseFile to
+ * keep track of intercepted handles (also used for temporary file, temporary
+ * file mappings, console buffering, and standard out/err buffering).
+ *
+ * PS. The header search optimization also comes in handy when cl.exe goes on
+ * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
+ * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
+ * optionally compile the three pass DLLs as executables during development
+ * and problem analysis.
+ *
+ *
+ * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
+ *
+ * The issues of the temporary files is pretty severe on the Dell machine used
+ * for benchmarking with full AV and encryption. The synthetic benchmark
+ * improved by 30% when kWorker implemented measures to keep them entirely in
+ * memory.
+ *
+ * kWorker implement these by recognizing the filename pattern in CreateFileW
+ * and creating/opening the given file as needed. The handle returned is a
+ * duplicate of the current process, thus giving us a good chance of catching
+ * API calls we're not intercepting.
+ *
+ * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
+ * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
+ * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
+ * UnmapViewOfFile.
+ *
+ *
+ * @section sec_kWorker_Numbers Some measurements.
+ *
+ * - r2881 building src/VBox/Runtime:
+ * - without: 2m01.016388s = 120.016388 s
+ * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
+ * - r2884 building vbox/debug (r110512):
+ * - without: 11m14.446609s = 674.446609 s
+ * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
+ * - r2896 building vbox/debug (r110577):
+ * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
+ * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
+ * MS Defender as AV):
+ * - without: 10m24.990389s = 624.990389s
+ * - with: 08m04.738184s = 484.738184s
+ * - delta: 624.99s - 484.74s = 140.25s
+ * - saved: 140.25/624.99 = 22% faster
+ *
+ *
+ * @subsection subsec_kWorker_Early_Numbers Early Experiments
+ *
+ * These are some early experiments doing 1024 compilations of
+ * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
+ * main function:
+ *
+ * Skylake (W10/amd64, only stdandard MS defender):
+ * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
+ * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
+ * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
+ * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
+ * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
+ * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
+ * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
+ *
+ * Dell (W7/amd64, infected by mcafee):
+ * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
+ * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
+ * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
+ *
+ * The command line:
+ * @code{.cpp}
+ "\"E:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/bin/amd64/cl.exe\" -c -c -TP -nologo -Zi -Zi -Zl -GR- -EHsc -GF -Zc:wchar_t- -Oy- -MT -W4 -Wall -wd4065 -wd4996 -wd4127 -wd4706 -wd4201 -wd4214 -wd4510 -wd4512 -wd4610 -wd4514 -wd4820 -wd4365 -wd4987 -wd4710 -wd4061 -wd4986 -wd4191 -wd4574 -wd4917 -wd4711 -wd4611 -wd4571 -wd4324 -wd4505 -wd4263 -wd4264 -wd4738 -wd4242 -wd4244 -WX -RTCsu -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/include -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/atlmfc [...]
+ * @endcode
+ */
diff --git a/src/kmk/Makefile.kmk b/src/kmk/Makefile.kmk
index 11fe58f..25a9da6 100644
--- a/src/kmk/Makefile.kmk
+++ b/src/kmk/Makefile.kmk
@@ -1,4 +1,4 @@
-# $Id: Makefile.kmk 2907 2016-09-09 22:33:26Z bird $
+# $Id: Makefile.kmk 2994 2016-11-01 22:41:41Z bird $
## @file
# Sub-makefile for kmk / GNU Make.
#
@@ -88,6 +88,7 @@ kmkmissing_SOURCES = \
kmkbuiltin/setmode.c \
kmkbuiltin/strmode.c \
kmkbuiltin/kbuild_protection.c \
+ kmkbuiltin/common-env-and-cwd-opt.c \
getopt.c \
getopt1.c \
electric.c
@@ -279,6 +280,7 @@ kmk_SOURCES += \
kmkbuiltin/mv.c \
kmkbuiltin/ln.c \
kmkbuiltin/printf.c \
+ kmkbuiltin/redirect.c \
kmkbuiltin/rm.c \
kmkbuiltin/rmdir.c \
$(if-expr $(KBUILD_TARGET) == win,kmkbuiltin/kSubmit.c) \
@@ -394,7 +396,6 @@ kmk_rm_SOURCES = \
kmkbuiltin/rm.c
kmk_redirect_TEMPLATE = BIN-KMK
-kmk_redirect_DEFS = kmk_builtin_redirect=main
kmk_redirect_SOURCES = \
kmkbuiltin/redirect.c
kmk_redirect_SOURCES.win = \
@@ -530,6 +531,16 @@ PROGRAMS.win += tstFileInfo
tstFileInfo_TEMPLATE = BIN
tstFileInfo_SOURCES = w32/tstFileInfo.c
+#
+# tstFileInfo
+#
+PROGRAMS.win += tstFtsFake
+tstFtsFake_TEMPLATE = BIN-KMK
+tstFtsFake_NOINST = 1
+tstFtsFake_DEFS = USE_OLD_FTS
+tstFtsFake_SOURCES = ../lib/nt/tstNtFts.c
+
+
include $(FILE_KBUILD_SUB_FOOTER)
@@ -545,8 +556,13 @@ $(kmk_0_OUTDIR)/config.h: $(kmk_config.h.$(KBUILD_TARGET))
#
# Some missing headers.
#
+if1of ($(KBUILD_TARGET), win nt)
+$(kmk_0_OUTDIR)/fts.h: $(MAKEFILE) | $(call DIRDEP,$(kmk_0_OUTDIR))
+ $(APPEND) -t "$@" "#include <nt/fts-nt.h>"
+else
$(kmk_0_OUTDIR)/fts.h: $(kmk_DEFPATH)/kmkbuiltin/ftsfake.h | $(call DIRDEP,$(kmk_0_OUTDIR))
$(CP) $^ $@
+endif
$(kmk_0_OUTDIR)/unistd.h: | $(call DIRDEP,$(kmk_0_OUTDIR))
$(ECHO_EXT) > $@
diff --git a/src/kmk/dir-nt-bird.c b/src/kmk/dir-nt-bird.c
index cd8ad6d..3724833 100644
--- a/src/kmk/dir-nt-bird.c
+++ b/src/kmk/dir-nt-bird.c
@@ -1,4 +1,4 @@
-/* $Id: dir-nt-bird.c 2886 2016-09-06 14:31:46Z bird $ */
+/* $Id: dir-nt-bird.c 2948 2016-09-20 15:36:07Z bird $ */
/** @file
* Reimplementation of dir.c for NT using kFsCache.
*
@@ -120,8 +120,8 @@ int dir_file_exists_p(const char *pszDir, const char *pszName)
fRc = 1;
else
{
- PKFSOBJ pNameObj = kFsCacheLookupRelativeToDirA(g_pFsCache, (PKFSDIR)pDirObj,
- pszName, strlen(pszName), &enmError, NULL);
+ PKFSOBJ pNameObj = kFsCacheLookupRelativeToDirA(g_pFsCache, (PKFSDIR)pDirObj, pszName, strlen(pszName),
+ 0/*fFlags*/, &enmError, NULL);
if (pNameObj)
{
fRc = pNameObj->bObjType == KFSOBJ_TYPE_MISSING;
@@ -412,13 +412,50 @@ void dir_setup_glob(glob_t *pGlob)
}
+/**
+ * Print statitstics.
+ */
+void print_dir_stats(void)
+{
+ FILE *pOut = stdout;
+ KU32 cMisses;
+
+ fputs("\n"
+ "# NT dir cache stats:\n", pOut);
+ fprintf(pOut, "# %u objects, taking up %u (%#x) bytes, avg %u bytes\n",
+ g_pFsCache->cObjects, g_pFsCache->cbObjects, g_pFsCache->cbObjects, g_pFsCache->cbObjects / g_pFsCache->cObjects);
+ fprintf(pOut, "# %u A path hashes, taking up %u (%#x) bytes, avg %u bytes, %u collision\n",
+ g_pFsCache->cAnsiPaths, g_pFsCache->cbAnsiPaths, g_pFsCache->cbAnsiPaths,
+ g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1), g_pFsCache->cAnsiPathCollisions);
+#ifdef KFSCACHE_CFG_UTF16
+ fprintf(pOut, "# %u W path hashes, taking up %u (%#x) bytes, avg %u bytes, %u collisions\n",
+ g_pFsCache->cUtf16Paths, g_pFsCache->cbUtf16Paths, g_pFsCache->cbUtf16Paths,
+ g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1), g_pFsCache->cUtf16PathCollisions);
+#endif
+ fprintf(pOut, "# %u child hash tables, total of %u entries, %u children inserted, %u collisions\n",
+ g_pFsCache->cChildHashTabs, g_pFsCache->cChildHashEntriesTotal,
+ g_pFsCache->cChildHashed, g_pFsCache->cChildHashCollisions);
+
+ cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
+ fprintf(pOut, "# %u lookups: %u (%" KU64_PRI " %%) path hash hits, %u (%" KU64_PRI "%%) walks hits, %u (%" KU64_PRI "%%) misses\n",
+ g_pFsCache->cLookups,
+ g_pFsCache->cPathHashHits, g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1),
+ g_pFsCache->cWalkHits, g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1),
+ cMisses, cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1));
+ fprintf(pOut, "# %u child searches, %u (%" KU64_PRI "%%) hash hits\n",
+ g_pFsCache->cChildSearches,
+ g_pFsCache->cChildHashHits, g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1));
+}
+
+
void print_dir_data_base(void)
{
/** @todo. */
-}
+}
+/* duplicated in kWorker.c */
void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
{
KFSLOOKUPERROR enmError;
@@ -440,7 +477,7 @@ void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
{
kHlpAssert(off > 1);
kHlpAssert(pAncestor != NULL);
- kHlpAssert(pAncestor->ObjcchName > 0);
+ kHlpAssert(pAncestor->Obj.cchName > 0);
pszFull[--off] = '/';
off -= pAncestor->Obj.cchName;
kHlpAssert(pAncestor->Obj.cchParent == off);
@@ -585,3 +622,66 @@ int dir_cache_volatile_dir(const char *pszDir)
return -1;
}
+/**
+ * Invalidates a deleted directory so the cache can close handles to it.
+ *
+ * Used by kmk_builtin_rm and kmk_builtin_rmdir.
+ *
+ * @returns 0 on success, -1 on failure.
+ * @param pszDir The directory to invalidate as deleted.
+ */
+int dir_cache_deleted_directory(const char *pszDir)
+{
+ if (kFsCacheInvalidateDeletedDirectoryA(g_pFsCache, pszDir))
+ return 0;
+ return -1;
+}
+
+
+int kmk_builtin_dircache(int argc, char **argv, char **envp)
+{
+ if (argc >= 2)
+ {
+ const char *pszCmd = argv[1];
+ if (strcmp(pszCmd, "invalidate") == 0)
+ {
+ if (argc == 2)
+ {
+ dir_cache_invalid_all();
+ return 0;
+ }
+ fprintf(stderr, "kmk_builtin_dircache: the 'invalidate' command takes no arguments!\n");
+ }
+ else if (strcmp(pszCmd, "invalidate-missing") == 0)
+ {
+ if (argc == 2)
+ {
+ dir_cache_invalid_missing ();
+ return 0;
+ }
+ fprintf(stderr, "kmk_builtin_dircache: the 'invalidate-missing' command takes no arguments!\n");
+ }
+ else if (strcmp(pszCmd, "volatile") == 0)
+ {
+ int i;
+ for (i = 2; i < argc; i++)
+ dir_cache_volatile_dir(argv[i]);
+ return 0;
+ }
+ else if (strcmp(pszCmd, "deleted") == 0)
+ {
+ int i;
+ for (i = 2; i < argc; i++)
+ dir_cache_deleted_directory(argv[i]);
+ return 0;
+ }
+ else
+ fprintf(stderr, "kmk_builtin_dircache: Invalid command '%s'!\n", pszCmd);
+ }
+ else
+ fprintf(stderr, "kmk_builtin_dircache: No command given!\n");
+
+ K_NOREF(envp);
+ return 2;
+}
+
diff --git a/src/kmk/dir.c b/src/kmk/dir.c
index b0e5215..c9b1b37 100644
--- a/src/kmk/dir.c
+++ b/src/kmk/dir.c
@@ -1329,6 +1329,15 @@ print_dir_data_base (void)
#endif
}
+#ifdef CONFIG_WITH_PRINT_STATS_SWITCH
+/* Print stats */
+
+void print_dir_stats (void)
+{
+ /** @todo normal dir stats. */
+}
+#endif
+
/* Hooks for globbing. */
#if defined(KMK) && !defined(__OS2__)
diff --git a/src/kmk/function.c b/src/kmk/function.c
index ddaf1a9..2720403 100644
--- a/src/kmk/function.c
+++ b/src/kmk/function.c
@@ -5473,6 +5473,18 @@ func_dircache_ctl (char *o, char **argv UNUSED, const char *funcname UNUSED)
dir_cache_volatile_dir (dir);
}
}
+ else if (strcmp (cmd, "deleted") == 0)
+ {
+ size_t i;
+ for (i = 1; argv[i] != NULL; i++)
+ {
+ const char *dir = argv[i];
+ while (isblank ((unsigned char)*dir))
+ dir++;
+ if (*dir)
+ dir_cache_deleted_directory (dir);
+ }
+ }
else
error (reading_file, "Unknown $(dircache-ctl ) command: '%s'", cmd);
# endif
diff --git a/src/kmk/kmkbuiltin.c b/src/kmk/kmkbuiltin.c
index 358f3fc..3cca960 100644
--- a/src/kmk/kmkbuiltin.c
+++ b/src/kmk/kmkbuiltin.c
@@ -1,4 +1,4 @@
-/* $Id: kmkbuiltin.c 2843 2016-08-28 15:31:02Z bird $ */
+/* $Id: kmkbuiltin.c 2912 2016-09-14 13:36:15Z bird $ */
/** @file
* kMk Builtin command execution.
*/
@@ -205,8 +205,8 @@ int kmk_builtin_command_parsed(int argc, char **argv, struct child *pChild, char
rc = kmk_builtin_mkdir(argc, argv, environ);
else if (!strcmp(pszCmd, "mv"))
rc = kmk_builtin_mv(argc, argv, environ);
- /*else if (!strcmp(pszCmd, "redirect"))
- rc = kmk_builtin_redirect(argc, argv, environ, pPidSpawned);*/
+ else if (!strcmp(pszCmd, "redirect"))
+ rc = kmk_builtin_redirect(argc, argv, environ, pChild, pPidSpawned);
else if (!strcmp(pszCmd, "rm"))
rc = kmk_builtin_rm(argc, argv, environ);
else if (!strcmp(pszCmd, "rmdir"))
@@ -232,6 +232,12 @@ int kmk_builtin_command_parsed(int argc, char **argv, struct child *pChild, char
rc = kmk_builtin_cat(argc, argv, environ);
else if (!strcmp(pszCmd, "sleep"))
rc = kmk_builtin_sleep(argc, argv, environ);
+ else if (!strcmp(pszCmd, "dircache"))
+#ifdef KBUILD_OS_WINDOWS
+ rc = kmk_builtin_dircache(argc, argv, environ);
+#else
+ rc = 0;
+#endif
else
{
printf("kmk_builtin: Unknown command '%s'!\n", pszCmd);
diff --git a/src/kmk/kmkbuiltin.h b/src/kmk/kmkbuiltin.h
index 8c3a8a3..03d0a98 100644
--- a/src/kmk/kmkbuiltin.h
+++ b/src/kmk/kmkbuiltin.h
@@ -1,4 +1,4 @@
-/* $Id: kmkbuiltin.h 2899 2016-09-09 09:03:57Z bird $ */
+/* $Id: kmkbuiltin.h 2912 2016-09-14 13:36:15Z bird $ */
/** @file
* kMk Builtin command handling.
*/
@@ -45,6 +45,7 @@ extern int kmk_builtin_cp(int argc, char **argv, char **envp);
extern int kmk_builtin_cat(int argc, char **argv, char **envp);
extern int kmk_builtin_chmod(int argc, char **argv, char **envp);
extern int kmk_builtin_cmp(int argc, char **argv, char **envp);
+extern int kmk_builtin_dircache(int argc, char **argv, char **envp);
extern int kmk_builtin_echo(int argc, char **argv, char **envp);
extern int kmk_builtin_expr(int argc, char **argv, char **envp);
extern int kmk_builtin_install(int argc, char **argv, char **envp);
@@ -53,6 +54,7 @@ extern int kmk_builtin_md5sum(int argc, char **argv, char **envp);
extern int kmk_builtin_mkdir(int argc, char **argv, char **envp);
extern int kmk_builtin_mv(int argc, char **argv, char **envp);
extern int kmk_builtin_printf(int argc, char **argv, char **envp);
+extern int kmk_builtin_redirect(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned);
extern int kmk_builtin_rm(int argc, char **argv, char **envp);
extern int kmk_builtin_rmdir(int argc, char **argv, char **envp);
extern int kmk_builtin_sleep(int argc, char **argv, char **envp);
@@ -72,5 +74,11 @@ extern int kmk_builtin_kDepObj(int argc, char **argv, char **envp);
extern char *kmk_builtin_func_printf(char *o, char **argv, const char *funcname);
+/* common-env-and-cwd-opt.c: */
+extern int kBuiltinOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
+ int cVerbosity, const char *pszValue);
+extern int kBuiltinOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove);
+extern int kBuiltinOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue);
+
#endif
diff --git a/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c b/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c
new file mode 100644
index 0000000..21b76d5
--- /dev/null
+++ b/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c
@@ -0,0 +1,240 @@
+/* $Id: common-env-and-cwd-opt.c 2912 2016-09-14 13:36:15Z bird $ */
+/** @file
+ * kMk Builtin command - Commmon environment and CWD option handling code.
+ */
+
+/*
+ * Copyright (c) 2007-2016 knut st. osmundsen <bird-kBuild-spamx at anduin.net>
+ *
+ * This file is part of kBuild.
+ *
+ * kBuild is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * kBuild is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kBuild. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "kmkbuiltin.h"
+#include "err.h"
+
+
+/** The environment variable compare function.
+ * We must use case insensitive compare on windows (Path vs PATH). */
+#ifdef KBUILD_OS_WINDOWS
+# define KSUBMIT_ENV_NCMP _strnicmp
+#else
+# define KSUBMIT_ENV_NCMP strncmp
+#endif
+
+
+/**
+ * Handles the --set var=value option.
+ *
+ * @returns 0 on success, non-zero exit code on error.
+ * @param papszEnv The environment vector.
+ * @param pcEnvVars Pointer to the variable holding the number of
+ * environment variables held by @a papszEnv.
+ * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
+ * environment vector.
+ * @param cVerbosity The verbosity level.
+ * @param pszValue The var=value string to apply.
+ */
+int kBuiltinOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, int cVerbosity, const char *pszValue)
+{
+ const char *pszEqual = strchr(pszValue, '=');
+ if (pszEqual)
+ {
+ char **papszEnv = *ppapszEnv;
+ unsigned iEnvVar;
+ unsigned cEnvVars = *pcEnvVars;
+ size_t const cchVar = pszEqual - pszValue;
+ for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
+ {
+ char *pszCur = papszEnv[iEnvVar];
+ if ( KSUBMIT_ENV_NCMP(pszCur, pszValue, cchVar) == 0
+ && pszCur[cchVar] == '=')
+ {
+ if (cVerbosity > 0)
+ warnx("replacing '%s' with '%s'", papszEnv[iEnvVar], pszValue);
+ free(papszEnv[iEnvVar]);
+ papszEnv[iEnvVar] = strdup(pszValue);
+ if (!papszEnv[iEnvVar])
+ return errx(1, "out of memory!");
+ break;
+ }
+ }
+ if (iEnvVar == cEnvVars)
+ {
+ /* Append new variable. We probably need to resize the vector. */
+ if ((cEnvVars + 2) > *pcAllocatedEnvVars)
+ {
+ *pcAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
+ papszEnv = (char **)realloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0]));
+ if (!papszEnv)
+ return errx(1, "out of memory!");
+ *ppapszEnv = papszEnv;
+ }
+ papszEnv[cEnvVars] = strdup(pszValue);
+ if (!papszEnv[cEnvVars])
+ return errx(1, "out of memory!");
+ papszEnv[++cEnvVars] = NULL;
+ *pcEnvVars = cEnvVars;
+ if (cVerbosity > 0)
+ warnx("added '%s'", papszEnv[iEnvVar]);
+ }
+ else
+ {
+ /* Check for duplicates. */
+ for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++)
+ if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszValue, cchVar) == 0
+ && papszEnv[iEnvVar][cchVar] == '=')
+ {
+ if (cVerbosity > 0)
+ warnx("removing duplicate '%s'", papszEnv[iEnvVar]);
+ free(papszEnv[iEnvVar]);
+ cEnvVars--;
+ if (iEnvVar != cEnvVars)
+ papszEnv[iEnvVar] = papszEnv[cEnvVars];
+ papszEnv[cEnvVars] = NULL;
+ iEnvVar--;
+ }
+ }
+ }
+ else
+ return errx(1, "Missing '=': -E %s", pszValue);
+
+ return 0;
+}
+
+
+/**
+ * Handles the --unset var option.
+ *
+ * @returns 0 on success, non-zero exit code on error.
+ * @param papszEnv The environment vector.
+ * @param pcEnvVars Pointer to the variable holding the number of
+ * environment variables held by @a papszEnv.
+ * @param cVerbosity The verbosity level.
+ * @param pszVarToRemove The name of the variable to remove.
+ */
+int kBuiltinOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove)
+{
+ if (strchr(pszVarToRemove, '=') == NULL)
+ {
+ unsigned cRemoved = 0;
+ size_t const cchVar = strlen(pszVarToRemove);
+ unsigned cEnvVars = *pcEnvVars;
+ unsigned iEnvVar;
+
+ for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
+ if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszVarToRemove, cchVar) == 0
+ && papszEnv[iEnvVar][cchVar] == '=')
+ {
+ if (cVerbosity > 0)
+ warnx(!cRemoved ? "removing '%s'" : "removing duplicate '%s'", papszEnv[iEnvVar]);
+ free(papszEnv[iEnvVar]);
+ cEnvVars--;
+ if (iEnvVar != cEnvVars)
+ papszEnv[iEnvVar] = papszEnv[cEnvVars];
+ papszEnv[cEnvVars] = NULL;
+ cRemoved++;
+ iEnvVar--;
+ }
+ *pcEnvVars = cEnvVars;
+
+ if (cVerbosity > 0 && !cRemoved)
+ warnx("not found '%s'", pszVarToRemove);
+ }
+ else
+ return errx(1, "Found invalid variable name character '=' in: -U %s", pszVarToRemove);
+ return 0;
+}
+
+
+
+/**
+ * Handles the --chdir dir option.
+ *
+ * @returns 0 on success, non-zero exit code on error.
+ * @param pszCwd The CWD buffer. Contains current CWD on input,
+ * modified by @a pszValue on output.
+ * @param cbCwdBuf The size of the CWD buffer.
+ * @param pszValue The --chdir value to apply.
+ */
+int kBuiltinOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue)
+{
+ size_t cchNewCwd = strlen(pszValue);
+ size_t offDst;
+ if (cchNewCwd)
+ {
+#ifdef HAVE_DOS_PATHS
+ if (*pszValue == '/' || *pszValue == '\\')
+ {
+ if (pszValue[1] == '/' || pszValue[1] == '\\')
+ offDst = 0; /* UNC */
+ else if (pszCwd[1] == ':' && isalpha(pszCwd[0]))
+ offDst = 2; /* Take drive letter from CWD. */
+ else
+ return errx(1, "UNC relative CWD not implemented: cur='%s' new='%s'", pszCwd, pszValue);
+ }
+ else if ( pszValue[1] == ':'
+ && isalpha(pszValue[0]))
+ {
+ if (pszValue[2] == '/'|| pszValue[2] == '\\')
+ offDst = 0; /* DOS style absolute path. */
+ else if ( pszCwd[1] == ':'
+ && tolower(pszCwd[0]) == tolower(pszValue[0]) )
+ {
+ pszValue += 2; /* Same drive as CWD, append drive relative path from value. */
+ cchNewCwd -= 2;
+ offDst = strlen(pszCwd);
+ }
+ else
+ {
+ /* Get current CWD on the specified drive and append value. */
+ int iDrive = tolower(pszValue[0]) - 'a' + 1;
+ if (!_getdcwd(iDrive, pszCwd, cbCwdBuf))
+ return err(1, "_getdcwd(%d,,) failed", iDrive);
+ pszValue += 2;
+ cchNewCwd -= 2;
+ }
+ }
+#else
+ if (*pszValue == '/')
+ offDst = 0;
+#endif
+ else
+ offDst = strlen(pszCwd); /* Relative path, append to the existing CWD value. */
+
+ /* Do the copying. */
+#ifdef HAVE_DOS_PATHS
+ if (offDst > 0 && pszCwd[offDst - 1] != '/' && pszCwd[offDst - 1] != '\\')
+#else
+ if (offDst > 0 && pszCwd[offDst - 1] != '/')
+#endif
+ pszCwd[offDst++] = '/';
+ if (offDst + cchNewCwd >= cbCwdBuf)
+ return errx(1, "Too long CWD: %*.*s%s", offDst, offDst, pszCwd, pszValue);
+ memcpy(&pszCwd[offDst], pszValue, cchNewCwd + 1);
+ }
+ /* else: relative, no change - quitely ignore. */
+ return 0;
+}
+
diff --git a/src/kmk/kmkbuiltin/cp.c b/src/kmk/kmkbuiltin/cp.c
index f1811b5..5c51bcc 100644
--- a/src/kmk/kmkbuiltin/cp.c
+++ b/src/kmk/kmkbuiltin/cp.c
@@ -221,11 +221,9 @@ kmk_builtin_cp(int argc, char *argv[], char **envp)
case 'p':
pflag = 1;
break;
-#if 0 /* only one -R */
case 'r':
rflag = 1;
break;
-#endif
case 'v':
vflag = 1;
break;
@@ -695,6 +693,7 @@ usage(FILE *fp)
" -f Force. Overrides -i and -n.\n"
" -i Iteractive. Overrides -n and -f.\n"
" -n Don't overwrite any files. Overrides -i and -f.\n"
+" -v Verbose.\n"
" --ignore-non-existing\n"
" Don't fail if the specified source file doesn't exist.\n"
" --changed\n"
diff --git a/src/kmk/kmkbuiltin/kDepIDB.c b/src/kmk/kmkbuiltin/kDepIDB.c
index e12d39a..dda407e 100644
--- a/src/kmk/kmkbuiltin/kDepIDB.c
+++ b/src/kmk/kmkbuiltin/kDepIDB.c
@@ -1,4 +1,4 @@
-/* $Id: kDepIDB.c 2856 2016-09-01 02:42:08Z bird $ */
+/* $Id: kDepIDB.c 2955 2016-09-21 19:05:53Z bird $ */
/** @file
* kDepIDB - Extract dependency information from a MS Visual C++ .idb file.
*/
@@ -864,7 +864,7 @@ int kmk_builtin_kDepIDB(int argc, char *argv[], char **envp)
*/
if (!i)
{
- depOptimize(fFixCase, fQuiet);
+ depOptimize(fFixCase, fQuiet, NULL /*pszIgnoredExt*/);
fprintf(pOutput, "%s:", pszTarget);
depPrint(pOutput);
if (fStubs)
diff --git a/src/kmk/kmkbuiltin/kDepObj.c b/src/kmk/kmkbuiltin/kDepObj.c
index 96076c0..5cb090a 100644
--- a/src/kmk/kmkbuiltin/kDepObj.c
+++ b/src/kmk/kmkbuiltin/kDepObj.c
@@ -1,4 +1,4 @@
-/* $Id: kDepObj.c 2896 2016-09-08 15:32:09Z bird $ */
+/* $Id: kDepObj.c 2955 2016-09-21 19:05:53Z bird $ */
/** @file
* kDepObj - Extract dependency information from an object file.
*/
@@ -973,7 +973,7 @@ static int kDepObjProcessFile(FILE *pInput)
static void usage(const char *a_argv0)
{
- printf("usage: %s -o <output> -t <target> [-fqs] <OMF-file>\n"
+ printf("usage: %s -o <output> -t <target> [-fqs] [-e <ignore-ext>] <OMF or COFF file>\n"
" or: %s --help\n"
" or: %s --version\n",
a_argv0, a_argv0, a_argv0);
@@ -991,6 +991,7 @@ int kmk_builtin_kDepObj(int argc, char *argv[], char **envp)
const char *pszTarget = NULL;
int fStubs = 0;
int fFixCase = 0;
+ const char *pszIgnoreExt = NULL;
/* Argument parsing. */
int fInput = 0; /* set when we've found input argument. */
int fQuiet = 0;
@@ -1009,39 +1010,66 @@ int kmk_builtin_kDepObj(int argc, char *argv[], char **envp)
{
if (argv[i][0] == '-')
{
+ const char *pszValue;
const char *psz = &argv[i][1];
- if (*psz == '-')
+ char chOpt;
+ chOpt = *psz++;
+ if (chOpt == '-')
{
- if (!strcmp(psz, "-quiet"))
- psz = "q";
- else if (!strcmp(psz, "-help"))
- psz = "?";
- else if (!strcmp(psz, "-version"))
- psz = "V";
+ /* Convert long to short option. */
+ if (!strcmp(psz, "quiet"))
+ chOpt = 'q';
+ else if (!strcmp(psz, "help"))
+ chOpt = '?';
+ else if (!strcmp(psz, "version"))
+ chOpt = 'V';
+ else
+ {
+ fprintf(stderr, "%s: syntax error: Invalid argument '%s'.\n", argv[0], argv[i]);
+ usage(argv[0]);
+ return 2;
+ }
+ psz = "";
}
- switch (*psz)
+ /*
+ * Requires value?
+ */
+ switch (chOpt)
+ {
+ case 'o':
+ case 't':
+ case 'e':
+ if (*psz)
+ pszValue = psz;
+ else if (++i < argc)
+ pszValue = argv[i];
+ else
+ {
+ fprintf(stderr, "%s: syntax error: The '-%c' option takes a value.\n", chOpt);
+ return 2;
+ }
+ break;
+
+ default:
+ pszValue = NULL;
+ break;
+ }
+
+
+ switch (chOpt)
{
/*
* Output file.
*/
case 'o':
{
- pszOutput = &argv[i][2];
if (pOutput)
{
fprintf(stderr, "%s: syntax error: only one output file!\n", argv[0]);
- return 1;
- }
- if (!*pszOutput)
- {
- if (++i >= argc)
- {
- fprintf(stderr, "%s: syntax error: The '-o' argument is missing the filename.\n", argv[0]);
- return 1;
- }
- pszOutput = argv[i];
+ return 2;
}
+ pszOutput = pszValue;
if (pszOutput[0] == '-' && !pszOutput[1])
pOutput = stdout;
else
@@ -1064,16 +1092,7 @@ int kmk_builtin_kDepObj(int argc, char *argv[], char **envp)
fprintf(stderr, "%s: syntax error: only one target!\n", argv[0]);
return 1;
}
- pszTarget = &argv[i][2];
- if (!*pszTarget)
- {
- if (++i >= argc)
- {
- fprintf(stderr, "%s: syntax error: The '-t' argument is missing the target name.\n", argv[0]);
- return 1;
- }
- pszTarget = argv[i];
- }
+ pszTarget = pszValue;
break;
}
@@ -1105,6 +1124,20 @@ int kmk_builtin_kDepObj(int argc, char *argv[], char **envp)
}
/*
+ * Extension to ignore.
+ */
+ case 'e':
+ {
+ if (pszIgnoreExt)
+ {
+ fprintf(stderr, "%s: syntax error: The '-e' option can only be used once!\n");
+ return 2;
+ }
+ pszIgnoreExt = pszValue;
+ break;
+ }
+
+ /*
* The mandatory version & help.
*/
case '?':
@@ -1120,7 +1153,7 @@ int kmk_builtin_kDepObj(int argc, char *argv[], char **envp)
default:
fprintf(stderr, "%s: syntax error: Invalid argument '%s'.\n", argv[0], argv[i]);
usage(argv[0]);
- return 1;
+ return 2;
}
}
else
@@ -1178,7 +1211,7 @@ int kmk_builtin_kDepObj(int argc, char *argv[], char **envp)
*/
if (!i)
{
- depOptimize(fFixCase, fQuiet);
+ depOptimize(fFixCase, fQuiet, pszIgnoreExt);
fprintf(pOutput, "%s:", pszTarget);
depPrint(pOutput);
if (fStubs)
diff --git a/src/kmk/kmkbuiltin/kSubmit.c b/src/kmk/kmkbuiltin/kSubmit.c
index fde8e7c..b3132a4 100644
--- a/src/kmk/kmkbuiltin/kSubmit.c
+++ b/src/kmk/kmkbuiltin/kSubmit.c
@@ -1,4 +1,4 @@
-/* $Id: kSubmit.c 2894 2016-09-08 13:27:56Z bird $ */
+/* $Id: kSubmit.c 2959 2016-09-21 20:53:32Z bird $ */
/** @file
* kMk Builtin command - submit job to a kWorker.
*/
@@ -573,13 +573,14 @@ static PWORKERINSTANCE kSubmitSelectWorkSpawnNewIfNecessary(unsigned cBitsWorker
* @param papszEnvVars The environment vector.
* @param pszCwd The current directory.
* @param fWatcomBrainDamage The wcc/wcc386 workaround.
+ * @param fNoPchCaching Whether to disable precompiled header caching.
* @param papszPostCmdArgs The post command and it's arguments.
* @param cPostCmdArgs Number of post command argument, including the
* command. Zero if no post command scheduled.
* @param pcbMsg Where to return the message length.
*/
static void *kSubmitComposeJobMessage(const char *pszExecutable, char **papszArgs, char **papszEnvVars,
- const char *pszCwd, int fWatcomBrainDamage,
+ const char *pszCwd, int fWatcomBrainDamage, int fNoPchCaching,
char **papszPostCmdArgs, uint32_t cPostCmdArgs, uint32_t *pcbMsg)
{
size_t cbTmp;
@@ -614,7 +615,8 @@ static void *kSubmitComposeJobMessage(const char *pszExecutable, char **papszArg
cbMsg += strlen(papszEnvVars[i]) + 1;
cEnvVars = i;
- cbMsg += 1;
+ cbMsg += 1; /* fWatcomBrainDamage */
+ cbMsg += 1; /* fNoPchCaching */
cbMsg += sizeof(cPostCmdArgs);
for (i = 0; i < cPostCmdArgs; i++)
@@ -666,6 +668,7 @@ static void *kSubmitComposeJobMessage(const char *pszExecutable, char **papszArg
/* flags */
*pbCursor++ = fWatcomBrainDamage != 0;
+ *pbCursor++ = fNoPchCaching != 0;
/* post command */
memcpy(pbCursor, &cPostCmdArgs, sizeof(cPostCmdArgs));
@@ -1095,7 +1098,7 @@ static void kSubmitAtExitCallback(void)
DWORD cMsElapsed = GetTickCount() - msStartTick;
DWORD dwWait = WaitForMultipleObjects(cHandles <= MAXIMUM_WAIT_OBJECTS ? cHandles : MAXIMUM_WAIT_OBJECTS,
ahHandles, FALSE /*bWaitAll*/,
- cMsElapsed < 1000 ? 1000 - cMsElapsed + 16 : 16);
+ cMsElapsed < 5000 ? 5000 - cMsElapsed + 16 : 16);
if ( dwWait >= WAIT_OBJECT_0
&& dwWait <= WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS)
{
@@ -1122,7 +1125,7 @@ static void kSubmitAtExitCallback(void)
{
/* Terminate the whole bunch. */
cKillRaids++;
- if (cKillRaids <= 2)
+ if (cKillRaids == 1 && getenv("KMK_KSUBMIT_NO_KILL") == NULL)
{
fprintf(stderr, "kmk/kSubmit: Killing %u lingering worker processe(s)!\n", cHandles);
for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
@@ -1135,7 +1138,7 @@ static void kSubmitAtExitCallback(void)
else
{
fprintf(stderr, "kmk/kSubmit: Giving up on the last %u worker processe(s). :-(\n", cHandles);
- break;
+ return;
}
}
else
@@ -1161,210 +1164,11 @@ static void kSubmitAtExitCallback(void)
}
-/** The environment variable compare function.
- * We must use case insensitive compare on windows (Path vs PATH). */
-#ifdef KBUILD_OS_WINDOWS
-# define KSUBMIT_ENV_NCMP _strnicmp
-#else
-# define KSUBMIT_ENV_NCMP strncmp
-#endif
-
-
-/**
- * Handles the --set var=value option.
- *
- * @returns 0 on success, non-zero exit code on error.
- * @param papszEnv The environment vector.
- * @param pcEnvVars Pointer to the variable holding the number of
- * environment variables held by @a papszEnv.
- * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
- * environment vector.
- * @param cVerbosity The verbosity level.
- * @param pszValue The var=value string to apply.
- */
-static int kSubmitOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
- int cVerbosity, const char *pszValue)
-{
- const char *pszEqual = strchr(pszValue, '=');
- if (pszEqual)
- {
- char **papszEnv = *ppapszEnv;
- unsigned iEnvVar;
- unsigned cEnvVars = *pcEnvVars;
- size_t const cchVar = pszEqual - pszValue;
- for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
- {
- char *pszCur = papszEnv[iEnvVar];
- if ( KSUBMIT_ENV_NCMP(pszCur, pszValue, cchVar) == 0
- && pszCur[cchVar] == '=')
- {
- if (cVerbosity > 0)
- fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue);
- free(papszEnv[iEnvVar]);
- papszEnv[iEnvVar] = xstrdup(pszValue);
- break;
- }
- }
- if (iEnvVar == cEnvVars)
- {
- /* Append new variable. We probably need to resize the vector. */
- if ((cEnvVars + 2) > *pcAllocatedEnvVars)
- {
- *pcAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
- *ppapszEnv = papszEnv = (char **)xrealloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0]));
- }
- papszEnv[cEnvVars++] = xstrdup(pszValue);
- papszEnv[cEnvVars] = NULL;
- *pcEnvVars = cEnvVars;
- if (cVerbosity > 0)
- fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]);
- }
- else
- {
- /* Check for duplicates. */
- for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++)
- if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszValue, cchVar) == 0
- && papszEnv[iEnvVar][cchVar] == '=')
- {
- if (cVerbosity > 0)
- fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
- free(papszEnv[iEnvVar]);
- cEnvVars--;
- if (iEnvVar != cEnvVars)
- papszEnv[iEnvVar] = papszEnv[cEnvVars];
- papszEnv[cEnvVars] = NULL;
- iEnvVar--;
- }
- }
- }
- else
- return errx(1, "Missing '=': -E %s", pszValue);
-
- return 0;
-}
-
-
-/**
- * Handles the --unset var option.
- *
- * @returns 0 on success, non-zero exit code on error.
- * @param papszEnv The environment vector.
- * @param pcEnvVars Pointer to the variable holding the number of
- * environment variables held by @a papszEnv.
- * @param cVerbosity The verbosity level.
- * @param pszVarToRemove The name of the variable to remove.
- */
-static int kSubmitOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove)
-{
- if (strchr(pszVarToRemove, '=') == NULL)
- {
- unsigned cRemoved = 0;
- size_t const cchVar = strlen(pszVarToRemove);
- unsigned cEnvVars = *pcEnvVars;
- unsigned iEnvVar;
-
- for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
- if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszVarToRemove, cchVar) == 0
- && papszEnv[iEnvVar][cchVar] == '=')
- {
- if (cVerbosity > 0)
- fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n"
- : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
- free(papszEnv[iEnvVar]);
- cEnvVars--;
- if (iEnvVar != cEnvVars)
- papszEnv[iEnvVar] = papszEnv[cEnvVars];
- papszEnv[cEnvVars] = NULL;
- cRemoved++;
- iEnvVar--;
- }
- *pcEnvVars = cEnvVars;
-
- if (cVerbosity > 0 && !cRemoved)
- fprintf(stderr, "kSubmit: not found '%s'\n", pszVarToRemove);
- }
- else
- return errx(1, "Found invalid variable name character '=' in: -U %s", pszVarToRemove);
- return 0;
-}
-
-
-
-/**
- * Handles the --chdir dir option.
- *
- * @returns 0 on success, non-zero exit code on error.
- * @param pszCwd The CWD buffer. Contains current CWD on input,
- * modified by @a pszValue on output.
- * @param cbCwdBuf The size of the CWD buffer.
- * @param pszValue The --chdir value to apply.
- */
-static int kSubmitOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue)
-{
- size_t cchNewCwd = strlen(pszValue);
- size_t offDst;
- if (cchNewCwd)
- {
-#ifdef HAVE_DOS_PATHS
- if (*pszValue == '/' || *pszValue == '\\')
- {
- if (pszValue[1] == '/' || pszValue[1] == '\\')
- offDst = 0; /* UNC */
- else if (pszCwd[1] == ':' && isalpha(pszCwd[0]))
- offDst = 2; /* Take drive letter from CWD. */
- else
- return errx(1, "UNC relative CWD not implemented: cur='%s' new='%s'", pszCwd, pszValue);
- }
- else if ( pszValue[1] == ':'
- && isalpha(pszValue[0]))
- {
- if (pszValue[2] == '/'|| pszValue[2] == '\\')
- offDst = 0; /* DOS style absolute path. */
- else if ( pszCwd[1] == ':'
- && tolower(pszCwd[0]) == tolower(pszValue[0]) )
- {
- pszValue += 2; /* Same drive as CWD, append drive relative path from value. */
- cchNewCwd -= 2;
- offDst = strlen(pszCwd);
- }
- else
- {
- /* Get current CWD on the specified drive and append value. */
- int iDrive = tolower(pszValue[0]) - 'a' + 1;
- if (!_getdcwd(iDrive, pszCwd, cbCwdBuf))
- return err(1, "_getdcwd(%d,,) failed", iDrive);
- pszValue += 2;
- cchNewCwd -= 2;
- }
- }
-#else
- if (*pszValue == '/')
- offDst = 0;
-#endif
- else
- offDst = strlen(pszCwd); /* Relative path, append to the existing CWD value. */
-
- /* Do the copying. */
-#ifdef HAVE_DOS_PATHS
- if (offDst > 0 && pszCwd[offDst - 1] != '/' && pszCwd[offDst - 1] != '\\')
-#else
- if (offDst > 0 && pszCwd[offDst - 1] != '/')
-#endif
- pszCwd[offDst++] = '/';
- if (offDst + cchNewCwd >= cbCwdBuf)
- return errx(1, "Too long CWD: %*.*s%s", offDst, offDst, pszCwd, pszValue);
- memcpy(&pszCwd[offDst], pszValue, cchNewCwd + 1);
- }
- /* else: relative, no change - quitely ignore. */
- return 0;
-}
-
-
static int usage(FILE *pOut, const char *argv0)
{
fprintf(pOut,
"usage: %s [-Z|--zap-env] [-E|--set <var=val>] [-U|--unset <var=val>]\n"
- " [-C|--chdir <dir>] [--wcc-brain-damage]\n"
+ " [-C|--chdir <dir>] [--wcc-brain-damage] [--no-pch-caching]\n"
" [-3|--32-bit] [-6|--64-bit] [-v]\n"
" [-P|--post-cmd <cmd> [args]] -- <program> [args]\n"
" or: %s --help\n"
@@ -1387,6 +1191,8 @@ static int usage(FILE *pOut, const char *argv0)
" --wcc-brain-damage\n"
" Works around wcc and wcc386 (Open Watcom) not following normal\n"
" quoting conventions on Windows, OS/2, and DOS.\n"
+ " --no-pch-caching\n"
+ " Do not cache precompiled header files because they're being created.\n"
" -v,--verbose\n"
" More verbose execution.\n"
" -P|--post-cmd <cmd> ...\n"
@@ -1400,7 +1206,7 @@ static int usage(FILE *pOut, const char *argv0)
"\n"
,
argv0, argv0, argv0);
- return 1;
+ return 2;
}
@@ -1413,11 +1219,11 @@ int kmk_builtin_kSubmit(int argc, char **argv, char **envp, struct child *pChild
unsigned cEnvVars;
char **papszEnv = NULL;
const char *pszExecutable = NULL;
- const char *pszCwd = NULL;
int iPostCmd = argc;
int cPostCmdArgs = 0;
unsigned cBitsWorker = g_cArchBits;
int fWatcomBrainDamage = 0;
+ int fNoPchCaching = 0;
int cVerbosity = 0;
size_t const cbCwdBuf = GET_PATH_MAX;
PATH_VAR(szCwd);
@@ -1476,6 +1282,12 @@ int kmk_builtin_kSubmit(int argc, char **argv, char **envp, struct child *pChild
continue;
}
+ if (strcmp(pszArg, "no-pch-caching") == 0)
+ {
+ fNoPchCaching = 1;
+ continue;
+ }
+
/* convert to short. */
if (strcmp(pszArg, "help") == 0)
chOpt = 'h';
@@ -1502,7 +1314,7 @@ int kmk_builtin_kSubmit(int argc, char **argv, char **envp, struct child *pChild
chOpt = 'e';
else
{
- errx(1, "Unknown option: '%s'", pszArg - 2);
+ errx(2, "Unknown option: '%s'", pszArg - 2);
return usage(stderr, argv[0]);
}
pszArg = "";
@@ -1541,20 +1353,20 @@ int kmk_builtin_kSubmit(int argc, char **argv, char **envp, struct child *pChild
break;
case 'E':
- rcExit = kSubmitOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
+ rcExit = kBuiltinOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
pChild->environment = papszEnv;
if (rcExit == 0)
break;
return rcExit;
case 'U':
- rcExit = kSubmitOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue);
+ rcExit = kBuiltinOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue);
if (rcExit == 0)
break;
return rcExit;
case 'C':
- rcExit = kSubmitOptChDir(szCwd, cbCwdBuf, pszValue);
+ rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue);
if (rcExit == 0)
break;
return rcExit;
@@ -1612,7 +1424,8 @@ int kmk_builtin_kSubmit(int argc, char **argv, char **envp, struct child *pChild
{
uint32_t cbMsg;
void *pvMsg = kSubmitComposeJobMessage(pszExecutable, &argv[iArg], papszEnv, szCwd,
- fWatcomBrainDamage, &argv[iPostCmd], cPostCmdArgs, &cbMsg);
+ fWatcomBrainDamage, fNoPchCaching,
+ &argv[iPostCmd], cPostCmdArgs, &cbMsg);
PWORKERINSTANCE pWorker = kSubmitSelectWorkSpawnNewIfNecessary(cBitsWorker, cVerbosity);
if (pWorker)
{
diff --git a/src/kmk/kmkbuiltin/md5sum.c b/src/kmk/kmkbuiltin/md5sum.c
index c56dc07..373cf5d 100644
--- a/src/kmk/kmkbuiltin/md5sum.c
+++ b/src/kmk/kmkbuiltin/md5sum.c
@@ -1,4 +1,4 @@
-/* $Id: md5sum.c 2414 2010-09-12 18:42:06Z bird $ */
+/* $Id: md5sum.c 2984 2016-11-01 18:24:11Z bird $ */
/** @file
* md5sum.
*/
@@ -299,26 +299,45 @@ static int calc_md5sum(void *pvFile, unsigned char pDigest[16], unsigned fProgre
{
int cb;
int rc = 0;
- char abBuf[32*1024];
struct MD5Context Ctx;
unsigned uPercent = 0;
- double cbFile = 0.0;
double off = 0.0;
-
- if (fProgress)
+ double cbFile = size_file(pvFile);
+
+ /* Get a decent sized buffer assuming we'll be spending more time reading
+ from the storage than doing MD5 sums. (2MB was choosen based on recent
+ SATA storage benchmarks which used that block size for sequential
+ tests.) We align the buffer address on a 16K boundrary to avoid most
+ transfer alignment issues. */
+ char *pabBufAligned;
+ size_t const cbBufAlign = 16*1024 - 1;
+ size_t const cbBufMax = 2048*1024;
+ size_t cbBuf = cbFile >= cbBufMax ? cbBufMax : ((size_t)cbFile + cbBufAlign) & ~(size_t)cbBufAlign;
+ char *pabBuf = (char *)malloc(cbBuf + cbBufAlign);
+ if (pabBuf)
+ pabBufAligned = (char *)(((uintptr_t)pabBuf + cbBufAlign) & ~(uintptr_t)cbBufAlign );
+ else
{
- cbFile = size_file(pvFile);
- if (cbFile < 1024*1024)
- fProgress = 0;
+ do
+ {
+ cbBuf /= 2;
+ pabBuf = (char *)malloc(cbBuf);
+ } while (!pabBuf && cbBuf > 4096);
+ if (!pabBuf)
+ return ENOMEM;
+ pabBufAligned = pabBuf;
}
+ if (cbFile < cbBuf * 4)
+ fProgress = 0;
+
MD5Init(&Ctx);
for (;;)
{
/* process a chunk. */
- cb = read_file(pvFile, abBuf, sizeof(abBuf));
+ cb = read_file(pvFile, pabBufAligned, cbBuf);
if (cb > 0)
- MD5Update(&Ctx, (unsigned char *)&abBuf[0], cb);
+ MD5Update(&Ctx, (unsigned char *)pabBufAligned, cb);
else if (!cb)
break;
else
@@ -348,6 +367,7 @@ static int calc_md5sum(void *pvFile, unsigned char pDigest[16], unsigned fProgre
if (fProgress)
printf("\b\b\b\b \b\b\b\b");
+ free(pabBuf);
return rc;
}
@@ -571,7 +591,7 @@ static int md5sum_file(const char *pszFilename, unsigned fText, unsigned fQuiet,
void *pvFile;
/*
- * Calcuate and print the MD5 sum for one file.
+ * Calculate and print the MD5 sum for one file.
*/
pvFile = open_file(pszFilename, fText);
if (pvFile)
diff --git a/src/kmk/kmkbuiltin/redirect.c b/src/kmk/kmkbuiltin/redirect.c
index cd4af45..5f5a558 100644
--- a/src/kmk/kmkbuiltin/redirect.c
+++ b/src/kmk/kmkbuiltin/redirect.c
@@ -1,4 +1,4 @@
-/* $Id: redirect.c 2839 2016-08-25 21:46:44Z bird $ */
+/* $Id: redirect.c 2916 2016-09-15 11:41:42Z bird $ */
/** @file
* kmk_redirect - Do simple program <-> file redirection (++).
*/
@@ -29,20 +29,37 @@
#ifdef __APPLE__
# define _POSIX_C_SOURCE 1 /* 10.4 sdk and unsetenv */
#endif
-#include "config.h"
+#include "make.h"
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
+#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
+# include <process.h>
+#endif
#if defined(_MSC_VER)
# include <ctype.h>
# include <io.h>
-# include <direct.h>
-# include <process.h>
# include "quote_argv.h"
#else
# include <unistd.h>
+# include <spawn.h>
+#endif
+
+#include <k/kDefs.h>
+#include <k/kTypes.h>
+#include "err.h"
+#include "kbuild_version.h"
+#include "kmkbuiltin.h"
+#ifdef KMK
+# ifdef KBUILD_OS_WINDOWS
+# include "sub_proc.h"
+# include "pathstuff.h"
+# endif
+# include "job.h"
+# include "variable.h"
#endif
#ifdef __OS2__
@@ -53,12 +70,13 @@
# endif
#endif
+#if !defined(KBUILD_OS_WINDOWS) && !defined(KBUILD_OS_OS2)
+# define USE_POSIX_SPAWN
+#endif
+
-/*********************************************************************************************************************************
-* Global Variables *
-*********************************************************************************************************************************/
-/** Number of times the '-v' switch was seen. */
-static unsigned g_cVerbosity = 0;
+/* String + strlen tuple. */
+#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
#if defined(_MSC_VER)
@@ -106,8 +124,11 @@ static const char *name(const char *pszName)
static int usage(FILE *pOut, const char *argv0)
{
+ argv0 = name(argv0);
fprintf(pOut,
- "usage: %s [-[rwa+tb]<fd> <file>] [-c<fd>] [-Z] [-E <var=val>] [-C <dir>] [--wcc-brain-damage] [-v] -- <program> [args]\n"
+ "usage: %s [-[rwa+tb]<fd> <file>] [-d<fd>=<src-fd>] [-c<fd>]\n"
+ " [-Z] [-E <var=val>] [-C <dir>] [--wcc-brain-damage]\n"
+ " [-v] -- <program> [args]\n"
" or: %s --help\n"
" or: %s --version\n"
"\n"
@@ -117,6 +138,9 @@ static int usage(FILE *pOut, const char *argv0)
" o = stdout\n"
" e = stderr\n"
"\n"
+ "The -d switch duplicate the right hand file descriptor (src-fd) to the left\n"
+ "hand side one (fd).\n"
+ "\n"
"The -c switch will close the specified file descriptor.\n"
"\n"
"The -Z switch zaps the environment.\n"
@@ -138,536 +162,1497 @@ static int usage(FILE *pOut, const char *argv0)
"/usr/bin/env on steroids.\n"
,
argv0, argv0, argv0);
- return 1;
+ return 2;
}
-int main(int argc, char **argv, char **envp)
+/**
+ * Decoded file descriptor operations.
+ */
+typedef struct REDIRECTORDERS
{
- int i;
- int j;
-#if defined(_MSC_VER)
- intptr_t rc;
+ enum {
+ kRedirectOrder_Invalid = 0,
+ kRedirectOrder_Close,
+ kRedirectOrder_Open,
+ kRedirectOrder_Dup,
+ } enmOrder;
+ /** The target file handle. */
+ int fdTarget;
+ /** The source file name, -1 on close only.
+ * This is an opened file if pszFilename is set. */
+ int fdSource;
+ /** Whether to remove the file on failure cleanup. */
+ int fRemoveOnFailure;
+ /** The open flags (for O_TEXT/O_BINARY) on windows. */
+ int fOpen;
+ /** The filename - NULL if close only. */
+ const char *pszFilename;
+#ifndef USE_POSIX_SPAWN
+ /** Saved file descriptor. */
+ int fdSaved;
+ /** Saved flags. */
+ int fSaved;
#endif
- FILE *pStdErr = stderr;
- FILE *pStdOut = stdout;
- int fWatcomBrainDamage = 0;
+} REDIRECTORDERS;
- /*
- * Parse arguments.
- */
- if (argc <= 1)
- return usage(pStdErr, name(argv[0]));
- for (i = 1; i < argc; i++)
+
+#ifdef _MSC_VER
+
+/**
+ * Safe way of getting the OS handle of a file descriptor without triggering
+ * invalid parameter handling.
+ *
+ * @returns The handle value if open, INVALID_HANDLE_VALUE if not.
+ * @param fd The file descriptor in question.
+ */
+static HANDLE mscGetOsHandle(int fd)
+{
+ intptr_t hHandle;
+ _invalid_parameter_handler pfnOld = _get_invalid_parameter_handler();
+ _set_invalid_parameter_handler(ignore_invalid_parameter);
+ hHandle = _get_osfhandle(fd);
+ _set_invalid_parameter_handler(pfnOld);
+ return hHandle != -1 ? (HANDLE)hHandle : INVALID_HANDLE_VALUE;
+}
+
+/**
+ * Checks if the specified file descriptor is open.
+ *
+ * @returns K_TRUE if open, K_FALSE if not.
+ * @param fd The file descriptor in question.
+ */
+static KBOOL mscIsOpenFile(int fd)
+{
+ return mscGetOsHandle(fd) != INVALID_HANDLE_VALUE;
+}
+
+/**
+ * Checks if the native handle is inheritable.
+ *
+ * @returns K_TRUE if it is, K_FALSE if it isn't or isn't a valid handle.
+ * @param hHandle The native handle.
+ */
+static KBOOL mscIsNativeHandleInheritable(HANDLE hHandle)
+{
+ DWORD fFlags = 0;
+ if (GetHandleInformation(hHandle, &fFlags))
+ return (fFlags & HANDLE_FLAG_INHERIT) != 0;
+ return K_FALSE;
+}
+
+/**
+ * Checks if the file descriptor is inheritable or not.
+ *
+ * @returns K_TRUE if it is, K_FALSE if it isn't or isn't a valid descriptor.
+ * @param fd The file descriptor in question.
+ */
+static KBOOL mscIsInheritable(int fd)
+{
+ HANDLE hHandle = mscGetOsHandle(fd);
+ if (hHandle != INVALID_HANDLE_VALUE)
+ return mscIsNativeHandleInheritable(hHandle);
+ return K_FALSE;
+}
+
+/**
+ * A dup3 like function.
+ *
+ * @returns fdNew on success, -1 on failure w/ error details written to pStdErr.
+ * @param fdSource The source descriptor.
+ * @param fdNew The new descriptor.
+ * @param fFlags The inherit and text/binary mode flag.
+ * @param pStdErr Working stderr to write error details to.
+ */
+static int mscDup3(int fdSource, int fdNew, int fFlags, FILE *pStdErr)
+{
+ if (!fFlags & _O_NOINHERIT)
{
- if (argv[i][0] == '-')
- {
- int fd;
- int fdOpened;
- int fOpen;
- char *psz = &argv[i][1];
- if (*psz == '-')
- {
- /* '--' ? */
- if (!psz[1])
- {
- i++;
- break;
- }
+ /* ASSUMES fFlags doesn't include any changing _O_TEXT/_O_BINARY. */
+ int fdDup = _dup2(fdSource, fdNew);
+ if (fdDup != -1)
+ return fdDup;
+ fprintf(pStdErr, "%s: _dup2(%d,%d) failed: %s\n", g_progname, fdSource, fdNew, strerror(errno));
+ }
+ else
+ {
+ HANDLE hSource = mscGetOsHandle(fdSource);
+ unsigned cTries = 0;
+ int aFdTries[48];
- /* convert to short. */
- if (!strcmp(psz, "-help"))
- psz = "h";
- else if (!strcmp(psz, "-version"))
- psz = "V";
- else if (!strcmp(psz, "-env"))
- psz = "E";
- else if (!strcmp(psz, "-chdir"))
- psz = "C";
- else if (!strcmp(psz, "-zap-env"))
- psz = "Z";
- else if (!strcmp(psz, "-close"))
- psz = "c";
- else if (!strcmp(psz, "-wcc-brain-damage"))
- {
- fWatcomBrainDamage = 1;
- continue;
- }
- }
+ if (hSource != INVALID_HANDLE_VALUE)
+ {
+ HANDLE hCurProc = GetCurrentProcess();
+ BOOL fInherit = !(fFlags & _O_NOINHERIT);
/*
- * Deal with the obligatory help and version switches first.
+ * Make sure the old descriptor is closed and can be used again.
*/
- if (*psz == 'h')
- {
- usage(pStdOut, name(argv[0]));
- return 0;
- }
- if (*psz == 'V')
- {
- printf("kmk_redirect - kBuild version %d.%d.%d (r%u)\n"
- "Copyright (C) 2007-2012 knut st. osmundsen\n",
- KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH,
- KBUILD_SVN_REV);
- return 0;
- }
+ _invalid_parameter_handler pfnOld = _get_invalid_parameter_handler();
+ _set_invalid_parameter_handler(ignore_invalid_parameter);
+ close(fdNew);
+ _set_invalid_parameter_handler(pfnOld);
/*
- * Environment switch?
+ * Duplicate the source handle till we've got a match.
*/
- if (*psz == 'E')
+ for (;;)
{
- psz++;
- if (*psz == ':' || *psz == '=')
- psz++;
- else
- {
- if (i + 1 >= argc)
- {
- fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]);
- return 1;
- }
- psz = argv[++i];
- }
-#ifdef __OS2__
- if ( !strncmp(psz, "BEGINLIBPATH=", sizeof("BEGINLIBPATH=") - 1)
- || !strncmp(psz, "ENDLIBPATH=", sizeof("ENDLIBPATH=") - 1)
- || !strncmp(psz, "LIBPATHSTRICT=", sizeof("LIBPATHSTRICT=") - 1))
- {
- ULONG ulVar = *psz == 'B' ? BEGIN_LIBPATH
- : *psz == 'E' ? END_LIBPATH
- : LIBPATHSTRICT;
- const char *pszVal = strchr(psz, '=') + 1;
- APIRET rc = DosSetExtLIBPATH(pszVal, ulVar);
- if (rc)
- {
- fprintf(pStdErr, "%s: error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu\n",
- name(argv[0]), pszVal, pszVal - psz - 1, psz, ulVar, rc);
- return 1;
- }
- }
- else
-#endif /* __OS2__ */
+ HANDLE hDup = INVALID_HANDLE_VALUE;
+ if (DuplicateHandle(hCurProc, hSource, hCurProc, &hDup, 0 /* DesiredAccess */,
+ fInherit, DUPLICATE_SAME_ACCESS))
{
- const char *pchEqual = strchr(psz, '=');
- if (pchEqual && pchEqual[1] != '\0')
+ int fdDup = _open_osfhandle((intptr_t)hDup, fFlags);
+ if (fdDup != -1)
{
- if (putenv(psz))
+ if (fdDup == fdNew)
{
- fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno));
- return 1;
+ while (cTries-- > 0)
+ close(aFdTries[cTries]);
+ return fdDup;
}
+
+ aFdTries[cTries++] = fdDup;
+ if ( fdDup < fdNew
+ && cTries < K_ELEMENTS(aFdTries))
+ continue;
+ fprintf(pStdErr, "%s: mscDup3(%d,%d): giving up! (last fdDup=%d)\n",
+ g_progname, fdSource, fdNew, fdDup);
}
else
{
- size_t cchVar = pchEqual ? (size_t)(pchEqual - psz) : strlen(psz);
- char *pszCopy = (char *)malloc(cchVar + 2);
- memcpy(pszCopy, psz, cchVar);
-
-#if defined(_MSC_VER) || defined(__OS2__)
- pszCopy[cchVar] = '=';
- pszCopy[cchVar + 1] = '\0';
- if (putenv(pszCopy))
- {
- fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), pszCopy, strerror(errno));
- return 1;
- }
-#else
- pszCopy[cchVar] = '\0';
- if (unsetenv(pszCopy))
- {
- fprintf(pStdErr, "%s: error: unsetenv(\"%s\"): %s\n", name(argv[0]), pszCopy, strerror(errno));
- return 1;
- }
-#endif
- free(pszCopy);
+ fprintf(pStdErr, "%s: _open_osfhandle(%#x) failed: %u\n", g_progname, hDup, strerror(errno));
+ CloseHandle(hDup);
}
}
- continue;
- }
-
- /*
- * Change directory switch?
- */
- if (*psz == 'C')
- {
- psz++;
- if (*psz == ':' || *psz == '=')
- psz++;
else
- {
- if (i + 1 >= argc)
- {
- fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]);
- return 1;
- }
- psz = argv[++i];
- }
- if (!chdir(psz))
- continue;
-#ifdef _MSC_VER
- {
- /* drop trailing slash if any. */
- size_t cch = strlen(psz);
- if ( cch > 2
- && (psz[cch - 1] == '/' || psz[cch - 1] == '\\')
- && psz[cch - 1] != ':')
- {
- int rc2;
- char *pszCopy = strdup(psz);
- do pszCopy[--cch] = '\0';
- while ( cch > 2
- && (pszCopy[cch - 1] == '/' || pszCopy[cch - 1] == '\\')
- && pszCopy[cch - 1] != ':');
- rc2 = chdir(pszCopy);
- free(pszCopy);
- if (!rc2)
- continue;
- }
- }
-#endif
- fprintf(pStdErr, "%s: error: chdir(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno));
- return 1;
+ fprintf(pStdErr, "%s: DuplicateHandle(%#x) failed: %u\n", g_progname, hSource, GetLastError());
+ break;
}
- /*
- * Zap environment switch?
- * This is a bit of a hack.
- */
- if (*psz == 'Z')
- {
- unsigned j = 0;
- while (envp[j] != NULL)
- j++;
- while (j-- > 0)
- {
- char *pszEqual = strchr(envp[j], '=');
- char *pszCopy;
+ while (cTries-- > 0)
+ close(aFdTries[cTries]);
+ }
+ else
+ fprintf(pStdErr, "%s: mscDup3(%d,%d): source descriptor is invalid!\n", g_progname, fdSource, fdNew);
+ }
+ return -1;
+}
- if (pszEqual)
- *pszEqual = '\0';
- pszCopy = strdup(envp[j]);
- if (pszEqual)
- *pszEqual = '=';
+#endif /* _MSC_VER */
-#if defined(_MSC_VER) || defined(__OS2__)
- putenv(pszCopy);
+static KBOOL kRedirectHasConflict(int fd, unsigned cOrders, REDIRECTORDERS *paOrders)
+{
+ while (cOrders-- > 0)
+ if (paOrders[cOrders].fdTarget == fd)
+ return K_TRUE;
+ return K_FALSE;
+}
+
+
+/**
+ * Creates a file descriptor for @a pszFilename that does not conflict with any
+ * previous orders.
+ *
+ * We need to be careful that there isn't a close or dup targetting the
+ * temporary file descriptor we return. Also, we need to take care with the
+ * descriptor's inheritability. It should only be inheritable if the returned
+ * descriptor matches the target descriptor (@a fdTarget).
+ *
+ * @returns File descriptor on success, -1 & err/errx on failure.
+ *
+ * The returned file descriptor is not inherited (i.e. close-on-exec),
+ * unless it matches @a fdTarget
+ *
+ * @param pszFilename The filename to open.
+ * @param fOpen The open flags.
+ * @param fMode The file creation mode (if applicable).
+ * @param cOrders The number of orders.
+ * @param paOrders The order array.
+ * @param fRemoveOnFailure Whether to remove the file on failure.
+ * @param fdTarget The target descriptor.
+ */
+static int kRedirectOpenWithoutConflict(const char *pszFilename, int fOpen, mode_t fMode,
+ unsigned cOrders, REDIRECTORDERS *paOrders, int fRemoveOnFailure, int fdTarget)
+{
+#ifdef _O_NOINHERIT
+ int const fNoInherit = _O_NOINHERIT;
+#elif defined(O_NOINHERIT)
+ int const fNoInherit = O_NOINHERIT;
+#elif defined(O_CLOEXEC)
+ int const fNoInherit = O_CLOEXEC;
#else
- unsetenv(pszCopy);
+# error "port me"
+#endif
+ int aFdTries[32];
+ int cTries;
+ int fdOpened;
+
+#ifdef KBUILD_OS_WINDOWS
+ if (strcmp(pszFilename, "/dev/null") == 0)
+ pszFilename = "nul";
+#endif
+
+ /* Open it first. */
+ fdOpened = open(pszFilename, fOpen | fNoInherit, fMode);
+ if (fdOpened < 0)
+ return err(-1, "open(%s,%#x,) failed", pszFilename, fOpen);
+
+ /* Check for conflicts. */
+ if (!kRedirectHasConflict(fdOpened, cOrders, paOrders))
+ {
+ if (fdOpened != fdTarget)
+ return fdOpened;
+#ifndef _MSC_VER /* Stupid, stupid MSVCRT! No friggin way of making a handle inheritable (or not). */
+ if (fcntl(fdOpened, F_SETFD, FD_CLOEXEC) != -1)
+ return fdOpened;
+#endif
+ }
+
+ /*
+ * Do conflict resolving.
+ */
+ cTries = 1;
+ aFdTries[cTries++] = fdOpened;
+ while (cTries < K_ELEMENTS(aFdTries))
+ {
+ fdOpened = open(pszFilename, fOpen | fNoInherit, fMode);
+ if (fdOpened >= 0)
+ {
+ if ( !kRedirectHasConflict(fdOpened, cOrders, paOrders)
+#ifdef _MSC_VER
+ && fdOpened != fdTarget
+#endif
+ )
+ {
+#ifndef _MSC_VER
+ if ( fdOpened != fdTarget
+ || fcntl(fdOpened, F_SETFD, FD_CLOEXEC) != -1)
#endif
- free(pszCopy);
+ {
+ while (cTries-- > 0)
+ close(aFdTries[cTries]);
+ return fdOpened;
}
- continue;
}
- /*
- * Verbose operation switch?
- */
- if (*psz == 'v')
+ }
+ else
+ {
+ err(-1, "open(%s,%#x,) #%u failed", pszFilename, cTries + 1, fOpen);
+ break;
+ }
+ aFdTries[cTries++] = fdOpened;
+ }
+
+ /*
+ * Give up.
+ */
+ if (fdOpened >= 0)
+ errx(-1, "failed to find a conflict free file descriptor for '%s'!", pszFilename);
+
+ while (cTries-- > 0)
+ close(aFdTries[cTries]);
+ return -1;
+}
+
+#ifndef USE_POSIX_SPAWN
+
+/**
+ * Saves a file handle to one which isn't inherited and isn't affected by the
+ * file orders.
+ *
+ * @returns 0 on success, non-zero exit code on failure.
+ * @param pToSave Pointer to the file order to save the target
+ * descriptor of.
+ * @param cOrders Number of file orders.
+ * @param paOrders The array of file orders.
+ * @param ppWorkingStdErr Pointer to a pointer to a working stderr. This will
+ * get replaced if we're saving stderr, so that we'll
+ * keep having a working one to report failures to.
+ */
+static int kRedirectSaveHandle(REDIRECTORDERS *pToSave, unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr)
+{
+ int fdToSave = pToSave->fdTarget;
+ int rcRet = 10;
+
+ /*
+ * First, check if there's actually handle here that needs saving.
+ */
+# ifdef KBUILD_OS_WINDOWS
+ HANDLE hToSave = mscGetOsHandle(fdToSave);
+ if (hToSave != INVALID_HANDLE_VALUE)
+ {
+ pToSave->fSaved = _setmode(fdToSave, _O_BINARY);
+ if (pToSave->fSaved != _O_BINARY)
+ _setmode(fdToSave, pToSave->fSaved);
+ if (!mscIsNativeHandleInheritable(hToSave))
+ pToSave->fSaved |= _O_NOINHERIT;
+ }
+ if (hToSave != INVALID_HANDLE_VALUE)
+# else
+ pToSave->fSaved = fcntl(pToSave->fdTarget, F_GETFD, 0);
+ if (pToSave->fSaved != -1)
+# endif
+ {
+ /*
+ * Try up to 32 times to get a duplicate descriptor that doesn't conflict.
+ */
+# ifdef KBUILD_OS_WINDOWS
+ HANDLE hCurProc = GetCurrentProcess();
+# endif
+ int aFdTries[32];
+ int cTries = 0;
+ do
+ {
+ /* Duplicate the handle (windows makes this complicated). */
+ int fdDup;
+# ifdef KBUILD_OS_WINDOWS
+ HANDLE hDup = INVALID_HANDLE_VALUE;
+ if (!DuplicateHandle(hCurProc, hToSave, hCurProc, &hDup, 0 /* DesiredAccess */,
+ FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
{
- g_cVerbosity++;
- continue;
+ fprintf(*ppWorkingStdErr, "%s: DuplicateHandle(%#x) failed: %u\n", g_progname, hToSave, GetLastError());
+ break;
}
-
- /*
- * Close the specified file descriptor (no stderr/out/in aliases).
- */
- if (*psz == 'c')
+ fdDup = _open_osfhandle((intptr_t)hDup, pToSave->fSaved | _O_NOINHERIT);
+ if (fdDup == -1)
+ {
+ fprintf(*ppWorkingStdErr, "%s: _open_osfhandle(%#x) failed: %u\n", g_progname, hDup, strerror(errno));
+ CloseHandle(hDup);
+ break;
+ }
+# else
+ fdDup = dup(fdToSave);
+ if (fdDup == -1)
+ {
+ fprintf(*ppWorkingStdErr, "%s: dup(%#x) failed: %u\n", g_progname, fdToSave, strerror(errno));
+ break;
+ }
+#endif
+ /* Is the duplicate usable? */
+ if (!kRedirectHasConflict(fdDup, cOrders, paOrders))
{
- psz++;
- if (!*psz)
+ pToSave->fdSaved = fdDup;
+ if ( *ppWorkingStdErr == stderr
+ && fdToSave == fileno(*ppWorkingStdErr))
{
- i++;
- if (i >= argc)
+ *ppWorkingStdErr = fdopen(fdDup, "wt");
+ if (*ppWorkingStdErr == NULL)
{
- fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0]));
- return 1;
+ fprintf(stderr, "%s: fdopen(%d,\"wt\") failed: %s\n", g_progname, fdDup, strerror(errno));
+ *ppWorkingStdErr = stderr;
+ close(fdDup);
+ break;
}
- psz = argv[i];
}
+ rcRet = 0;
+ break;
+ }
- fd = (int)strtol(psz, &psz, 0);
- if (!fd || *psz)
- {
- fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]);
- return 1;
+ /* Not usuable, stash it and try again. */
+ aFdTries[cTries++] = fdDup;
+ } while (cTries < K_ELEMENTS(aFdTries));
- }
- if (fd < 0)
+ /*
+ * Clean up unused duplicates.
+ */
+ while (cTries-- > 0)
+ close(aFdTries[cTries]);
+ }
+ else
+ {
+ /*
+ * Nothing to save.
+ */
+ pToSave->fdSaved = -1;
+ rcRet = 0;
+ }
+ return rcRet;
+}
+
+
+/**
+ * Cleans up the file operation orders.
+ *
+ * This does not restore stuff, just closes handles we've opened for the guest.
+ *
+ * @param cOrders Number of file operation orders.
+ * @param paOrders The file operation orders.
+ * @param fFailed Set if it's a failure.
+ */
+static void kRedirectCleanupFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, KBOOL fFailure)
+{
+ unsigned i = cOrders;
+ while (i-- > 0)
+ {
+ if ( paOrders[i].enmOrder == kRedirectOrder_Open
+ && paOrders[i].fdSource != -1)
+ {
+ close(paOrders[i].fdSource);
+ paOrders[i].fdSource = -1;
+ if ( fFailure
+ && paOrders[i].fRemoveOnFailure
+ && paOrders[i].pszFilename)
+ remove(paOrders[i].pszFilename);
+ }
+ }
+}
+
+
+/**
+ * Restores the target file descriptors affected by the file operation orders.
+ *
+ * @param cOrders Number of file operation orders.
+ * @param paOrders The file operation orders.
+ * @param ppWorkingStdErr Pointer to a pointer to the working stderr. If this
+ * is one of the saved file descriptors, we'll restore
+ * it to stderr.
+ */
+static void kRedirectRestoreFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr)
+{
+ int iSavedErrno = errno;
+ unsigned i = cOrders;
+ while (i-- > 0)
+ {
+ if (paOrders[i].fdSaved != -1)
+ {
+ KBOOL fRestoreStdErr = *ppWorkingStdErr != stderr
+ && paOrders[i].fdSaved == fileno(*ppWorkingStdErr);
+
+#ifdef KBUILD_OS_WINDOWS
+ if (mscDup3(paOrders[i].fdSaved, paOrders[i].fdTarget, paOrders[i].fSaved, *ppWorkingStdErr) != -1)
+#else
+ if (dup2(paOrders[i].fdSaved, paOrders[i].fdTarget) != -1)
+#endif
+ {
+ close(paOrders[i].fdSaved);
+ paOrders[i].fdSaved = -1;
+
+ if (fRestoreStdErr)
{
- fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]);
- return 1;
+ *ppWorkingStdErr = stderr;
+ assert(fileno(stderr) == paOrders[i].fdTarget);
}
- /** @todo deal with stderr */
- safeCloseFd(fd);
- continue;
}
+#ifndef KBUILD_OS_WINDOWS
+ else
+ fprintf(*ppWorkingStdErr, "%s: dup2(%d,%d) failed: %s",
+ g_progname, paOrders[i].fdSaved, paOrders[i].fdTarget, strerror(errno));
+#endif
+ }
+
+#ifndef KBUILD_OS_WINDOWS
+ if (paOrders[i].fSaved != -1)
+ {
+ if (fcntl(paOrders[i].fdTarget, F_SETFD, paOrders[i].fSaved & FD_CLOEXEC) == -1)
+ paOrders[i].fSaved = -1;
+ else
+ fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_SETFD,%s) failed: %s",
+ g_progname, paOrders[i].fdTarget, paOrders[i].fSaved & FD_CLOEXEC ? "FD_CLOEXEC" : "0", strerror(errno));
+ }
+#endif
+ }
+ errno = iSavedErrno;
+}
- /*
- * Parse a file descriptor argument.
- */
- /* mode */
- switch (*psz)
+/**
+ * Executes the file operation orders.
+ *
+ * @returns 0 on success, exit code on failure.
+ * @param cOrders Number of file operation orders.
+ * @param paOrders File operation orders to execute.
+ * @param ppWorkingStdErr Where to return a working stderr (mainly for
+ * kRedirectRestoreFdOrders).
+ */
+static int kRedirectExecFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr)
+{
+ unsigned i;
+
+ *ppWorkingStdErr = stderr;
+ for (i = 0; i < cOrders; i++)
+ {
+ int rcExit = 10;
+ switch (paOrders[i].enmOrder)
+ {
+ case kRedirectOrder_Close:
{
- case 'r':
- psz++;
- if (*psz == '+')
+ /* If the handle isn't used by any of the following operation,
+ just mark it as non-inheritable if necessary. */
+ int const fdTarget = paOrders[i].fdTarget;
+ unsigned j;
+ for (j = i + 1; j < cOrders; j++)
+ if (paOrders[j].fdTarget == fdTarget)
+ break;
+# ifdef _MSC_VER
+ if (j >= cOrders && !mscIsInheritable(fdTarget))
+ rcExit = 0;
+# else
+ if (j >= cOrders)
+ {
+ paOrders[j].fSaved = fcntl(fdTarget, F_GETFD, 0);
+ if (paOrders[j].fSaved != -1)
{
- fOpen = O_RDWR;
- psz++;
+ if (paOrders[j].fSaved & FD_CLOEXEC)
+ rcExit = 0;
+ else if ( fcntl(fdTarget, F_SETFD, FD_CLOEXEC) != -1
+ || errno == EBADF)
+ rcExit = 0;
+ else
+ fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_SETFD,FD_CLOEXEC) failed: %s",
+ g_progname, fdTarget, strerror(errno));
}
+ else if (errno == EBADF)
+ rcExit = 0;
else
- fOpen = O_RDONLY;
- break;
+ fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_GETFD,0) failed: %s", g_progname, fdTarget, strerror(errno));
+ }
+# endif
+ else
+ rcExit = kRedirectSaveHandle(&paOrders[i], cOrders, paOrders, ppWorkingStdErr);
+ break;
+ }
- case 'w':
- psz++;
- if (*psz == '+')
- {
- psz++;
- fOpen = O_RDWR | O_CREAT | O_TRUNC;
- }
+ case kRedirectOrder_Dup:
+ case kRedirectOrder_Open:
+ rcExit = kRedirectSaveHandle(&paOrders[i], cOrders, paOrders, ppWorkingStdErr);
+ if (rcExit == 0)
+ {
+ if (dup2(paOrders[i].fdSource, paOrders[i].fdTarget) != -1)
+ rcExit = 0;
else
- fOpen = O_WRONLY | O_CREAT | O_TRUNC;
- break;
-
- case 'a':
- psz++;
- if (*psz == '+')
{
- psz++;
- fOpen = O_RDWR | O_CREAT | O_APPEND;
+ if (paOrders[i].enmOrder == kRedirectOrder_Open)
+ fprintf(*ppWorkingStdErr, "%s: dup2(%d [%s],%d) failed: %s", g_progname, paOrders[i].fdSource,
+ paOrders[i].pszFilename, paOrders[i].fdTarget, strerror(errno));
+ else
+ fprintf(*ppWorkingStdErr, "%s: dup2(%d,%d) failed: %s",
+ g_progname, paOrders[i].fdSource, paOrders[i].fdTarget, strerror(errno));
+ rcExit = 10;
}
- else
- fOpen = O_WRONLY | O_CREAT | O_APPEND;
- break;
+ }
+ break;
- case 'i': /* make sure stdin is read-only. */
- fOpen = O_RDONLY;
- break;
+ default:
+ fprintf(*ppWorkingStdErr, "%s: error! invalid enmOrder=%d\n", g_progname, paOrders[i].enmOrder);
+ rcExit = 99;
+ break;
+ }
- case '+':
- fprintf(pStdErr, "%s: syntax error: Unexpected '+' in '%s'\n", name(argv[0]), argv[i]);
- return 1;
+ if (rcExit != 0)
+ {
+ kRedirectRestoreFdOrders(i, paOrders, ppWorkingStdErr);
+ return rcExit;
+ }
+ }
- default:
- fOpen = O_RDWR | O_CREAT | O_TRUNC;
- break;
- }
+ return 0;
+}
- /* binary / text modifiers */
- switch (*psz)
- {
- case 'b':
-#ifdef O_BINARY
- fOpen |= O_BINARY;
+#endif /* !USE_POSIX_SPAWN */
+
+
+/**
+ * Does the child spawning .
+ *
+ * @returns Exit code.
+ * @param pszExecutable The child process executable.
+ * @param cArgs Number of arguments.
+ * @param papszArgs The child argument vector.
+ * @param fWatcomBrainDamage Whether MSC need to do quoting according to
+ * weird Watcom WCC rules.
+ * @param papszEnv The child environment vector.
+ * @param pszCwd The current working directory of the child.
+ * @param pszSavedCwd The saved current working directory. This is
+ * NULL if the CWD doesn't need changing.
+ * @param cOrders Number of file operation orders.
+ * @param paOrders The file operation orders.
+ * @param pFileActions The posix_spawn file actions.
+ * @param cVerbosity The verbosity level.
+ * @param pPidSpawned Where to return the PID of the spawned child
+ * when we're inside KMK and we're return without
+ * waiting.
+ * @param pfIsChildExitCode Where to indicate whether the return exit code
+ * is from the child or from our setup efforts.
+ */
+static int kRedirectDoSpawn(const char *pszExecutable, int cArgs, char **papszArgs, int fWatcomBrainDamage, char **papszEnv,
+ const char *pszCwd, const char *pszSavedCwd, unsigned cOrders, REDIRECTORDERS *paOrders,
+#ifdef USE_POSIX_SPAWN
+ posix_spawn_file_actions_t *pFileActions,
#endif
- psz++;
- break;
+ unsigned cVerbosity,
+#ifdef KMK
+ pid_t *pPidSpawned,
+#endif
+ KBOOL *pfIsChildExitCode)
+{
+ int rcExit;
+ int i;
+#ifdef _MSC_VER
+ char **papszArgsOriginal = papszArgs;
+#endif
+ *pfIsChildExitCode = K_FALSE;
- case 't':
-#ifdef O_TEXT
- fOpen |= O_TEXT;
+#ifdef _MSC_VER
+ /*
+ * Do MSC parameter quoting.
+ */
+ papszArgs = malloc((cArgs + 1) * sizeof(papszArgs[0]));
+ if (papszArgs)
+ memcpy(papszArgs, papszArgsOriginal, (cArgs + 1) * sizeof(papszArgs[0]));
+ else
+ return errx(9, "out of memory!");
+
+ rcExit = quote_argv(cArgs, papszArgs, fWatcomBrainDamage, 0 /*fFreeOrLeak*/);
+ if (rcExit == 0)
#endif
- psz++;
- break;
+ {
+ /*
+ * Display what we're about to execute if we're in verbose mode.
+ */
+ if (cVerbosity > 0)
+ {
+ for (i = 0; i < cArgs; i++)
+ warnx("debug: argv[%i]=%s<eos>", i, papszArgs[i]);
+ for (i = 0; i < (int)cOrders; i++)
+ switch (paOrders[i].enmOrder)
+ {
+ case kRedirectOrder_Close:
+ warnx("debug: close %d\n", paOrders[i].fdTarget);
+ break;
+ case kRedirectOrder_Dup:
+ warnx("debug: dup %d to %d\n", paOrders[i].fdSource, paOrders[i].fdTarget);
+ break;
+ case kRedirectOrder_Open:
+ warnx("debug: open '%s' (%#x) as [%d ->] %d\n",
+ paOrders[i].pszFilename, paOrders[i].fOpen, paOrders[i].fdSource, paOrders[i].fdTarget);
+ break;
+ default:
+ warnx("error! invalid enmOrder=%d", paOrders[i].enmOrder);
+ assert(0);
+ break;
+ }
+ if (pszSavedCwd)
+ warnx("debug: chdir %s\n", pszCwd);
+ }
- default:
-#ifdef O_BINARY
- fOpen |= O_BINARY;
+ /*
+ * Change working directory if so requested.
+ */
+ if (pszSavedCwd)
+ {
+ if (chdir(pszCwd) < 0)
+ rcExit = errx(10, "Failed to change directory to '%s'", pszCwd);
+ }
+ if (rcExit == 0)
+ {
+#ifndef USE_POSIX_SPAWN
+ /*
+ * Execute the file orders.
+ */
+ FILE *pWorkingStdErr = NULL;
+ rcExit = kRedirectExecFdOrders(cOrders, paOrders, &pWorkingStdErr);
+ if (rcExit == 0)
#endif
- break;
+ {
+#ifdef KMK
+ /*
+ * We're spawning from within kmk.
+ */
+#if defined(KBUILD_OS_WINDOWS)
+ /* Windows is slightly complicated due to handles and sub_proc.c. */
+ HANDLE hProcess = (HANDLE)_spawnvpe(_P_NOWAIT, pszExecutable, papszArgs, papszEnv);
+ kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
+ if ((intptr_t)hProcess != -1)
+ {
+ if (process_kmk_register_redirect(hProcess, pPidSpawned) == 0)
+ {
+ if (cVerbosity > 0)
+ warnx("debug: spawned %d", *pPidSpawned);
+ }
+ else
+ {
+ DWORD dwTmp;
+ warn("sub_proc is out of slots, waiting for child...");
+ dwTmp = WaitForSingleObject(hProcess, INFINITE);
+ if (dwTmp != WAIT_OBJECT_0)
+ warn("WaitForSingleObject failed: %#x\n", dwTmp);
+
+ if (GetExitCodeProcess(hProcess, &dwTmp))
+ rcExit = (int)dwTmp;
+ else
+ {
+ warn("GetExitCodeProcess failed: %u\n", GetLastError());
+ TerminateProcess(hProcess, 127);
+ rcExit = 127;
+ }
+
+ CloseHandle(hProcess);
+ *pPidSpawned = 0;
+ *pfIsChildExitCode = K_TRUE;
+ }
+ }
+ else
+ rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
+
+# elif defined(KBUILD_OS_OS2)
+ *pPidSpawned = _spawnve(_P_NOWAIT, pszExecutable, papszArgs, papszEnv);
+ kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
+ if (*pPidSpawned != -1)
+ {
+ if (cVerbosity > 0)
+ warnx("debug: spawned %d", *pPidSpawned);
+ }
+ else
+ {
+ rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
+ *pPidSpawned = 0;
+ }
+#else
+ rcExit = posix_spawnp(pPidSpawned, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars);
+ if (rcExit == 0)
+ {
+ if (cVerbosity > 0)
+ warnx("debug: spawned %d", *pPidSpawned);
+ }
+ else
+ {
+ rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit));
+ *pPidSpawned = 0;
+ }
+#endif
+
+#else /* !KMK */
+ /*
+ * Spawning from inside the kmk_redirect executable.
+ */
+# if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
+ errno = 0;
+ rcExit = (int)_spawnvpe(_P_WAIT, pszExecutable, papszArgs, papszEnv);
+ kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
+ if (rcExit != -1 || errno == 0)
+ {
+ *pfIsChildExitCode = K_TRUE;
+ if (cVerbosity > 0)
+ warnx("debug: exit code: %d", rcExit);
+ }
+ else
+ rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
+
+# else
+ pid_t pidChild = 0;
+ rcExit = posix_spawnp(&pidChild, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars);
+ if (rcExit == 0)
+ {
+ *pfIsChildExitCode = K_TRUE;
+ if (cVerbosity > 0)
+ warnx("debug: spawned %d", pidChild);
+
+ /* Wait for the child. */
+ for (;;)
+ {
+ pid_t pid = waitpid(pidChild, &rcExit, 0 /*block*/);
+ if (pid == pidChild)
+ {
+ if (cVerbosity > 0)
+ warnx("debug: %d exit code: %d", pidChild, rcExit);
+ break;
+ }
+ if ( errno != EINTR
+# ifdef ERESTART
+ && errno != ERESTART
+# endif
+ )
+ {
+ rcExit = err(11, "waitpid failed");
+ kill(pidChild, SIGKILL);
+ break;
+ }
+ }
+ }
+ else
+ rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit));
+# endif
+#endif /* !KMK */
}
+ }
+
+ /*
+ * Restore the current directory.
+ */
+ if (pszSavedCwd)
+ {
+ if (chdir(pszSavedCwd) < 0)
+ warn("Failed to restore directory to '%s'", pszSavedCwd);
+ }
+ }
+#ifdef _MSC_VER
+ else
+ rcExit = errx(9, "quite_argv failed: %u", rcExit);
+
+ /* Restore the original argv strings, freeing the quote_argv replacements. */
+ i = cArgs;
+ while (i-- > 0)
+ if (papszArgs[i] != papszArgsOriginal[i])
+ free(papszArgs[i]);
+ free(papszArgs);
+#endif
+ return rcExit;
+}
+
+
+/**
+ * The function that does almost everything here... ugly.
+ */
+#ifdef KMK
+int kmk_builtin_redirect(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned)
+#else
+int main(int argc, char **argv, char **envp)
+#endif
+{
+ int rcExit = 0;
+ KBOOL fChildExitCode = K_FALSE;
+#ifdef USE_POSIX_SPAWN
+ posix_spawn_file_actions_t FileActions;
+#endif
+ unsigned cOrders = 0;
+ REDIRECTORDERS aOrders[32];
+
+ int iArg;
+ const char *pszExecutable = NULL;
+ char **papszEnv = NULL;
+ unsigned cAllocatedEnvVars;
+ unsigned iEnvVar;
+ unsigned cEnvVars;
+ int fWatcomBrainDamage = 0;
+ int cVerbosity = 0;
+ char *pszSavedCwd = NULL;
+ size_t const cbCwdBuf = GET_PATH_MAX;
+ PATH_VAR(szCwd);
+#ifdef KBUILD_OS_OS2
+ ULONG ulLibPath;
+ char *apszSavedLibPaths[LIBPATHSTRICT + 1] = { NULL, NULL, NULL, NULL };
+#endif
+
+
+ g_progname = argv[0];
+
+ if (argc <= 1)
+ return usage(stderr, argv[0]);
+
+ /*
+ * Create default program environment.
+ */
+#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
+ if (getcwd_fs(szCwd, cbCwdBuf) != NULL)
+#else
+ if (getcwd(szCwd, cbCwdBuf) != NULL)
+#endif
+ { /* likely */ }
+ else
+ return err(9, "getcwd failed");
+
+#if defined(KMK)
+ /* We get it from kmk and just count it: */
+ papszEnv = pChild->environment;
+ if (!papszEnv)
+ pChild->environment = papszEnv = target_environment(pChild->file);
+ cEnvVars = 0;
+ while (papszEnv[cEnvVars] != NULL)
+ cEnvVars++;
+ cAllocatedEnvVars = cEnvVars;
+#else
+ /* We make a copy and we manage ourselves: */
+ cEnvVars = 0;
+ while (envp[cEnvVars] != NULL)
+ cEnvVars++;
+
+ cAllocatedEnvVars = cEnvVars + 4;
+ papszEnv = malloc((cAllocatedEnvVars + 1) * sizeof(papszEnv));
+ if (!papszEnv)
+ return errx(9, "out of memory!");
+
+ iEnvVar = cEnvVars;
+ papszEnv[iEnvVar] = NULL;
+ while (iEnvVar-- > 0)
+ {
+ papszEnv[iEnvVar] = strdup(envp[iEnvVar]);
+ if (!papszEnv[iEnvVar])
+ {
+ while (iEnvVar-- > 0)
+ free(papszEnv[iEnvVar]);
+ free(papszEnv);
+ return errx(9, "out of memory!");
+ }
+ }
+#endif
- /* convert to file descriptor number */
- switch (*psz)
+#ifdef USE_POSIX_SPAWN
+ /*
+ * Init posix attributes.
+ */
+ rcExit = posix_spawn_file_actions_init(&FileActions);
+ if (rcExit != 0)
+ rcExit = errx(9, "posix_spawn_file_actions_init failed: %s", strerror(rcExit));
+#endif
+
+ /*
+ * Parse arguments.
+ */
+ for (iArg = 1; rcExit == 0 && iArg < argc; iArg++)
+ {
+ char *pszArg = argv[iArg];
+ if (*pszArg == '-')
+ {
+ int fd;
+ char chOpt;
+ const char *pszValue;
+
+ chOpt = *++pszArg;
+ pszArg++;
+ if (chOpt == '-')
{
- case 'i':
- fd = 0;
- psz++;
+ /* '--' indicates where the bits to execute start. */
+ if (*pszArg == '\0')
+ {
+ iArg++;
break;
+ }
+
+ if ( strcmp(pszArg, "wcc-brain-damage") == 0
+ || strcmp(pszArg, "watcom-brain-damage") == 0)
+ {
+ fWatcomBrainDamage = 1;
+ continue;
+ }
- case 'o':
- fd = 1;
- psz++;
+ /* convert to short. */
+ if (strcmp(pszArg, "help") == 0)
+ chOpt = 'h';
+ else if (strcmp(pszArg, "version") == 0)
+ chOpt = 'V';
+ else if ( strcmp(pszArg, "set") == 0
+ || strcmp(pszArg, "env") == 0)
+ chOpt = 'E';
+ else if (strcmp(pszArg, "unset") == 0)
+ chOpt = 'U';
+ else if ( strcmp(pszArg, "zap-env") == 0
+ || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
+ chOpt = 'Z';
+ else if (strcmp(pszArg, "chdir") == 0)
+ chOpt = 'C';
+ else if (strcmp(pszArg, "close") == 0)
+ chOpt = 'c';
+ else if (strcmp(pszArg, "verbose") == 0)
+ chOpt = 'v';
+ else
+ {
+ errx(2, "Unknown option: '%s'", pszArg - 2);
+ rcExit = usage(stderr, argv[0]);
break;
+ }
+ pszArg = "";
+ }
+
+ /*
+ * Deal with the obligatory help and version switches first to get them out of the way.
+ */
+ if (chOpt == 'h')
+ {
+ usage(stdout, argv[0]);
+ rcExit = -1;
+ break;
+ }
+ if (chOpt == 'V')
+ {
+ kbuild_version(argv[0]);
+ rcExit = -1;
+ break;
+ }
- case 'e':
- fd = 2;
- psz++;
+ /*
+ * Get option value first, if the option takes one.
+ */
+ if ( chOpt == 'E'
+ || chOpt == 'U'
+ || chOpt == 'C'
+ || chOpt == 'c'
+ || chOpt == 'd'
+ || chOpt == 'e')
+ {
+ if (*pszArg != '\0')
+ pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
+ else if (++iArg < argc)
+ pszValue = argv[iArg];
+ else
+ {
+ errx(2, "syntax error: Option -%c requires a value!", chOpt);
+ rcExit = usage(stderr, argv[0]);
break;
+ }
+ }
+ else
+ pszValue = NULL;
- case '0':
- if (!psz[1])
+ /*
+ * Environment switch?
+ */
+ if (chOpt == 'E')
+ {
+ const char *pchEqual = strchr(pszValue, '=');
+#ifdef KBUILD_OS_OS2
+ if ( strncmp(pszValue, TUPLE("BEGINLIBPATH=")) == 0
+ || strncmp(pszValue, TUPLE("ENDLIBPATH=")) == 0
+ || strncmp(pszValue, TUPLE("LIBPATHSTRICT=")) == 0)
+ {
+ ULONG ulVar = *pszValue == 'B' ? BEGIN_LIBPATH
+ : *pszValue == 'E' ? END_LIBPATH
+ : LIBPATHSTRICT;
+ APIRET rc;
+ if (apszSavedLibPaths[ulVar] == NULL)
{
- fd = 0;
- psz++;
- break;
+ /* The max length is supposed to be 1024 bytes. */
+ apszSavedLibPaths[ulVar] = calloc(1024 * 2);
+ if (apszSavedLibPaths[ulVar])
+ {
+ rc = DosQueryExtLIBPATH(apszSavedLibPaths[ulVar], ulVar);
+ if (rc)
+ {
+ rcExit = errx(9, "DosQueryExtLIBPATH(,%u) failed: %lu", ulVar, rc);
+ free(apszSavedLibPaths[ulVar]);
+ apszSavedLibPaths[ulVar] = NULL;
+ }
+ }
+ else
+ rcExit = errx(9, "out of memory!");
}
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- fd = (int)strtol(psz, &psz, 0);
- if (!fd)
+ if (rcExit == 0)
{
- fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]);
- return 1;
+ rc = DosSetExtLIBPATH(pchEqual + 1, ulVar);
+ if (rc)
+ rcExit = errx(9, "error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu",
+ pchEqual, pchEqual - pszValue, pchEqual + 1, ulVar, rc);
+ }
+ continue;
+ }
+#endif /* KBUILD_OS_OS2 */
+ /* We differ from kSubmit here and use putenv sematics. */
+ if (pchEqual)
+ {
+ if (pchEqual[1] != '\0')
+ {
+ rcExit = kBuiltinOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
+#ifdef KMK
+ pChild->environment = papszEnv;
+#endif
}
- if (fd < 0)
+ else
{
- fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]);
- return 1;
+ char *pszCopy = strdup(pszValue);
+ if (pszCopy)
+ {
+ pszCopy[pchEqual - pszValue] = '\0';
+ rcExit = kBuiltinOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszCopy);
+ free(pszCopy);
+ }
+ else
+ rcExit = errx(1, "out of memory!");
}
- break;
+ continue;
+ }
+ /* Simple unset. */
+ chOpt = 'U';
+ }
- /*
- * Invalid argument.
- */
- default:
- fprintf(pStdErr, "%s: error: failed to convert '%s' ('%s') to a file descriptor\n", name(argv[0]), psz, argv[i]);
- return 1;
+ /*
+ * Unset environment variable.
+ */
+ if (chOpt == 'U')
+ {
+#ifdef KBUILD_OS_OS2
+ if ( strcmp(pszValue, "BEGINLIBPATH") == 0
+ || strcmp(pszValue, "ENDLIBPATH") == 0
+ || strcmp(pszValue, "LIBPATHSTRICT") == 0)
+ rcExit = errx(2, "error: '%s' cannot be unset, only set to an empty value using -E/--set.", pszValue);
+ else
+#endif
+ rcExit = kBuiltinOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue);
+ continue;
}
/*
- * Check for the filename.
+ * Zap environment switch?
*/
- if (*psz)
+ if ( chOpt == 'Z'
+ || chOpt == 'i' /* GNU env compatibility. */ )
{
- if (*psz != ':' && *psz != '=')
- {
- fprintf(pStdErr, "%s: syntax error: characters following the file descriptor: '%s' ('%s')\n", name(argv[0]), psz, argv[i]);
- return 1;
- }
- psz++;
+ for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
+ free(papszEnv[iEnvVar]);
+ papszEnv[0] = NULL;
+ cEnvVars = 0;
+ continue;
}
- else
+
+ /*
+ * Change directory switch?
+ */
+ if (chOpt == 'C')
{
- i++;
- if (i >= argc)
- {
- fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0]));
- return 1;
- }
- psz = argv[i];
+ if (pszSavedCwd == NULL)
+ pszSavedCwd = strdup(szCwd);
+ if (pszSavedCwd)
+ rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue);
+ else
+ rcExit = err(9, "out of memory!");
+ continue;
}
+
/*
- * Setup the redirection.
+ * Verbose operation switch?
*/
- if (fd == fileno(pStdErr))
+ if (chOpt == 'v')
+ {
+ cVerbosity++;
+ continue;
+ }
+
+ /*
+ * Executable image other than the first argument following '--'.
+ */
+ if (chOpt == 'e')
+ {
+ pszExecutable = pszValue;
+ continue;
+ }
+
+ /*
+ * Okay, it is some file descriptor opearation. Make sure we've got room for it.
+ */
+ if (cOrders + 1 < K_ELEMENTS(aOrders))
+ {
+ aOrders[cOrders].fdTarget = -1;
+ aOrders[cOrders].fdSource = -1;
+ aOrders[cOrders].fOpen = 0;
+ aOrders[cOrders].fRemoveOnFailure = 0;
+ aOrders[cOrders].pszFilename = NULL;
+#ifndef USE_POSIX_SPAWN
+ aOrders[cOrders].fdSaved = -1;
+#endif
+ }
+ else
+ {
+ rcExit = errx(2, "error: too many file actions (max: %d)", K_ELEMENTS(aOrders));
+ break;
+ }
+
+ if (chOpt == 'c')
{
/*
- * Move stderr to a new location, making it close on exec.
- * If pStdOut has already teamed up with pStdErr, update it too.
+ * Close the specified file descriptor (no stderr/out/in aliases).
*/
- FILE *pNew;
- fdOpened = dup(fileno(pStdErr));
- if (fdOpened == -1)
+ char *pszTmp;
+ fd = (int)strtol(pszValue, &pszTmp, 0);
+ if (pszTmp == pszValue || *pszTmp != '\0')
+ rcExit = errx(2, "error: failed to convert '%s' to a number", pszValue);
+ else if (fd < 0)
+ rcExit = errx(2, "error: negative fd %d (%s)", fd, pszValue);
+ else
{
- fprintf(pStdErr, "%s: error: failed to dup stderr (%d): %s\n", name(argv[0]), fileno(pStdErr), strerror(errno));
- return 1;
+ aOrders[cOrders].enmOrder = kRedirectOrder_Close;
+ aOrders[cOrders].fdTarget = fd;
+ cOrders++;
+#ifdef USE_POSIX_SPAWN
+ rcExit = posix_spawn_file_actions_addclose(&FileActions, fd);
+ if (rcExit != 0)
+ rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
+#endif
}
-#ifdef _MSC_VER
- /** @todo figure out how to make the handle close-on-exec. We'll simply close it for now.
- * SetHandleInformation + set FNOINHERIT in CRT.
+ }
+ else if (chOpt == 'd')
+ {
+ /*
+ * Duplicate file handle. Value is fdTarget=fdSource
*/
-#else
- if (fcntl(fdOpened, F_SETFD, FD_CLOEXEC) == -1)
+ char *pszEqual;
+ fd = (int)strtol(pszValue, &pszEqual, 0);
+ if (pszEqual == pszValue)
+ rcExit = errx(2, "error: failed to convert target descriptor of '-d %s' to a number", pszValue);
+ else if (fd < 0)
+ rcExit = errx(2, "error: negative target descriptor %d ('-d %s')", fd, pszValue);
+ else if (*pszEqual != '=')
+ rcExit = errx(2, "syntax error: expected '=' to follow target descriptor: '-d %s'", pszValue);
+ else
{
- fprintf(pStdErr, "%s: error: failed to make stderr (%d) close-on-exec: %s\n", name(argv[0]), fdOpened, strerror(errno));
- return 1;
- }
+ char *pszEnd;
+ int fdSource = (int)strtol(++pszEqual, &pszEnd, 0);
+ if (pszEnd == pszEqual || *pszEnd != '\0')
+ rcExit = errx(2, "error: failed to convert source descriptor of '-d %s' to a number", pszValue);
+ else if (fdSource < 0)
+ rcExit = errx(2, "error: negative source descriptor %d ('-d %s')", fdSource, pszValue);
+ else
+ {
+ aOrders[cOrders].enmOrder = kRedirectOrder_Dup;
+ aOrders[cOrders].fdTarget = fd;
+ aOrders[cOrders].fdSource = fdSource;
+ cOrders++;
+#ifdef USE_POSIX_SPAWN
+ rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdSource, fd);
+ if (rcExit != 0)
+ rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
#endif
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Open file as a given file descriptor.
+ */
+ int fdOpened;
+ int fOpen;
- pNew = fdopen(fdOpened, "w");
- if (!pNew)
+ /* mode */
+ switch (chOpt)
{
- fprintf(pStdErr, "%s: error: failed to fdopen the new stderr (%d): %s\n", name(argv[0]), fdOpened, strerror(errno));
- return 1;
+ case 'r':
+ chOpt = *pszArg++;
+ if (chOpt == '+')
+ {
+ fOpen = O_RDWR;
+ chOpt = *pszArg++;
+ }
+ else
+ fOpen = O_RDONLY;
+ break;
+
+ case 'w':
+ chOpt = *pszArg++;
+ if (chOpt == '+')
+ {
+ fOpen = O_RDWR | O_CREAT | O_TRUNC;
+ chOpt = *pszArg++;
+ }
+ else
+ fOpen = O_WRONLY | O_CREAT | O_TRUNC;
+ aOrders[cOrders].fRemoveOnFailure = 1;
+ break;
+
+ case 'a':
+ chOpt = *pszArg++;
+ if (chOpt == '+')
+ {
+ fOpen = O_RDWR | O_CREAT | O_APPEND;
+ chOpt = *pszArg++;
+ }
+ else
+ fOpen = O_WRONLY | O_CREAT | O_APPEND;
+ break;
+
+ case 'i': /* make sure stdin is read-only. */
+ fOpen = O_RDONLY;
+ break;
+
+ case '+':
+ rcExit = errx(2, "syntax error: Unexpected '+' in '%s'", argv[iArg]);
+ continue;
+
+ default:
+ fOpen = O_RDWR | O_CREAT | O_TRUNC;
+ aOrders[cOrders].fRemoveOnFailure = 1;
+ break;
}
- if (pStdOut == pStdErr)
- pStdOut = pNew;
- pStdErr = pNew;
- }
- else if (fd == 1 && pStdOut != pStdErr)
- pStdOut = pStdErr;
- /*
- * Close and open the new file descriptor.
- */
- safeCloseFd(fd);
-#if defined(_MSC_VER)
- if (!strcmp(psz, "/dev/null"))
- psz = (char *)"nul";
+ /* binary / text modifiers */
+ switch (chOpt)
+ {
+ case 'b':
+ chOpt = *pszArg++;
+ default:
+#ifdef O_BINARY
+ fOpen |= O_BINARY;
+#elif defined(_O_BINARY)
+ fOpen |= _O_BINARY;
#endif
- fdOpened = open(psz, fOpen, 0666);
- if (fdOpened == -1)
- {
- fprintf(pStdErr, "%s: error: failed to open '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno));
- return 1;
- }
- if (fdOpened != fd)
- {
- /* move it (dup2 returns 0 on MSC). */
- if (dup2(fdOpened, fd) == -1)
+ break;
+
+ case 't':
+#ifdef O_TEXT
+ fOpen |= O_TEXT;
+#elif defined(_O_TEXT)
+ fOpen |= _O_TEXT;
+#endif
+ chOpt = *pszArg++;
+ break;
+
+ }
+
+ /* convert to file descriptor number */
+ switch (chOpt)
{
- fprintf(pStdErr, "%s: error: failed to dup '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno));
- return 1;
+ case 'i':
+ fd = 0;
+ break;
+
+ case 'o':
+ fd = 1;
+ break;
+
+ case 'e':
+ fd = 2;
+ break;
+
+ case '0':
+ if (*pszArg == '\0')
+ {
+ fd = 0;
+ break;
+ }
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ pszValue = pszArg - 1;
+ fd = (int)strtol(pszValue, &pszArg, 0);
+ if (pszArg == pszValue)
+ rcExit = errx(2, "error: failed to convert '%s' to a number", argv[iArg]);
+ else if (fd < 0)
+ rcExit = errx(2, "error: negative fd %d (%s)", fd, argv[iArg]);
+ else
+ break;
+ continue;
+
+ /*
+ * Invalid argument.
+ */
+ default:
+ rcExit = errx(2, "error: failed to convert '%s' ('%s') to a file descriptor", pszArg, argv[iArg]);
+ continue;
+ }
+
+ /*
+ * Check for the filename.
+ */
+ if (*pszArg != '\0')
+ {
+ if (*pszArg != ':' && *pszArg != '=')
+ {
+ rcExit = errx(2, "syntax error: characters following the file descriptor: '%s' ('%s')",
+ pszArg, argv[iArg]);
+ break;
+ }
+ pszArg++;
+ }
+ else if (++iArg < argc)
+ pszArg = argv[iArg];
+ else
+ {
+ rcExit = errx(2, "syntax error: missing filename argument.");
+ break;
+ }
+
+ /*
+ * Open the file. We could've used posix_spawn_file_actions_addopen here,
+ * but that means complicated error reporting. So, since we need to do
+ * this for windows anyway, just do it the same way everywhere.
+ */
+ fdOpened = kRedirectOpenWithoutConflict(pszArg, fOpen, 0666, cOrders, aOrders,
+ aOrders[cOrders].fRemoveOnFailure, fd);
+ if (fdOpened >= 0)
+ {
+ aOrders[cOrders].enmOrder = kRedirectOrder_Open;
+ aOrders[cOrders].fdTarget = fd;
+ aOrders[cOrders].fdSource = fdOpened;
+ aOrders[cOrders].fOpen = fOpen;
+ aOrders[cOrders].pszFilename = pszArg;
+ cOrders++;
+
+#ifdef USE_POSIX_SPAWN
+ if (fdOpened != fdSource)
+ {
+ rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdOpened, fd);
+ if (rcExit != 0)
+ rcExit = err(9, "posix_spawn_file_actions_adddup2(,%d [%s], %d) failed: %s",
+ fdOpened, fd, pszArg, strerror(rcExit));
+ }
+#endif
}
- close(fdOpened);
+ else
+ rcExit = 9;
}
}
else
{
- fprintf(pStdErr, "%s: syntax error: Invalid argument '%s'.\n", name(argv[0]), argv[i]);
- return usage(pStdErr, name(argv[0]));
+ errx(2, "syntax error: Invalid argument '%s'.", argv[iArg]);
+ rcExit = usage(stderr, argv[0]);
}
}
+ if (!pszExecutable)
+ pszExecutable = argv[iArg];
/*
* Make sure there's something to execute.
*/
- if (i >= argc)
+ if (rcExit == 0 && iArg < argc)
{
- fprintf(pStdErr, "%s: syntax error: nothing to execute!\n", name(argv[0]));
- return usage(pStdErr, name(argv[0]));
+ /*
+ * Do the spawning in a separate function (main is far to large as it is by now).
+ */
+ rcExit = kRedirectDoSpawn(pszExecutable, argc - iArg, &argv[iArg], fWatcomBrainDamage, papszEnv, szCwd, pszSavedCwd,
+#ifdef USE_POSIX_SPAWN
+ cOrders, aOrders, &FileActions, cVerbosity,
+#else
+ cOrders, aOrders, cVerbosity,
+#endif
+#ifdef KMK
+ pPidSpawned,
+#endif
+ &fChildExitCode);
}
-
-#if defined(_MSC_VER)
- if (fileno(pStdErr) != 2) /* no close-on-exec flag on windows */
+ else if (rcExit == 0)
{
- fclose(pStdErr);
- pStdErr = NULL;
+ errx(2, "syntax error: nothing to execute!");
+ rcExit = usage(stderr, argv[0]);
}
+ /* Help and version sets rcExit to -1. Change it to zero. */
+ else if (rcExit == -1)
+ rcExit = 0;
- /* MSC is a PITA since it refuses to quote the arguments... */
- quote_argv(argc - i, &argv[i], fWatcomBrainDamage, 0 /*fFreeOrLeak*/);
- if (g_cVerbosity > 0)
- for (j = i; j < argc; j++)
- fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", j - i, argv[j]);
- rc = _spawnvp(_P_WAIT, argv[i], &argv[i]);
- if (rc == -1 && pStdErr)
- {
- fprintf(pStdErr, "%s: error: _spawnvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno));
- rc = 1;
- }
- return rc;
-#else
- if (g_cVerbosity > 0)
- for (j = i; j < argc; j++)
- fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", j - i, argv[j]);
- execvp(argv[i], &argv[i]);
- fprintf(pStdErr, "%s: error: _execvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno));
- return 1;
+ /*
+ * Cleanup.
+ */
+ if (pszSavedCwd)
+ free(pszSavedCwd);
+ kRedirectCleanupFdOrders(cOrders, aOrders, rcExit != 0 && !fChildExitCode);
+#ifdef USE_POSIX_SPAWN
+ posix_spawn_file_actions_destroy(&FileActions);
#endif
+#ifndef KMK
+ iEnvVar = cEnvVars;
+ while (iEnvVar-- > 0)
+ free(papszEnv[iEnvVar]);
+ free(papszEnv);
+#endif
+#ifdef KBUILD_OS_OS2
+ for (ulLibPath = 0; ulLibPath < K_ELEMENTS(apszSavedLibPaths); ulLibPath++)
+ if (apszSavedLibPaths[ulLibPath] != NULL)
+ {
+ APIRET rc = DosSetExtLIBPATH(apszSavedLibPaths[ulLibPath], ulLibPath);
+ if (rc != 0)
+ warnx("DosSetExtLIBPATH('%s',%u) failed with %u when restoring the original values!",
+ apszSavedLibPaths[ulLibPath], ulLibPath, rc);
+ free(apszSavedLibPaths[ulLibPath])
+ }
+#endif
+
+ return rcExit;
}
diff --git a/src/kmk/kmkbuiltin/rm.c b/src/kmk/kmkbuiltin/rm.c
index 17b7ca2..986f991 100644
--- a/src/kmk/kmkbuiltin/rm.c
+++ b/src/kmk/kmkbuiltin/rm.c
@@ -388,14 +388,28 @@ rm_tree(char **argv)
switch (p->fts_info) {
case FTS_DP:
case FTS_DNR:
+#ifdef KBUILD_OS_WINDOWS
+ if (p->fts_parent->fts_dirfd != NT_FTS_INVALID_HANDLE_VALUE) {
+ rval = birdUnlinkForcedEx(p->fts_parent->fts_dirfd, p->fts_name);
+ } else {
+ rval = birdUnlinkForced(p->fts_accpath);
+ }
+#else
rval = rmdir(p->fts_accpath);
+#endif
if (rval == 0 || (fflag && errno == ENOENT)) {
if (rval == 0 && vflag)
(void)printf("%s\n",
p->fts_path);
+#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
+ if (rval == 0) {
+ extern int dir_cache_deleted_directory(const char *pszDir);
+ dir_cache_deleted_directory(p->fts_accpath);
+ }
+#endif
continue;
}
- operation = "mkdir";
+ operation = "rmdir";
break;
#ifdef FTS_W
@@ -424,7 +438,11 @@ rm_tree(char **argv)
if (!rm_overwrite(p->fts_accpath, NULL))
continue;
#ifdef KBUILD_OS_WINDOWS
- rval = birdUnlinkForcedFast(p->fts_accpath);
+ if (p->fts_parent->fts_dirfd != NT_FTS_INVALID_HANDLE_VALUE) {
+ rval = birdUnlinkForcedFastEx(p->fts_parent->fts_dirfd, p->fts_name);
+ } else {
+ rval = birdUnlinkForcedFast(p->fts_accpath);
+ }
#else
rval = unlink(p->fts_accpath);
#endif
diff --git a/src/kmk/kmkbuiltin/rmdir.c b/src/kmk/kmkbuiltin/rmdir.c
index 34771dd..52821be 100644
--- a/src/kmk/kmkbuiltin/rmdir.c
+++ b/src/kmk/kmkbuiltin/rmdir.c
@@ -59,6 +59,9 @@ __FBSDID("$FreeBSD: src/bin/rmdir/rmdir.c,v 1.20 2005/01/26 06:51:28 ssouhlal Ex
#ifdef _MSC_VER
# include "mscfakes.h"
#endif
+#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
+extern int dir_cache_deleted_directory(const char *pszDir);
+#endif
static int rm_path(char *);
static int usage(FILE *);
@@ -134,8 +137,13 @@ kmk_builtin_rmdir(int argc, char *argv[], char **envp)
if (!ignore_fail_on_not_exist || errno != ENOENT)
continue;
/* (only ignored doesn't exist errors fall thru) */
- } else if (vflag) {
- printf("%s\n", *argv);
+ } else {
+#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
+ dir_cache_deleted_directory(*argv);
+#endif
+ if (vflag) {
+ printf("%s\n", *argv);
+ }
}
if (pflag)
errors |= rm_path(*argv);
@@ -177,13 +185,19 @@ rm_path(char *path)
#endif
if (rmdir(path) < 0) {
- if (ignore_fail_on_non_empty && (errno == ENOTEMPTY || errno == EPERM || errno == EACCES || errno == EINVAL || errno == EEXIST))
+ if ( ignore_fail_on_non_empty
+ && ( errno == ENOTEMPTY || errno == EPERM || errno == EACCES || errno == EINVAL || errno == EEXIST))
break;
if (!ignore_fail_on_not_exist || errno != ENOENT) {
warn("rmdir: %s", path);
return (1);
}
}
+#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
+ else {
+ dir_cache_deleted_directory(path);
+ }
+#endif
if (vflag)
printf("%s\n", path);
}
diff --git a/src/kmk/main.c b/src/kmk/main.c
index b648f42..7bb2437 100644
--- a/src/kmk/main.c
+++ b/src/kmk/main.c
@@ -89,6 +89,7 @@ void verify_file_data_base (void);
#ifdef CONFIG_WITH_PRINT_STATS_SWITCH
void print_variable_stats (void);
+void print_dir_stats (void);
void print_file_stats (void);
#endif
@@ -915,6 +916,7 @@ set_make_priority_and_affinity (void)
#ifdef WINDOWS32
+# ifndef KMK
/*
* HANDLE runtime exceptions by avoiding a requestor on the GUI. Capture
* exception and print it to stderr instead.
@@ -989,6 +991,7 @@ handle_runtime_exceptions( struct _EXCEPTION_POINTERS *exinfo )
return (255); /* not reached */
#endif
}
+# endif /* !KMK */
/*
* On WIN32 systems we don't have the luxury of a /bin directory that
@@ -1231,16 +1234,33 @@ get_online_cpu_count(void)
{
# ifdef WINDOWS32
/* Windows: Count the active CPUs. */
- int cpus, i;
- SYSTEM_INFO si;
- GetSystemInfo(&si);
- for (i = cpus = 0; i < sizeof(si.dwActiveProcessorMask) * 8; i++)
+ int cpus;
+
+ /* Process groups (W7+). */
+ typedef DWORD (WINAPI *PFNGETACTIVEPROCESSORCOUNT)(DWORD);
+ PFNGETACTIVEPROCESSORCOUNT pfnGetActiveProcessorCount;
+ pfnGetActiveProcessorCount = (PFNGETACTIVEPROCESSORCOUNT)GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
+ "GetActiveProcessorCount");
+ if (pfnGetActiveProcessorCount)
+ cpus = pfnGetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
+ /* Legacy (<= Vista). */
+ else
{
- if (si.dwActiveProcessorMask & 1)
- cpus++;
- si.dwActiveProcessorMask >>= 1;
+ int i;
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ for (i = cpus = 0; i < sizeof(si.dwActiveProcessorMask) * 8; i++)
+ {
+ if (si.dwActiveProcessorMask & 1)
+ cpus++;
+ si.dwActiveProcessorMask >>= 1;
+ }
}
- return cpus ? cpus : 1;
+ if (!cpus)
+ cpus = 1;
+ if (cpus > 64)
+ cpus = 64; /* (wait for multiple objects limit) */
+ return cpus;
# elif defined(__OS2__)
/* OS/2: Count the active CPUs. */
@@ -1376,7 +1396,9 @@ main (int argc, char **argv, char **envp)
char *windows32_path = NULL;
# ifndef ELECTRIC_HEAP /* Drop this because it prevents JIT debugging. */
+# ifndef KMK /* Don't want none of this crap. */
SetUnhandledExceptionFilter(handle_runtime_exceptions);
+# endif
# endif /* !ELECTRIC_HEAP */
/* start off assuming we have no shell */
@@ -3858,6 +3880,7 @@ print_stats ()
/* Make stuff: */
print_variable_stats ();
print_file_stats ();
+ print_dir_stats ();
# ifdef KMK
print_kbuild_define_stats ();
# endif
diff --git a/src/kmk/make.h b/src/kmk/make.h
index 1903299..982cd0c 100644
--- a/src/kmk/make.h
+++ b/src/kmk/make.h
@@ -983,6 +983,7 @@ extern void dir_cache_invalid_after_job (void);
extern void dir_cache_invalid_all (void);
extern void dir_cache_invalid_missing (void);
extern int dir_cache_volatile_dir (const char *dir);
+extern int dir_cache_deleted_directory(const char *pszDir);
# endif
#endif
diff --git a/src/kmk/w32/include/sub_proc.h b/src/kmk/w32/include/sub_proc.h
index 4876e51..0cba3f4 100644
--- a/src/kmk/w32/include/sub_proc.h
+++ b/src/kmk/w32/include/sub_proc.h
@@ -49,6 +49,7 @@ EXTERN_DECL(BOOL process_kill, (HANDLE proc, int signal));
EXTERN_DECL(int process_used_slots, (VOID_DECL));
#ifdef KMK
EXTERN_DECL(int process_kmk_register_submit, (HANDLE hEvent, intptr_t clue, pid_t *pPid));
+EXTERN_DECL(int process_kmk_register_redirect, (HANDLE hProcess, pid_t *pPid));
#endif
/* support routines */
diff --git a/src/kmk/w32/subproc/sub_proc.c b/src/kmk/w32/subproc/sub_proc.c
index 695589f..f2e9b49 100644
--- a/src/kmk/w32/subproc/sub_proc.c
+++ b/src/kmk/w32/subproc/sub_proc.c
@@ -198,6 +198,7 @@ process_register(HANDLE proc)
}
#ifdef KMK
+
/**
* Interface used by kmkbuiltin/kSubmit.c to register stuff going down in a
* worker process.
@@ -222,7 +223,30 @@ process_kmk_register_submit(HANDLE hEvent, intptr_t clue, pid_t *pPid)
}
return -1;
}
-#endif
+
+/**
+ * Interface used by kmkbuiltin/kRedirect.c to register a spawned process.
+ *
+ * @returns 0 on success, -1 if there are too many sub-processes already.
+ * @param hProcess The process handle.
+ * @param pPid Where to return the pid that job.c expects.
+ */
+int
+process_kmk_register_redirect(HANDLE hProcess, pid_t *pPid)
+{
+ if (proc_index < MAXIMUM_WAIT_OBJECTS) {
+ sub_process *pSubProc = (sub_process *)xcalloc(sizeof(*pSubProc));
+ pSubProc->enmType = kRegular;
+ pSubProc->pid = (intptr_t)hProcess;
+
+ proc_array[proc_index++] = pSubProc;
+ *pPid = (intptr_t)pSubProc;
+ return 0;
+ }
+ return -1;
+}
+
+#endif /* KMK */
/*
* Return the number of processes that we are still waiting for.
diff --git a/src/lib/Makefile.kmk b/src/lib/Makefile.kmk
index b5833c2..0cd8307 100644
--- a/src/lib/Makefile.kmk
+++ b/src/lib/Makefile.kmk
@@ -1,10 +1,10 @@
-# $Id: Makefile.kmk 2906 2016-09-09 22:15:57Z bird $
+# $Id: Makefile.kmk 2994 2016-11-01 22:41:41Z bird $
## @file
# Sub-makefile for various libraries and stuff.
#
#
-# Copyright (c) 2006-2013 knut st. osmundsen <bird-kBuild-spamx at anduin.net>
+# Copyright (c) 2006-2016 knut st. osmundsen <bird-kBuild-spamx at anduin.net>
#
# This file is part of kBuild.
#
@@ -52,6 +52,7 @@ kUtil_SOURCES.win = \
nt/ntdir.c \
nt/ntstat.c \
nt/ntunlink.c \
+ nt/fts-nt.c \
nt/kFsCache.c \
kStuff/kHlp/CRT/kHlpCRTString.cpp \
kStuff/kHlp/CRT/kHlpCRTAlloc.cpp
@@ -79,5 +80,11 @@ tstNtStat_SOURCES = nt/tstNtStat.c
tstNtStat_LIBS = $(LIB_KUTIL)
tstNtStat_NOINST = 1
+PROGRAMS.win += tstNtFts
+tstNtFts_TEMPLATE = BIN
+tstNtFts_SOURCES = nt/tstNtFts.c nt/fts-nt.c
+tstNtFts_LIBS = $(LIB_KUTIL)
+tstNtFts_NOINST = 1
+
include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/lib/kDep.c b/src/lib/kDep.c
index da7a0dd..186c20c 100644
--- a/src/lib/kDep.c
+++ b/src/lib/kDep.c
@@ -1,4 +1,4 @@
-/* $Id: kDep.c 2886 2016-09-06 14:31:46Z bird $ */
+/* $Id: kDep.c 2955 2016-09-21 19:05:53Z bird $ */
/** @file
* kDep - Common Dependency Managemnt Code.
*/
@@ -58,6 +58,10 @@
#include "kDep.h"
+#ifdef KWORKER
+extern int kwFsPathExists(const char *pszPath);
+#endif
+
/*******************************************************************************
* Global Variables *
@@ -182,13 +186,14 @@ static void fixcase(char *pszFilename)
/**
* 'Optimizes' and corrects the dependencies.
*/
-void depOptimize(int fFixCase, int fQuiet)
+void depOptimize(int fFixCase, int fQuiet, const char *pszIgnoredExt)
{
/*
* Walk the list correct the names and re-insert them.
*/
- PDEP pDepOrg = g_pDeps;
- PDEP pDep = g_pDeps;
+ size_t cchIgnoredExt = pszIgnoredExt ? strlen(pszIgnoredExt) : 0;
+ PDEP pDepOrg = g_pDeps;
+ PDEP pDep = g_pDeps;
g_pDeps = NULL;
for (; pDep; pDep = pDep->pNext)
{
@@ -198,7 +203,7 @@ void depOptimize(int fFixCase, int fQuiet)
char szFilename[PATH_MAX + 1];
#endif
char *pszFilename;
-#ifndef KMK
+#if !defined(KWORKER) && !defined(KMK)
struct stat s;
#endif
@@ -210,6 +215,14 @@ void depOptimize(int fFixCase, int fQuiet)
continue;
pszFilename = pDep->szFilename;
+ /*
+ * Skip pszIgnoredExt if given.
+ */
+ if ( pszIgnoredExt
+ && pDep->cchFilename > cchIgnoredExt
+ && memcmp(&pDep->szFilename[pDep->cchFilename - cchIgnoredExt], pszIgnoredExt, cchIgnoredExt) == 0)
+ continue;
+
#if K_OS != K_OS_OS2 && K_OS != K_OS_WINDOWS
/*
* Skip any drive letters from compilers running in wine.
@@ -238,7 +251,9 @@ void depOptimize(int fFixCase, int fQuiet)
/*
* Check that the file exists before we start depending on it.
*/
-#ifdef KMK
+#ifdef KWORKER
+ if (!kwFsPathExists(pszFilename))
+#elif defined(KMK)
if (!file_exists_p(pszFilename))
#elif K_OS == K_OS_WINDOWS
if (birdStatModTimeOnly(pszFilename, &s.st_mtim, 1 /*fFollowLink*/) != 0)
diff --git a/src/lib/kDep.h b/src/lib/kDep.h
index b1c35aa..ad84a53 100644
--- a/src/lib/kDep.h
+++ b/src/lib/kDep.h
@@ -1,4 +1,4 @@
-/* $Id: kDep.h 2851 2016-08-31 17:30:52Z bird $ */
+/* $Id: kDep.h 2955 2016-09-21 19:05:53Z bird $ */
/** @file
* kDep - Common Dependency Managemnt Code.
*/
@@ -47,7 +47,7 @@ typedef struct DEP
extern PDEP depAdd(const char *pszFilename, size_t cchFilename);
-extern void depOptimize(int fFixCase, int fQuiet);
+extern void depOptimize(int fFixCase, int fQuiet, const char *pszIgnoredExt);
extern void depPrint(FILE *pOutput);
extern void depPrintStubs(FILE *pOutput);
extern void depCleanup(void);
diff --git a/src/lib/kStuff/include/k/kHlpAssert.h b/src/lib/kStuff/include/k/kHlpAssert.h
index 70bc526..aac38be 100644
--- a/src/lib/kStuff/include/k/kHlpAssert.h
+++ b/src/lib/kStuff/include/k/kHlpAssert.h
@@ -1,4 +1,4 @@
-/* $Id: kHlpAssert.h 70 2015-08-13 09:03:02Z bird $ */
+/* $Id: kHlpAssert.h 93 2016-09-15 11:53:59Z bird $ */
/** @file
* kHlpAssert - Assertion Macros.
*/
@@ -221,12 +221,18 @@ extern "C" {
#define kHlpAssertRC(rc) kHlpAssertMsg((rc) == 0, ("%s = %d\n", #rc, (rc)))
#define kHlpAssertRCReturn(rc, rcRet) kHlpAssertMsgReturn((rc) == 0, ("%s = %d -> %d\n", #rc, (rc), (rcRet)), (rcRet))
#define kHlpAssertRCReturnVoid(rc) kHlpAssertMsgReturnVoid((rc) == 0, ("%s = %d -> %d\n", #rc, (rc), (rcRet)))
-#define kHlpAssertFailed() kHlpAssert(0)
-#define kHlpAssertFailedReturn(rcRet) kHlpAssertReturn(0, (rcRet))
-#define kHlpAssertFailedReturnVoid() kHlpAssertReturnVoid(0)
-#define kHlpAssertMsgFailed(msg) kHlpAssertMsg(0, msg)
-#define kHlpAssertMsgFailedReturn(msg, rcRet) kHlpAssertMsgReturn(0, msg, (rcRet))
-#define kHlpAssertMsgFailedReturnVoid(msg) kHlpAssertMsgReturnVoid(0, msg))
+#define kHlpAssertFailed() kHlpAssert(0)
+#define kHlpAssertFailedStmt(stmt) kHlpAssertStmt(0, stmt)
+#define kHlpAssertFailedReturn(rcRet) kHlpAssertReturn(0, (rcRet))
+#define kHlpAssertFailedStmtReturn(stmt, rcRet) kHlpAssertStmtReturn(0, stmt, (rcRet))
+#define kHlpAssertFailedReturnVoid() kHlpAssertReturnVoid(0)
+#define kHlpAssertFailedStmtReturnVoid(stmt) kHlpAssertStmtReturnVoid(0, stmt)
+#define kHlpAssertMsgFailed(msg) kHlpAssertMsg(0, msg)
+#define kHlpAssertMsgFailedStmt(msg, stmt) kHlpAssertMsgStmt(0, msg, stmt)
+#define kHlpAssertMsgFailedReturn(msg, rcRet) kHlpAssertMsgReturn(0, msg, (rcRet))
+#define kHlpAssertMsgFailedStmtReturn(msg, stmt, rcRet) kHlpAssertMsgStmtReturn(0, msg, stmt, (rcRet))
+#define kHlpAssertMsgFailedReturnVoid(msg) kHlpAssertMsgReturnVoid(0, msg)
+#define kHlpAssertMsgFailedStmtReturnVoid(msg, stmt) kHlpAssertMsgStmtReturnVoid(0, msg, stmt)
/**
* Helper function that displays the first part of the assertion message.
diff --git a/src/lib/kStuff/include/k/kTypes.h b/src/lib/kStuff/include/k/kTypes.h
index d79f295..c957566 100644
--- a/src/lib/kStuff/include/k/kTypes.h
+++ b/src/lib/kStuff/include/k/kTypes.h
@@ -1,4 +1,4 @@
-/* $Id: kTypes.h 29 2009-07-01 20:30:29Z bird $ */
+/* $Id: kTypes.h 95 2016-09-26 07:23:08Z bird $ */
/** @file
* kTypes - Typedefs And Related Constants And Macros.
*/
@@ -122,9 +122,21 @@
/** @def KSSIZE_MIN
* Memory size min constant.*/
/** @def KSIZE_PRI
- * Memory size printf format. */
+ * Memory size default printf format (hex). */
+/** @def KSIZE_PRI_U
+ * Memory size unsigned decimal printf format. */
+/** @def KSIZE_PRI_I
+ * Memory size signed decimal printf format. */
+/** @def KSIZE_PRI_X
+ * Memory size hexadecimal printf format. */
/** @def KSSIZE_PRI
- * Memory size printf format. */
+ * Memory size default printf format (hex). */
+/** @def KSSIZE_PRI_U
+ * Memory size unsigned decimal printf format. */
+/** @def KSSIZE_PRI_I
+ * Memory size signed decimal printf format. */
+/** @def KSSIZE_PRI_X
+ * Memory size hexadecimal printf format. */
/** @typedef KIPTR
* Signed integer type capable of containing a pointer value. */
@@ -172,7 +184,7 @@ typedef unsigned char KU8;
#define KI64_C(c) (c ## LL)
#define KU64_C(c) (c ## ULL)
#define KI32_C(c) (c)
-#define KU32_C(c) (c)
+#define KU32_C(c) (c ## U)
#define KI16_C(c) (c)
#define KU16_C(c) (c)
#define KI8_C(c) (c)
@@ -198,6 +210,9 @@ typedef KU32 KSIZE;
#define KSIZE_C(c) KU32_C(c)
#define KSIZE_MAX KU32_MAX
#define KSIZE_PRI KX32_PRI
+#define KSIZE_PRI_U KU32_PRI
+#define KSIZE_PRI_I KI32_PRI
+#define KSIZE_PRI_X KX32_PRI
#define KIPTR_C(c) KI32_C(c)
typedef KI32 KIPTR;
@@ -245,7 +260,7 @@ typedef unsigned short KU16;
typedef signed char KI8;
typedef unsigned char KU8;
#define KI32_C(c) (c)
-#define KU32_C(c) (c)
+#define KU32_C(c) (c ## U)
#define KI16_C(c) (c)
#define KU16_C(c) (c)
#define KI8_C(c) (c)
@@ -266,10 +281,16 @@ typedef KI64 KSSIZE;
#define KSSIZE_MAX KI64_MAX
#define KSSIZE_MIN KI64_MIN
#define KSSIZE_PRI KX64_PRI
+#define KSSIZE_PRI_U KU64_PRI
+#define KSSIZE_PRI_I KI64_PRI
+#define KSSIZE_PRI_X KX64_PRI
typedef KU64 KSIZE;
#define KSIZE_C(c) KU64_C(c)
#define KSIZE_MAX KU64_MAX
+#define KSIZE_PRI_U KU64_PRI
+#define KSIZE_PRI_I KI64_PRI
+#define KSIZE_PRI_X KX64_PRI
#define KSIZE_PRI KX64_PRI
typedef KI64 KIPTR;
diff --git a/src/lib/msc_buffered_printf.c b/src/lib/msc_buffered_printf.c
index 2638b4f..6b7c886 100644
--- a/src/lib/msc_buffered_printf.c
+++ b/src/lib/msc_buffered_printf.c
@@ -1,4 +1,4 @@
-/* $Id: msc_buffered_printf.c 2910 2016-09-10 00:57:29Z bird $ */
+/* $Id: msc_buffered_printf.c 2967 2016-09-26 18:14:13Z bird $ */
/** @file
* printf, vprintf, fprintf, puts, fputs console optimizations for Windows/MSC.
*/
@@ -46,6 +46,12 @@
#undef fputs
#pragma warning(disable: 4273) /* inconsistent dll linkage*/
+#ifndef KWORKER
+# define DLL_IMPORT __declspec(dllexport)
+#else
+# define DLL_IMPORT
+#endif
+
extern size_t maybe_con_fwrite(void const *pvBuf, size_t cbUnit, size_t cUnits, FILE *pFile);
@@ -56,7 +62,7 @@ extern size_t maybe_con_fwrite(void const *pvBuf, size_t cbUnit, size_t cUnits,
* @param pszFormat The format string.
* @param ... Format arguments.
*/
-__declspec(dllexport)
+DLL_IMPORT
int __cdecl printf(const char *pszFormat, ...)
{
int cchRet;
@@ -75,7 +81,7 @@ int __cdecl printf(const char *pszFormat, ...)
* @param pszFormat The format string.
* @param va Format arguments.
*/
-__declspec(dllexport)
+DLL_IMPORT
int __cdecl vprintf(const char *pszFormat, va_list va)
{
/*
@@ -113,7 +119,7 @@ int __cdecl vprintf(const char *pszFormat, va_list va)
* @param pszFormat The format string.
* @param va Format arguments.
*/
-__declspec(dllexport)
+DLL_IMPORT
int __cdecl fprintf(FILE *pFile, const char *pszFormat, ...)
{
va_list va;
@@ -159,7 +165,7 @@ int __cdecl fprintf(FILE *pFile, const char *pszFormat, ...)
* @returns Units written; 0 & errno on failure.
* @param pszString The string to write. (newline is appended)
*/
-__declspec(dllexport)
+DLL_IMPORT
int __cdecl puts(const char *pszString)
{
size_t cchString = strlen(pszString);
@@ -234,7 +240,7 @@ int __cdecl puts(const char *pszString)
* @param pszString The string to write (no newline added).
* @param pFile The output file.
*/
-__declspec(dllexport)
+DLL_IMPORT
int __cdecl fputs(const char *pszString, FILE *pFile)
{
size_t cchString = strlen(pszString);
diff --git a/src/lib/nt/fts-nt.c b/src/lib/nt/fts-nt.c
new file mode 100644
index 0000000..1b815a1
--- /dev/null
+++ b/src/lib/nt/fts-nt.c
@@ -0,0 +1,1015 @@
+/* $Id: fts-nt.c 2992 2016-11-01 22:06:08Z bird $ */
+/** @file
+ * Source for the NT port of BSD fts.c.
+ *
+ * @copyright 1990, 1993, 1994 The Regents of the University of California. All rights reserved.
+ * @copyright NT modifications Copyright (C) 2016 knut st. osmundsen <bird-klibc-spam-xiv at anduin.net>
+ * @licenses BSD3
+ *
+ *
+ * Some hints about how the code works.
+ *
+ * The input directories & files are entered into a pseudo root directory and
+ * processed one after another, depth first.
+ *
+ * Directories are completely read into memory first and arranged as linked
+ * list anchored on FTS::fts_cur. fts_read does a pop-like operation on that
+ * list, freeing the nodes after they've been completely processed.
+ * Subdirectories are returned twice by fts_read, the first time when it
+ * decends into it (FTS_D), and the second time as it ascends from it (FTS_DP).
+ *
+ * In parallel to fts_read, there's the fts_children API that fetches the
+ * directory content in a similar manner, but for the consumption of the API
+ * caller rather than FTS itself. The result hangs on FTS::fts_child so it can
+ * be freed when the directory changes or used by fts_read when it is called
+ * upon to enumerate the directory.
+ *
+ *
+ * The NT port of the code does away with the directory changing in favor of
+ * using directory relative opens (present in NT since for ever, just not
+ * exposed thru Win32). A new FTSENT member fts_dirfd has been added to make
+ * this possible for API users too.
+ *
+ * Note! When using Win32 APIs with path input relative to the current
+ * directory, the internal DOS <-> NT path converter will expand it to a
+ * full path and subject it to the 260 char limit.
+ *
+ * The richer NT directory enumeration API allows us to do away with all the
+ * stat() calls, and not have to do link counting and other interesting things
+ * to try speed things up. (You typical stat() implementation on windows is
+ * actually a directory enum call with the name of the file as filter.)
+ */
+
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $OpenBSD: fts.c,v 1.22 1999/10/03 19:22:22 millert Exp $
+ */
+
+#if 0
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
+#endif /* LIBC_SCCS and not lint */
+#endif
+
+#include <errno.h>
+#include "fts-nt.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "nthlp.h"
+#include "ntdir.h"
+
+static FTSENT *fts_alloc(FTS *, char *, size_t);
+static FTSENT *fts_build(FTS *, int);
+static void fts_lfree(FTSENT *);
+static void fts_load(FTS *, FTSENT *);
+static size_t fts_maxarglen(char * const *);
+static void fts_padjust(FTS *, FTSENT *);
+static int fts_palloc(FTS *, size_t);
+static FTSENT *fts_sort(FTS *, FTSENT *, size_t);
+static int fts_stat(FTS *, FTSENT *, int, HANDLE);
+static int fts_process_stats(FTSENT *, BirdStat_T const *);
+
+#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
+
+#define CLR(opt) (sp->fts_options &= ~(opt))
+#define ISSET(opt) (sp->fts_options & (opt))
+#define SET(opt) (sp->fts_options |= (opt))
+
+/* fts_build flags */
+#define BCHILD 1 /* fts_children */
+#define BNAMES 2 /* fts_children, names only */
+#define BREAD 3 /* fts_read */
+
+/* NT needs these: */
+#define MAXPATHLEN 260
+#define MAX(a, b) ( (a) >= (b) ? (a) : (b) )
+
+#define AT_SYMLINK_NOFOLLOW 1
+#define fstatat(hDir, pszPath, pStat, fFlags) birdStatAt((hDir), (pszPath), (pStat), (fFlags) != AT_SYMLINK_NOFOLLOW)
+#define FTS_NT_DUMMY_SYMFD_VALUE ((HANDLE)~(intptr_t)(2)) /* current process */
+
+/*
+ * Internal representation of an FTS, including extra implementation
+ * details. The FTS returned from fts_open points to this structure's
+ * ftsp_fts member (and can be cast to an _fts_private as required)
+ */
+struct _fts_private {
+ FTS ftsp_fts;
+};
+
+
+FTS * FTSCALL
+nt_fts_open(char * const *argv, int options,
+ int (*compar)(const FTSENT * const *, const FTSENT * const *))
+{
+ struct _fts_private *priv;
+ FTS *sp;
+ FTSENT *p, *root;
+ FTSENT *parent, *tmp;
+ size_t len, nitems;
+
+ /* Options check. */
+ if (options & ~FTS_OPTIONMASK) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /* fts_open() requires at least one path */
+ if (*argv == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /* Allocate/initialize the stream. */
+ if ((priv = calloc(1, sizeof(*priv))) == NULL)
+ return (NULL);
+ sp = &priv->ftsp_fts;
+ sp->fts_compar = compar;
+ sp->fts_options = options;
+ SET(FTS_NOCHDIR); /* NT: FTS_NOCHDIR is always on (for external consumes) */
+
+ /* Shush, GCC. */
+ tmp = NULL;
+
+ /*
+ * Start out with 1K of path space, and enough, in any case,
+ * to hold the user's paths.
+ */
+ if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
+ goto mem1;
+
+ /* Allocate/initialize root's parent. */
+ if ((parent = fts_alloc(sp, "", 0)) == NULL)
+ goto mem2;
+ parent->fts_level = FTS_ROOTPARENTLEVEL;
+
+ /* Allocate/initialize root(s). */
+ for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
+ /* NT: We need to do some small input transformations to make this and
+ the API user code happy. 1. Lone drive letters get a dot
+ appended so it won't matter if a slash is appended afterwards.
+ 2. DOS slashes are converted to UNIX ones. */
+ char *slash;
+ len = strlen(*argv);
+ if (len == 2 && argv[0][1] == ':') {
+ char tmp[4];
+ tmp[0] = argv[0][0];
+ tmp[1] = ':';
+ tmp[2] = '.';
+ tmp[3] = '\0';
+ p = fts_alloc(sp, tmp, 3);
+ } else {
+ p = fts_alloc(sp, *argv, len);
+ }
+#if 1 /* bird */
+ if (p != NULL) { /* likely */ } else { goto mem3; }
+#endif
+ slash = strchr(p->fts_name, '\\');
+ while (slash != NULL) {
+ *slash++ = '/';
+ slash = strchr(p->fts_name, '\\');
+ }
+ p->fts_level = FTS_ROOTLEVEL;
+ p->fts_parent = parent;
+ p->fts_accpath = p->fts_name;
+ p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), INVALID_HANDLE_VALUE);
+
+ /* Command-line "." and ".." are real directories. */
+ if (p->fts_info == FTS_DOT)
+ p->fts_info = FTS_D;
+
+ /*
+ * If comparison routine supplied, traverse in sorted
+ * order; otherwise traverse in the order specified.
+ */
+ if (compar) {
+ p->fts_link = root;
+ root = p;
+ } else {
+ p->fts_link = NULL;
+ if (root == NULL)
+ tmp = root = p;
+ else {
+ tmp->fts_link = p;
+ tmp = p;
+ }
+ }
+ }
+ if (compar && nitems > 1)
+ root = fts_sort(sp, root, nitems);
+
+ /*
+ * Allocate a dummy pointer and make fts_read think that we've just
+ * finished the node before the root(s); set p->fts_info to FTS_INIT
+ * so that everything about the "current" node is ignored.
+ */
+ if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
+ goto mem3;
+ sp->fts_cur->fts_link = root;
+ sp->fts_cur->fts_info = FTS_INIT;
+
+ return (sp);
+
+mem3: fts_lfree(root);
+ free(parent);
+mem2: free(sp->fts_path);
+mem1: free(sp);
+ return (NULL);
+}
+
+
+static void
+fts_load(FTS *sp, FTSENT *p)
+{
+ size_t len;
+ char *cp;
+
+ /*
+ * Load the stream structure for the next traversal. Since we don't
+ * actually enter the directory until after the preorder visit, set
+ * the fts_accpath field specially so the chdir gets done to the right
+ * place and the user can access the first node. From fts_open it's
+ * known that the path will fit.
+ */
+ len = p->fts_pathlen = p->fts_namelen;
+ memmove(sp->fts_path, p->fts_name, len + 1);
+ if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
+ len = strlen(++cp);
+ memmove(p->fts_name, cp, len + 1);
+ p->fts_namelen = len;
+ }
+ p->fts_accpath = p->fts_path = sp->fts_path;
+ sp->fts_dev = p->fts_dev;
+}
+
+int FTSCALL
+nt_fts_close(FTS *sp)
+{
+ FTSENT *freep, *p;
+ /*int saved_errno;*/
+
+ /*
+ * This still works if we haven't read anything -- the dummy structure
+ * points to the root list, so we step through to the end of the root
+ * list which has a valid parent pointer.
+ */
+ if (sp->fts_cur) {
+ for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
+ freep = p;
+ p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
+ free(freep);
+ }
+ free(p);
+ }
+
+ /* Free up child linked list, sort array, path buffer. */
+ if (sp->fts_child)
+ fts_lfree(sp->fts_child);
+ if (sp->fts_array)
+ free(sp->fts_array);
+ free(sp->fts_path);
+
+ /* Free up the stream pointer. */
+ free(sp);
+ return (0);
+}
+
+/*
+ * Special case of "/" at the end of the path so that slashes aren't
+ * appended which would cause paths to be written as "....//foo".
+ */
+#define NAPPEND(p) \
+ (p->fts_path[p->fts_pathlen - 1] == '/' \
+ ? p->fts_pathlen - 1 : p->fts_pathlen)
+
+static void
+fts_free_entry(FTSENT *tmp)
+{
+ if (tmp != NULL) {
+ if (tmp->fts_dirfd != INVALID_HANDLE_VALUE) {
+ birdCloseFile(tmp->fts_dirfd);
+ tmp->fts_dirfd = INVALID_HANDLE_VALUE;
+ }
+ free(tmp);
+ }
+}
+
+FTSENT * FTSCALL
+nt_fts_read(FTS *sp)
+{
+ FTSENT *p, *tmp;
+ int instr;
+ char *t;
+
+ /* If finished or unrecoverable error, return NULL. */
+ if (sp->fts_cur == NULL || ISSET(FTS_STOP))
+ return (NULL);
+
+ /* Set current node pointer. */
+ p = sp->fts_cur;
+
+ /* Save and zero out user instructions. */
+ instr = p->fts_instr;
+ p->fts_instr = FTS_NOINSTR;
+
+ /* Any type of file may be re-visited; re-stat and re-turn. */
+ if (instr == FTS_AGAIN) {
+ p->fts_info = fts_stat(sp, p, 0, INVALID_HANDLE_VALUE);
+ return (p);
+ }
+
+ /*
+ * Following a symlink -- SLNONE test allows application to see
+ * SLNONE and recover. If indirecting through a symlink, have
+ * keep a pointer to current location. If unable to get that
+ * pointer, follow fails.
+ *
+ * NT: Since we don't change directory, we just set fts_symfd to a
+ * placeholder value handle value here in case a API client
+ * checks it. Ditto FTS_SYMFOLLOW.
+ */
+ if (instr == FTS_FOLLOW &&
+ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
+ p->fts_info = fts_stat(sp, p, 1, INVALID_HANDLE_VALUE);
+ if (p->fts_info == FTS_D /*&& !ISSET(FTS_NOCHDIR)*/) {
+ p->fts_symfd = FTS_NT_DUMMY_SYMFD_VALUE;
+ p->fts_flags |= FTS_SYMFOLLOW;
+ }
+ return (p);
+ }
+
+ /* Directory in pre-order. */
+ if (p->fts_info == FTS_D) {
+ /* If skipped or crossed mount point, do post-order visit. */
+ if (instr == FTS_SKIP ||
+ (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
+ if (p->fts_flags & FTS_SYMFOLLOW) {
+ p->fts_symfd = INVALID_HANDLE_VALUE;
+ }
+ if (sp->fts_child) {
+ fts_lfree(sp->fts_child);
+ sp->fts_child = NULL;
+ }
+ p->fts_info = FTS_DP;
+ return (p);
+ }
+
+ /* Rebuild if only read the names and now traversing. */
+ if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
+ CLR(FTS_NAMEONLY);
+ fts_lfree(sp->fts_child);
+ sp->fts_child = NULL;
+ }
+
+ /*
+ * Cd to the subdirectory.
+ *
+ * If have already read and now fail to chdir, whack the list
+ * to make the names come out right, and set the parent errno
+ * so the application will eventually get an error condition.
+ * Set the FTS_DONTCHDIR flag so that when we logically change
+ * directories back to the parent we don't do a chdir.
+ *
+ * If haven't read do so. If the read fails, fts_build sets
+ * FTS_STOP or the fts_info field of the node.
+ */
+ if (sp->fts_child != NULL) {
+ /* nothing to do */
+ } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
+ if (ISSET(FTS_STOP))
+ return (NULL);
+ return (p);
+ }
+ p = sp->fts_child;
+ sp->fts_child = NULL;
+ goto name;
+ }
+
+ /* Move to the next node on this level. */
+next: tmp = p;
+ if ((p = p->fts_link) != NULL) {
+ /*
+ * If reached the top, return to the original directory (or
+ * the root of the tree), and load the paths for the next root.
+ */
+ if (p->fts_level == FTS_ROOTLEVEL) {
+ fts_free_entry(tmp);
+ fts_load(sp, p);
+ return (sp->fts_cur = p);
+ }
+
+ /*
+ * User may have called fts_set on the node. If skipped,
+ * ignore. If followed, get a file descriptor so we can
+ * get back if necessary.
+ */
+ if (p->fts_instr == FTS_SKIP) {
+ fts_free_entry(tmp);
+ goto next;
+ }
+ if (p->fts_instr == FTS_FOLLOW) {
+ p->fts_info = fts_stat(sp, p, 1, INVALID_HANDLE_VALUE);
+ /* NT: See above regarding fts_symfd. */
+ if (p->fts_info == FTS_D /*&& !ISSET(FTS_NOCHDIR)*/) {
+ p->fts_symfd = FTS_NT_DUMMY_SYMFD_VALUE;
+ p->fts_flags |= FTS_SYMFOLLOW;
+ }
+ p->fts_instr = FTS_NOINSTR;
+ }
+
+ fts_free_entry(tmp);
+
+name: t = sp->fts_path + NAPPEND(p->fts_parent);
+ *t++ = '/';
+ memmove(t, p->fts_name, p->fts_namelen + 1);
+ return (sp->fts_cur = p);
+ }
+
+ /* Move up to the parent node. */
+ p = tmp->fts_parent;
+
+ if (p->fts_level == FTS_ROOTPARENTLEVEL) {
+ /*
+ * Done; free everything up and set errno to 0 so the user
+ * can distinguish between error and EOF.
+ */
+ fts_free_entry(tmp);
+ fts_free_entry(p);
+ errno = 0;
+ return (sp->fts_cur = NULL);
+ }
+
+ /* NUL terminate the pathname. */
+ sp->fts_path[p->fts_pathlen] = '\0';
+
+ /*
+ * Return to the parent directory. If at a root node or came through
+ * a symlink, go back through the file descriptor. Otherwise, cd up
+ * one directory.
+ *
+ * NT: We're doing no fchdir, but we need to close the directory handle
+ * and clear fts_symfd now.
+ */
+ if (p->fts_flags & FTS_SYMFOLLOW) {
+ p->fts_symfd = INVALID_HANDLE_VALUE;
+ }
+ if (p->fts_dirfd != INVALID_HANDLE_VALUE) {
+ birdCloseFile(p->fts_dirfd);
+ p->fts_dirfd = INVALID_HANDLE_VALUE;
+ }
+ fts_free_entry(tmp);
+ p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
+ return (sp->fts_cur = p);
+}
+
+/*
+ * Fts_set takes the stream as an argument although it's not used in this
+ * implementation; it would be necessary if anyone wanted to add global
+ * semantics to fts using fts_set. An error return is allowed for similar
+ * reasons.
+ */
+/* ARGSUSED */
+int FTSCALL
+nt_fts_set(FTS *sp, FTSENT *p, int instr)
+{
+ if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
+ instr != FTS_NOINSTR && instr != FTS_SKIP) {
+ errno = EINVAL;
+ return (1);
+ }
+ p->fts_instr = instr;
+ return (0);
+}
+
+FTSENT * FTSCALL
+nt_fts_children(FTS *sp, int instr)
+{
+ FTSENT *p;
+
+ if (instr != 0 && instr != FTS_NAMEONLY) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /* Set current node pointer. */
+ p = sp->fts_cur;
+
+ /*
+ * Errno set to 0 so user can distinguish empty directory from
+ * an error.
+ */
+ errno = 0;
+
+ /* Fatal errors stop here. */
+ if (ISSET(FTS_STOP))
+ return (NULL);
+
+ /* Return logical hierarchy of user's arguments. */
+ if (p->fts_info == FTS_INIT)
+ return (p->fts_link);
+
+ /*
+ * If not a directory being visited in pre-order, stop here. Could
+ * allow FTS_DNR, assuming the user has fixed the problem, but the
+ * same effect is available with FTS_AGAIN.
+ */
+ if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
+ return (NULL);
+
+ /* Free up any previous child list. */
+ if (sp->fts_child != NULL) {
+ fts_lfree(sp->fts_child);
+ sp->fts_child = NULL; /* (bird - double free for _open(".") failure in original) */
+ }
+
+ /* NT: Some BSD utility sets FTS_NAMEONLY? We don't really need this
+ optimization, but since it only hurts that utility, it can stay. */
+ if (instr == FTS_NAMEONLY) {
+ assert(0); /* don't specify FTS_NAMEONLY on NT. */
+ SET(FTS_NAMEONLY);
+ instr = BNAMES;
+ } else
+ instr = BCHILD;
+
+ return (sp->fts_child = fts_build(sp, instr));
+}
+
+#ifndef fts_get_clientptr
+#error "fts_get_clientptr not defined"
+#endif
+
+void *
+(FTSCALL fts_get_clientptr)(FTS *sp)
+{
+
+ return (fts_get_clientptr(sp));
+}
+
+#ifndef fts_get_stream
+#error "fts_get_stream not defined"
+#endif
+
+FTS *
+(FTSCALL fts_get_stream)(FTSENT *p)
+{
+ return (fts_get_stream(p));
+}
+
+void FTSCALL
+nt_fts_set_clientptr(FTS *sp, void *clientptr)
+{
+
+ sp->fts_clientptr = clientptr;
+}
+
+/*
+ * This is the tricky part -- do not casually change *anything* in here. The
+ * idea is to build the linked list of entries that are used by fts_children
+ * and fts_read. There are lots of special cases.
+ *
+ * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
+ * set and it's a physical walk (so that symbolic links can't be directories),
+ * we can do things quickly. First, if it's a 4.4BSD file system, the type
+ * of the file is in the directory entry. Otherwise, we assume that the number
+ * of subdirectories in a node is equal to the number of links to the parent.
+ * The former skips all stat calls. The latter skips stat calls in any leaf
+ * directories and for any files after the subdirectories in the directory have
+ * been found, cutting the stat calls by about 2/3.
+ *
+ * NT: We do not do any link counting or stat avoiding, which invalidates the
+ * above warnings. This function is very simple for us.
+ */
+static FTSENT *
+fts_build(FTS *sp, int type)
+{
+ BirdDirEntry_T *dp;
+ FTSENT *p, *head;
+ FTSENT *cur, *tail;
+ DIR *dirp;
+ void *oldaddr;
+ char *cp;
+ int saved_errno, doadjust;
+ long level;
+ size_t dnamlen, len, maxlen, nitems;
+ unsigned fDirOpenFlags;
+
+ /* Set current node pointer. */
+ cur = sp->fts_cur;
+
+ /*
+ * Open the directory for reading. If this fails, we're done.
+ * If being called from fts_read, set the fts_info field.
+ *
+ * NT: We do a two stage open so we can keep the directory handle around
+ * after we've enumerated the directory. The dir handle is used by
+ * us here and by the API users to more efficiently and safely open
+ * members of the directory.
+ */
+ fDirOpenFlags = BIRDDIR_F_EXTRA_INFO | BIRDDIR_F_KEEP_HANDLE;
+ if (cur->fts_dirfd == INVALID_HANDLE_VALUE) {
+ if (cur->fts_parent->fts_dirfd != INVALID_HANDLE_VALUE) {
+ /* (This works fine for symlinks too, since we follow them.) */
+ cur->fts_dirfd = birdOpenFileEx(cur->fts_parent->fts_dirfd,
+ cur->fts_name,
+ FILE_READ_DATA | SYNCHRONIZE,
+ 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,
+ OBJ_CASE_INSENSITIVE);
+ } else {
+ cur->fts_dirfd = birdOpenFile(cur->fts_accpath,
+ FILE_READ_DATA | SYNCHRONIZE,
+ 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,
+ OBJ_CASE_INSENSITIVE);
+ }
+ } else {
+ fDirOpenFlags |= BIRDDIR_F_RESTART_SCAN;
+ }
+ dirp = birdDirOpenFromHandle(cur->fts_dirfd, NULL, fDirOpenFlags);
+ if (dirp == NULL) {
+ if (type == BREAD) {
+ cur->fts_info = FTS_DNR;
+ cur->fts_errno = errno;
+ }
+ return (NULL);
+ }
+
+ /*
+ * Figure out the max file name length that can be stored in the
+ * current path -- the inner loop allocates more path as necessary.
+ * We really wouldn't have to do the maxlen calculations here, we
+ * could do them in fts_read before returning the path, but it's a
+ * lot easier here since the length is part of the dirent structure.
+ *
+ * If not changing directories set a pointer so that can just append
+ * each new name into the path.
+ */
+ len = NAPPEND(cur);
+ cp = sp->fts_path + len;
+ *cp++ = '/';
+ len++;
+ maxlen = sp->fts_pathlen - len;
+
+ level = cur->fts_level + 1;
+
+ /* Read the directory, attaching each entry to the `link' pointer. */
+ doadjust = 0;
+ for (head = tail = NULL, nitems = 0; dirp && (dp = birdDirRead(dirp));) {
+ dnamlen = dp->d_namlen;
+ if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
+ continue;
+
+ if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL)
+ goto mem1;
+ if (dnamlen >= maxlen) { /* include space for NUL */
+ oldaddr = sp->fts_path;
+ if (fts_palloc(sp, dnamlen + len + 1)) {
+ /*
+ * No more memory for path or structures. Save
+ * errno, free up the current structure and the
+ * structures already allocated.
+ */
+mem1: saved_errno = errno;
+ if (p)
+ free(p);
+ fts_lfree(head);
+ birdDirClose(dirp);
+ birdCloseFile(cur->fts_dirfd);
+ cur->fts_dirfd = INVALID_HANDLE_VALUE;
+ cur->fts_info = FTS_ERR;
+ SET(FTS_STOP);
+ errno = saved_errno;
+ return (NULL);
+ }
+ /* Did realloc() change the pointer? */
+ if (oldaddr != sp->fts_path) {
+ doadjust = 1;
+ if (1 /*ISSET(FTS_NOCHDIR)*/)
+ cp = sp->fts_path + len;
+ }
+ maxlen = sp->fts_pathlen - len;
+ }
+
+ p->fts_level = level;
+ p->fts_parent = sp->fts_cur;
+ p->fts_pathlen = len + dnamlen;
+ p->fts_accpath = p->fts_path;
+ p->fts_stat = dp->d_stat;
+ p->fts_info = fts_process_stats(p, &dp->d_stat);
+
+ /* We walk in directory order so "ls -f" doesn't get upset. */
+ p->fts_link = NULL;
+ if (head == NULL)
+ head = tail = p;
+ else {
+ tail->fts_link = p;
+ tail = p;
+ }
+ ++nitems;
+ }
+
+ birdDirClose(dirp);
+
+ /*
+ * If realloc() changed the address of the path, adjust the
+ * addresses for the rest of the tree and the dir list.
+ */
+ if (doadjust)
+ fts_padjust(sp, head);
+
+ /*
+ * Reset the path back to original state.
+ */
+ sp->fts_path[cur->fts_pathlen] = '\0';
+
+ /* If didn't find anything, return NULL. */
+ if (!nitems) {
+ if (type == BREAD)
+ cur->fts_info = FTS_DP;
+ return (NULL);
+ }
+
+ /* Sort the entries. */
+ if (sp->fts_compar && nitems > 1)
+ head = fts_sort(sp, head, nitems);
+ return (head);
+}
+
+
+/**
+ * @note Only used on NT with input arguments, FTS_AGAIN, and links that needs
+ * following. On link information is generally retrieved during directory
+ * enumeration on NT, in line with it's DOS/OS2/FAT API heritage.
+ */
+static int
+fts_stat(FTS *sp, FTSENT *p, int follow, HANDLE dfd)
+{
+ int saved_errno;
+ const char *path;
+
+ if (dfd == INVALID_HANDLE_VALUE) {
+ path = p->fts_accpath;
+ } else {
+ path = p->fts_name;
+ }
+
+ /*
+ * If doing a logical walk, or application requested FTS_FOLLOW, do
+ * a stat(2). If that fails, check for a non-existent symlink. If
+ * fail, set the errno from the stat call.
+ */
+ if (ISSET(FTS_LOGICAL) || follow) {
+ if (fstatat(dfd, path, &p->fts_stat, 0)) {
+ saved_errno = errno;
+ if (fstatat(dfd, path, &p->fts_stat, AT_SYMLINK_NOFOLLOW)) {
+ p->fts_errno = saved_errno;
+ goto err;
+ }
+ errno = 0;
+ if (S_ISLNK(p->fts_stat.st_mode))
+ return (FTS_SLNONE);
+ }
+ } else if (fstatat(dfd, path, &p->fts_stat, AT_SYMLINK_NOFOLLOW)) {
+ p->fts_errno = errno;
+err: memset(&p->fts_stat, 0, sizeof(struct stat));
+ return (FTS_NS);
+ }
+ return fts_process_stats(p, &p->fts_stat);
+}
+
+/* Shared between fts_stat and fts_build. */
+static int
+fts_process_stats(FTSENT *p, BirdStat_T const *sbp)
+{
+ if (S_ISDIR(sbp->st_mode)) {
+ FTSENT *t;
+ fts_dev_t dev;
+ fts_ino_t ino;
+
+ /*
+ * Set the device/inode. Used to find cycles and check for
+ * crossing mount points. Also remember the link count, used
+ * in fts_build to limit the number of stat calls. It is
+ * understood that these fields are only referenced if fts_info
+ * is set to FTS_D.
+ */
+ dev = p->fts_dev = sbp->st_dev;
+ ino = p->fts_ino = sbp->st_ino;
+ p->fts_nlink = sbp->st_nlink;
+
+ if (ISDOT(p->fts_name))
+ return (FTS_DOT);
+
+ /*
+ * Cycle detection is done by brute force when the directory
+ * is first encountered. If the tree gets deep enough or the
+ * number of symbolic links to directories is high enough,
+ * something faster might be worthwhile.
+ */
+ for (t = p->fts_parent;
+ t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
+ if (ino == t->fts_ino && dev == t->fts_dev) {
+ p->fts_cycle = t;
+ return (FTS_DC);
+ }
+ return (FTS_D);
+ }
+ if (S_ISLNK(sbp->st_mode))
+ return (FTS_SL);
+ if (S_ISREG(sbp->st_mode))
+ return (FTS_F);
+ return (FTS_DEFAULT);
+}
+
+/*
+ * The comparison function takes pointers to pointers to FTSENT structures.
+ * Qsort wants a comparison function that takes pointers to void.
+ * (Both with appropriate levels of const-poisoning, of course!)
+ * Use a trampoline function to deal with the difference.
+ */
+static int
+fts_compar(const void *a, const void *b)
+{
+ FTS *parent;
+
+ parent = (*(const FTSENT * const *)a)->fts_fts;
+ return (*parent->fts_compar)(a, b);
+}
+
+static FTSENT *
+fts_sort(FTS *sp, FTSENT *head, size_t nitems)
+{
+ FTSENT **ap, *p;
+
+ /*
+ * Construct an array of pointers to the structures and call qsort(3).
+ * Reassemble the array in the order returned by qsort. If unable to
+ * sort for memory reasons, return the directory entries in their
+ * current order. Allocate enough space for the current needs plus
+ * 40 so don't realloc one entry at a time.
+ */
+ if (nitems > sp->fts_nitems) {
+ void *ptr;
+ sp->fts_nitems = nitems + 40;
+ ptr = realloc(sp->fts_array, sp->fts_nitems * sizeof(FTSENT *));
+ if (ptr != NULL) {
+ sp->fts_array = ptr;
+ } else {
+ free(sp->fts_array);
+ sp->fts_array = NULL;
+ sp->fts_nitems = 0;
+ return (head);
+ }
+ }
+ for (ap = sp->fts_array, p = head; p; p = p->fts_link)
+ *ap++ = p;
+ qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar);
+ for (head = *(ap = sp->fts_array); --nitems; ++ap)
+ ap[0]->fts_link = ap[1];
+ ap[0]->fts_link = NULL;
+ return (head);
+}
+
+static FTSENT *
+fts_alloc(FTS *sp, char *name, size_t namelen)
+{
+ FTSENT *p;
+ size_t len;
+
+ struct ftsent_withstat {
+ FTSENT ent;
+ struct stat statbuf;
+ };
+
+ /*
+ * The file name is a variable length array. Allocate the FTSENT
+ * structure and the file name.
+ */
+ len = sizeof(FTSENT) + namelen + 1;
+ if ((p = malloc(len)) == NULL)
+ return (NULL);
+
+ p->fts_name = (char *)(p + 1);
+ p->fts_statp = &p->fts_stat;
+
+ /* Copy the name and guarantee NUL termination. */
+ memcpy(p->fts_name, name, namelen);
+ p->fts_name[namelen] = '\0';
+ p->fts_namelen = namelen;
+ p->fts_path = sp->fts_path;
+ p->fts_errno = 0;
+ p->fts_flags = 0;
+ p->fts_instr = FTS_NOINSTR;
+ p->fts_number = 0;
+ p->fts_pointer = NULL;
+ p->fts_fts = sp;
+ p->fts_symfd = INVALID_HANDLE_VALUE;
+ p->fts_dirfd = INVALID_HANDLE_VALUE;
+ return (p);
+}
+
+static void
+fts_lfree(FTSENT *head)
+{
+ FTSENT *p;
+
+ /* Free a linked list of structures. */
+ while ((p = head)) {
+ head = head->fts_link;
+ assert(p->fts_dirfd == INVALID_HANDLE_VALUE);
+ free(p);
+ }
+}
+
+/*
+ * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
+ * Most systems will allow creation of paths much longer than MAXPATHLEN, even
+ * though the kernel won't resolve them. Add the size (not just what's needed)
+ * plus 256 bytes so don't realloc the path 2 bytes at a time.
+ */
+static int
+fts_palloc(FTS *sp, size_t more)
+{
+ void *ptr;
+
+ sp->fts_pathlen += more + 256;
+ ptr = realloc(sp->fts_path, sp->fts_pathlen);
+ if (ptr) {
+ /*likely */
+ } else {
+ free(sp->fts_path);
+ }
+ sp->fts_path = ptr;
+ return (ptr == NULL);
+}
+
+/*
+ * When the path is realloc'd, have to fix all of the pointers in structures
+ * already returned.
+ */
+static void
+fts_padjust(FTS *sp, FTSENT *head)
+{
+ FTSENT *p;
+ char *addr = sp->fts_path;
+
+#define ADJUST(p) do { \
+ if ((p)->fts_accpath != (p)->fts_name) { \
+ (p)->fts_accpath = \
+ (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
+ } \
+ (p)->fts_path = addr; \
+} while (0)
+ /* Adjust the current set of children. */
+ for (p = sp->fts_child; p; p = p->fts_link)
+ ADJUST(p);
+
+ /* Adjust the rest of the tree, including the current level. */
+ for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
+ ADJUST(p);
+ p = p->fts_link ? p->fts_link : p->fts_parent;
+ }
+}
+
+static size_t
+fts_maxarglen(char * const *argv)
+{
+ size_t len, max;
+
+ for (max = 0; *argv; ++argv)
+ if ((len = strlen(*argv)) > max)
+ max = len;
+ return (max + 1);
+}
+
diff --git a/src/lib/nt/fts-nt.h b/src/lib/nt/fts-nt.h
new file mode 100644
index 0000000..dc990b0
--- /dev/null
+++ b/src/lib/nt/fts-nt.h
@@ -0,0 +1,177 @@
+/* $Id: fts-nt.h 2997 2016-11-01 23:28:02Z bird $ */
+/** @file
+ * Header for the NT port of BSD fts.h.
+ *
+ * @copyright Copyright (c) 1989, 1993 The Regents of the University of California. All rights reserved.
+ * @copyright NT modifications Copyright (C) 2016 knut st. osmundsen <bird-klibc-spam-xiv at anduin.net>
+ * @licenses BSD3
+ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fts.h 8.3 (Berkeley) 8/14/94
+ * $FreeBSD$
+ *
+ */
+
+#ifndef INCLUDED_FTS_NT_H
+#define INCLUDED_FTS_NT_H
+
+#include <sys/types.h>
+#include <stdint.h>
+#include "ntstat.h" /* ensure correct stat structure */
+
+typedef uint64_t fts_dev_t;
+typedef uint64_t fts_ino_t;
+typedef uint32_t fts_nlink_t;
+#ifdef _WINNT_
+typedef HANDLE fts_fd_t;
+# define NT_FTS_INVALID_HANDLE_VALUE INVALID_HANDLE_VALUE
+#else
+typedef void * fts_fd_t;
+# define NT_FTS_INVALID_HANDLE_VALUE ((void *)~(uintptr_t)0)
+#endif
+#define FTSCALL __cdecl
+
+typedef struct {
+ struct _ftsent *fts_cur; /* current node */
+ struct _ftsent *fts_child; /* linked list of children */
+ struct _ftsent **fts_array; /* sort array */
+ fts_dev_t fts_dev; /* starting device # */
+ char *fts_path; /* path for this descent */
+ size_t fts_pathlen; /* sizeof(path) */
+ size_t fts_nitems; /* elements in the sort array */
+ int (FTSCALL *fts_compar) /* compare function */
+ (const struct _ftsent * const *, const struct _ftsent * const *);
+
+#define FTS_COMFOLLOW 0x001 /* follow command line symlinks */
+#define FTS_LOGICAL 0x002 /* logical walk */
+#define FTS_NOCHDIR 0x004 /* don't change directories */
+#define FTS_NOSTAT 0x008 /* don't get stat info */
+#define FTS_PHYSICAL 0x010 /* physical walk */
+#define FTS_SEEDOT 0x020 /* return dot and dot-dot */
+#define FTS_XDEV 0x040 /* don't cross devices */
+#if 0 /* No whiteout on NT. */
+#define FTS_WHITEOUT 0x080 /* return whiteout information */
+#endif
+#define FTS_OPTIONMASK 0x0ff /* valid user option mask */
+
+#define FTS_NAMEONLY 0x100 /* (private) child names only */
+#define FTS_STOP 0x200 /* (private) unrecoverable error */
+ int fts_options; /* fts_open options, global flags */
+ void *fts_clientptr; /* thunk for sort function */
+} FTS;
+
+typedef struct _ftsent {
+ struct _ftsent *fts_cycle; /* cycle node */
+ struct _ftsent *fts_parent; /* parent directory */
+ struct _ftsent *fts_link; /* next file in directory */
+ int64_t fts_number; /* local numeric value */
+#define fts_bignum fts_number /* XXX non-std, should go away */
+ void *fts_pointer; /* local address value */
+ char *fts_accpath; /* access path */
+ char *fts_path; /* root path */
+ int fts_errno; /* errno for this node */
+ fts_fd_t fts_symfd; /* NT: Normally -1; -2 we followed this symlinked dir */
+ fts_fd_t fts_dirfd; /* NT: Handle to the directory (NT_FTS_)INVALID_HANDLE_VALUE if not valid */
+ size_t fts_pathlen; /* strlen(fts_path) */
+ size_t fts_namelen; /* strlen(fts_name) */
+
+ fts_ino_t fts_ino; /* inode */
+ fts_dev_t fts_dev; /* device */
+ fts_nlink_t fts_nlink; /* link count */
+
+#define FTS_ROOTPARENTLEVEL -1
+#define FTS_ROOTLEVEL 0
+ long fts_level; /* depth (-1 to N) */
+
+#define FTS_D 1 /* preorder directory */
+#define FTS_DC 2 /* directory that causes cycles */
+#define FTS_DEFAULT 3 /* none of the above */
+#define FTS_DNR 4 /* unreadable directory */
+#define FTS_DOT 5 /* dot or dot-dot */
+#define FTS_DP 6 /* postorder directory */
+#define FTS_ERR 7 /* error; errno is set */
+#define FTS_F 8 /* regular file */
+#define FTS_INIT 9 /* initialized only */
+#define FTS_NS 10 /* stat(2) failed */
+#define FTS_NSOK 11 /* no stat(2) requested */
+#define FTS_SL 12 /* symbolic link */
+#define FTS_SLNONE 13 /* symbolic link without target */
+//#define FTS_W 14 /* whiteout object */
+ int fts_info; /* user status for FTSENT structure */
+
+#define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */
+#define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */
+#define FTS_ISW 0x04 /* this is a whiteout object */
+ unsigned fts_flags; /* private flags for FTSENT structure */
+
+#define FTS_AGAIN 1 /* read node again */
+#define FTS_FOLLOW 2 /* follow symbolic link */
+#define FTS_NOINSTR 3 /* no instructions */
+#define FTS_SKIP 4 /* discard node */
+ int fts_instr; /* fts_set() instructions */
+
+ struct stat *fts_statp; /* stat(2) information */
+ char *fts_name; /* file name */
+ FTS *fts_fts; /* back pointer to main FTS */
+ BirdStat_T fts_stat; /* NT: We always got stat info. */
+} FTSENT;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+FTSENT *FTSCALL nt_fts_children(FTS *, int);
+int FTSCALL nt_fts_close(FTS *);
+void *FTSCALL nt_fts_get_clientptr(FTS *);
+#define fts_get_clientptr(fts) ((fts)->fts_clientptr)
+FTS *FTSCALL nt_fts_get_stream(FTSENT *);
+#define fts_get_stream(ftsent) ((ftsent)->fts_fts)
+FTS *FTSCALL nt_fts_open(char * const *, int,
+ int (FTSCALL*)(const FTSENT * const *, const FTSENT * const *));
+FTSENT *FTSCALL nt_fts_read(FTS *);
+int FTSCALL nt_fts_set(FTS *, FTSENT *, int);
+void FTSCALL nt_fts_set_clientptr(FTS *, void *);
+
+/* API mappings. */
+#define fts_children(a_pFts, a_iInstr) nt_fts_children(a_pFts, a_iInstr)
+#define fts_close(a_pFts) nt_fts_close(a_pFts)
+#define fts_open(a_papszArgs, a_fOptions, a_pfnCompare) nt_fts_open(a_papszArgs, a_fOptions, a_pfnCompare)
+#define fts_read(a_pFts) nt_fts_read(a_pFts)
+#define fts_set(a_pFts, a_pFtsEntry, a_iInstr) nt_fts_set(a_pFts, a_pFtsEntry, a_iInstr)
+#define fts_set_clientptr(a_pFts, a_pvUser) nt_fts_set_clientptr(a_pFts, a_pvUser)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !INCLUDED_FTS_NT_H */
+
diff --git a/src/lib/nt/kFsCache.c b/src/lib/nt/kFsCache.c
index dd3140b..cb28198 100644
--- a/src/lib/nt/kFsCache.c
+++ b/src/lib/nt/kFsCache.c
@@ -1,4 +1,4 @@
-/* $Id: kFsCache.c 2879 2016-09-05 20:14:21Z bird $ */
+/* $Id: kFsCache.c 2985 2016-11-01 18:26:35Z bird $ */
/** @file
* ntdircache.c - NT directory content cache.
*/
@@ -40,7 +40,9 @@
#include <stdio.h>
#include <mbstring.h>
#include <wchar.h>
-//#include <intrin.h>
+#ifdef _MSC_VER
+# include <intrin.h>
+#endif
//#include <setjmp.h>
//#include <ctype.h>
@@ -87,6 +89,11 @@ typedef KFSDIRREPOP *PKFSDIRREPOP;
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *penmError);
+
/**
* Retains a reference to a cache object, internal version.
@@ -199,12 +206,12 @@ static KSIZE kFsCacheStrHashEx(const char *pszString, KU32 *puHash)
* @param pchString Pointer to the substring (not terminated).
* @param cchString The length of the substring.
*/
-static KU32 kFsCacheStrHashN(const char *pszString, KSIZE cchString)
+static KU32 kFsCacheStrHashN(const char *pchString, KSIZE cchString)
{
KU32 uHash = 0;
while (cchString-- > 0)
{
- KU32 uChar = (unsigned char)*pszString++;
+ KU32 uChar = (unsigned char)*pchString++;
uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
}
return uHash;
@@ -587,6 +594,9 @@ PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent,
pObj->abUnused[1] = K_FALSE;
pObj->fFlags = pParent->Obj.fFlags & KFSOBJ_F_INHERITED_MASK;
pObj->pParent = pParent;
+ pObj->uNameHash = 0;
+ pObj->pNextNameHash = NULL;
+ pObj->pNameAlloc = NULL;
pObj->pUserDataHead = NULL;
#ifdef KFSCACHE_CFG_UTF16
@@ -636,7 +646,7 @@ PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent,
kHlpAssert(pbExtra - (KU8 *)pObj == cbObj);
/*
- * Type specific initilization.
+ * Type specific initialization.
*/
if (fDirish)
{
@@ -644,8 +654,8 @@ PKFSOBJ kFsCacheCreateObject(PKFSCACHE pCache, PKFSDIR pParent,
pDirObj->cChildren = 0;
pDirObj->cChildrenAllocated = 0;
pDirObj->papChildren = NULL;
- pDirObj->cHashTab = 0;
- pDirObj->paHashTab = NULL;
+ pDirObj->fHashTabMask = 0;
+ pDirObj->papHashTab = NULL;
pDirObj->hDir = INVALID_HANDLE_VALUE;
pDirObj->uDevNo = pParent->uDevNo;
pDirObj->iLastWrite = 0;
@@ -794,11 +804,135 @@ static PKFSOBJ kFsCacheCreateMissingW(PKFSCACHE pCache, PKFSDIR pParent, const w
return NULL;
}
+
+/**
+ * Does the growing of names.
+ *
+ * @returns pCur
+ * @param pCache The cache.
+ * @param pCur The object.
+ * @param pchName The name (not necessarily terminated).
+ * @param cchName Name length.
+ * @param pwcName The UTF-16 name (not necessarily terminated).
+ * @param cwcName The length of the UTF-16 name in wchar_t's.
+ * @param pchShortName The short name.
+ * @param cchShortName The length of the short name. This is 0 if no short
+ * name.
+ * @param pwcShortName The short UTF-16 name.
+ * @param cwcShortName The length of the short UTF-16 name. This is 0 if
+ * no short name.
+ */
+static PKFSOBJ kFsCacheRefreshGrowNames(PKFSCACHE pCache, PKFSOBJ pCur,
+ const char *pchName, KU32 cchName,
+ wchar_t const *pwcName, KU32 cwcName
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ , const char *pchShortName, KU32 cchShortName,
+ wchar_t const *pwcShortName, KU32 cwcShortName
+#endif
+ )
+{
+ PKFSOBJNAMEALLOC pNameAlloc;
+ char *pch;
+ KU32 cbNeeded;
+
+ pCache->cNameGrowths++;
+
+ /*
+ * Figure out our requirements.
+ */
+ cbNeeded = sizeof(KU32) + cchName + 1;
+#ifdef KFSCACHE_CFG_UTF16
+ cbNeeded += (cwcName + 1) * sizeof(wchar_t);
+#endif
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ cbNeeded += cchShortName + !!cchShortName;
+# ifdef KFSCACHE_CFG_UTF16
+ cbNeeded += (cwcShortName + !!cwcShortName) * sizeof(wchar_t);
+# endif
+#endif
+ cbNeeded = K_ALIGN_Z(cbNeeded, 8); /* Memory will likely be 8 or 16 byte aligned, so we might just claim it. */
+
+ /*
+ * Allocate memory.
+ */
+ pNameAlloc = pCur->pNameAlloc;
+ if (!pNameAlloc)
+ {
+ pNameAlloc = (PKFSOBJNAMEALLOC)kHlpAlloc(cbNeeded);
+ if (!pNameAlloc)
+ return pCur;
+ pCache->cbObjects += cbNeeded;
+ pCur->pNameAlloc = pNameAlloc;
+ pNameAlloc->cb = cbNeeded;
+ }
+ else if (pNameAlloc->cb < cbNeeded)
+ {
+ pNameAlloc = (PKFSOBJNAMEALLOC)kHlpRealloc(pNameAlloc, cbNeeded);
+ if (!pNameAlloc)
+ return pCur;
+ pCache->cbObjects += cbNeeded - pNameAlloc->cb;
+ pCur->pNameAlloc = pNameAlloc;
+ pNameAlloc->cb = cbNeeded;
+ }
+
+ /*
+ * Copy out the new names, starting with the wide char ones to avoid misaligning them.
+ */
+ pch = &pNameAlloc->abSpace[0];
+
+#ifdef KFSCACHE_CFG_UTF16
+ pCur->pwszName = (wchar_t *)pch;
+ pCur->cwcName = cwcName;
+ pch = kHlpMemPCopy(pch, pwcName, cwcName * sizeof(wchar_t));
+ *pch++ = '\0';
+ *pch++ = '\0';
+
+# ifdef KFSCACHE_CFG_SHORT_NAMES
+ if (cwcShortName == 0)
+ {
+ pCur->pwszShortName = pCur->pwszName;
+ pCur->cwcShortName = pCur->cwcName;
+ }
+ else
+ {
+ pCur->pwszShortName = (wchar_t *)pch;
+ pCur->cwcShortName = cwcShortName;
+ pch = kHlpMemPCopy(pch, pwcShortName, cwcShortName * sizeof(wchar_t));
+ *pch++ = '\0';
+ *pch++ = '\0';
+ }
+# endif
+#endif
+
+ pCur->pszName = pch;
+ pCur->cchName = cchName;
+ pch = kHlpMemPCopy(pch, pchName, cchShortName);
+ *pch++ = '\0';
+
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ if (cchShortName == 0)
+ {
+ pCur->pszShortName = pCur->pszName;
+ pCur->cchShortName = pCur->cchName;
+ }
+ else
+ {
+ pCur->pszShortName = pch;
+ pCur->cchShortName = cchShortName;
+ pch = kHlpMemPCopy(pch, pchShortName, cchShortName);
+ *pch++ = '\0';
+ }
+#endif
+
+ return pCur;
+}
+
+
/**
* Worker for kFsCacheDirFindOldChild that refreshes the file ID value on an
* object found by name.
*
- * @returns Pointer to the existing object if found, NULL if not.
+ * @returns pCur.
* @param pDirRePop Repopulation data.
* @param pCur The object to check the names of.
* @param idFile The file ID.
@@ -818,7 +952,7 @@ static PKFSOBJ kFsCacheDirRefreshOldChildFileId(PKFSDIRREPOP pDirRePop, PKFSOBJ
* Worker for kFsCacheDirFindOldChild that checks the names after an old object
* has been found the file ID.
*
- * @returns Pointer to the existing object if found, NULL if not.
+ * @returns pCur.
* @param pDirRePop Repopulation data.
* @param pCur The object to check the names of.
* @param pwcName The file name.
@@ -832,11 +966,15 @@ static PKFSOBJ kFsCacheDirRefreshOldChildName(PKFSDIRREPOP pDirRePop, PKFSOBJ pC
#endif
)
{
+ char szName[KFSCACHE_CFG_MAX_ANSI_NAME];
+ int cchName;
+
+ pDirRePop->pCache->cNameChanges++;
+
/*
* Convert the names to ANSI first, that way we know all the lengths.
*/
- char szName[KFSCACHE_CFG_MAX_ANSI_NAME];
- int cchName = WideCharToMultiByte(CP_ACP, 0, pwcName, cwcName, szName, sizeof(szName) - 1, NULL, NULL);
+ cchName = WideCharToMultiByte(CP_ACP, 0, pwcName, cwcName, szName, sizeof(szName) - 1, NULL, NULL);
if (cchName >= 0)
{
#ifdef KFSCACHE_CFG_SHORT_NAMES
@@ -906,34 +1044,25 @@ static PKFSOBJ kFsCacheDirRefreshOldChildName(PKFSDIRREPOP pDirRePop, PKFSOBJ pC
return pCur;
}
}
+
+ return kFsCacheRefreshGrowNames(pDirRePop->pCache, pCur, szName, cchName, pwcName, cwcName,
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ szShortName, cchShortName, pwcShortName, cwcShortName
+#endif
+ );
}
}
-
- fprintf(stderr,
- "kFsCacheDirRefreshOldChildName - not implemented!\n"
- " Old name: %#x '%ls'\n"
- " New name: %#x '%*.*ls'\n"
- " Old short: %#x '%ls'\n"
- " New short: %#x '%*.*ls'\n",
- pCur->cwcName, pCur->pwszName,
- cwcName, cwcName, cwcName, pwcName,
- pCur->cwcShortName, pCur->pwszShortName,
- cwcShortName, cwcShortName, cwcShortName, pwcShortName);
- __debugbreak();
- /** @todo implement this. It's not entirely straight forward, especially if
- * the name increases! Also, it's something that may happend during
- * individual object refresh and we might want to share code... */
-
+ fprintf(stderr, "kFsCacheDirRefreshOldChildName: WideCharToMultiByte error\n");
return pCur;
}
/**
* Worker for kFsCacheDirFindOldChild that checks the names after an old object
- * has been found the file ID.
+ * has been found by the file ID.
*
- * @returns Pointer to the existing object if found, NULL if not.
+ * @returns pCur.
* @param pDirRePop Repopulation data.
* @param pCur The object to check the names of.
* @param pwcName The file name.
@@ -1109,7 +1238,7 @@ K_INLINE PKFSOBJ kFsCacheDirFindOldChild(PKFSDIRREPOP pDirRePop, KI64 idFile, wc
KU32 cOldChildren = pDirRePop->cOldChildren;
if (cOldChildren > 0)
{
- KU32 const iNextOldChild = K_MIN(pDirRePop->iNextOldChild, cOldChildren);
+ KU32 const iNextOldChild = K_MIN(pDirRePop->iNextOldChild, cOldChildren - 1);
PKFSOBJ pCur = pDirRePop->papOldChildren[iNextOldChild];
if ( pCur->Stats.st_ino == idFile
@@ -1252,8 +1381,23 @@ static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLO
*/
else if (pDir->fPopulated)
{
- KU32 cAllocated = K_ALIGN_Z(pDir->cChildren, 16);
- void *pvNew = kHlpAlloc(sizeof(pDir->papChildren[0]) * cAllocated);
+ KU32 cAllocated;
+ void *pvNew;
+
+ /* Make sure we really need to do this first. */
+ if (!pDir->fNeedRePopulating)
+ {
+ if ( pDir->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
+ || pDir->Obj.uCacheGen == pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
+ return K_TRUE;
+ if ( kFsCacheRefreshObj(pCache, &pDir->Obj, penmError)
+ && !pDir->fNeedRePopulating)
+ return K_TRUE;
+ }
+
+ /* Yes we do need to. */
+ cAllocated = K_ALIGN_Z(pDir->cChildren, 16);
+ pvNew = kHlpAlloc(sizeof(pDir->papChildren[0]) * cAllocated);
if (pvNew)
{
DirRePop.papOldChildren = pDir->papChildren;
@@ -1515,6 +1659,23 @@ static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLO
KFSCACHE_LOG(("Refreshing %s/%s/ - %s was removed.\n",
pDir->Obj.pParent->Obj.pszName, pDir->Obj.pszName, pOldChild->pszName));
kHlpAssert(pOldChild->bObjType != KFSOBJ_TYPE_DIR);
+ /* Remove from hash table. */
+ if (pOldChild->uNameHash != 0)
+ {
+ KU32 idx = pOldChild->uNameHash & pDir->fHashTabMask;
+ PKFSOBJ pPrev = pDir->papHashTab[idx];
+ if (pPrev == pOldChild)
+ pDir->papHashTab[idx] = pOldChild->pNextNameHash;
+ else
+ {
+ while (pPrev && pPrev->pNextNameHash != pOldChild)
+ pPrev = pPrev->pNextNameHash;
+ kHlpAssert(pPrev);
+ if (pPrev)
+ pPrev->pNextNameHash = pOldChild->pNextNameHash;
+ }
+ pOldChild->uNameHash = 0;
+ }
}
kFsCacheObjRelease(pCache, pOldChild);
}
@@ -1586,16 +1747,26 @@ static KBOOL kFsCacheDirIsModified(PKFSDIR pDir)
if ( pDir->hDir != INVALID_HANDLE_VALUE
&& (pDir->Obj.fFlags & KFSOBJ_F_WORKING_DIR_MTIME) )
{
- MY_IO_STATUS_BLOCK Ios;
- MY_FILE_BASIC_INFORMATION BasicInfo;
- MY_NTSTATUS rcNt;
+ if (!pDir->fNeedRePopulating)
+ {
+ MY_IO_STATUS_BLOCK Ios;
+ MY_FILE_BASIC_INFORMATION BasicInfo;
+ MY_NTSTATUS rcNt;
- Ios.Information = -1;
- Ios.u.Status = -1;
+ Ios.Information = -1;
+ Ios.u.Status = -1;
- rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
- if (MY_NT_SUCCESS(rcNt))
- return BasicInfo.LastWriteTime.QuadPart != pDir->iLastWrite;
+ rcNt = g_pfnNtQueryInformationFile(pDir->hDir, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
+ if (MY_NT_SUCCESS(rcNt))
+ {
+ if (BasicInfo.LastWriteTime.QuadPart != pDir->iLastWrite)
+ {
+ pDir->fNeedRePopulating = K_TRUE;
+ return K_TRUE;
+ }
+ return K_FALSE;
+ }
+ }
}
/* The cache root never changes. */
else if (!pDir->Obj.pParent)
@@ -1810,6 +1981,7 @@ static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *
KFSCACHE_LOG(("Refreshing %s/%s, ID changed %#llx -> %#llx and names too...\n",
pObj->pParent->Obj.pszName, pObj->pszName, pObj->Stats.st_ino, uBuf.WithId.FileId.QuadPart));
fprintf(stderr, "kFsCacheRefreshObj - ID + name change not implemented!!\n");
+ fflush(stderr);
__debugbreak();
pObj->Stats.st_ino = uBuf.WithId.FileId.QuadPart;
/** @todo implement as needed. */
@@ -1895,6 +2067,7 @@ static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *
/* ouch! */
kHlpAssertMsgFailed(("%#x\n", rcNt));
fprintf(stderr, "kFsCacheRefreshObj - rcNt=%#x on dir - not implemented!\n", rcNt);
+ fflush(stderr);
__debugbreak();
fRc = K_FALSE;
}
@@ -1915,15 +2088,17 @@ static KBOOL kFsCacheRefreshObj(PKFSCACHE pCache, PKFSOBJ pObj, KFSLOOKUPERROR *
* missing node.
* @param pCache The cache.
* @param chLetter The uppercased drive letter.
+ * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
* @param penmError Where to return details as to why the lookup
* failed.
*/
-static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPERROR *penmError)
+static PKFSOBJ kFsCacheLookupDrive(PKFSCACHE pCache, char chLetter, KU32 fFlags, KFSLOOKUPERROR *penmError)
{
- KU32 const uHash = chLetter - 'A';
+ KU32 const uNameHash = chLetter - 'A';
+ PKFSOBJ pCur = pCache->RootDir.papHashTab[uNameHash];
+
KU32 cLeft;
PKFSOBJ *ppCur;
-
MY_UNICODE_STRING NtPath;
wchar_t wszTmp[8];
char szTmp[4];
@@ -1931,19 +2106,32 @@ static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPER
/*
* Custom drive letter hashing.
*/
- if (pCache->RootDir.paHashTab)
+ kHlpAssert((uNameHash & pCache->RootDir.fHashTabMask) == uNameHash);
+ while (pCur)
{
- /** @todo PKFSOBJHASH pHash = */
+ if ( pCur->uNameHash == uNameHash
+ && pCur->cchName == 2
+ && pCur->pszName[0] == chLetter
+ && pCur->pszName[1] == ':')
+ {
+ if (pCur->bObjType == KFSOBJ_TYPE_DIR)
+ return pCur;
+ if ( (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
+ || kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
+ return pCur;
+ return NULL;
+ }
+ pCur = pCur->pNextNameHash;
}
/*
- * Special cased lookup.
+ * Make 100% sure it's not there.
*/
cLeft = pCache->RootDir.cChildren;
ppCur = pCache->RootDir.papChildren;
while (cLeft-- > 0)
{
- PKFSOBJ pCur = *ppCur++;
+ pCur = *ppCur++;
if ( pCur->cchName == 2
&& pCur->pszName[0] == chLetter
&& pCur->pszName[1] == ':')
@@ -1951,12 +2139,19 @@ static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPER
if (pCur->bObjType == KFSOBJ_TYPE_DIR)
return pCur;
kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
- if (kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
+ if ( (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
+ || kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
return pCur;
return NULL;
}
}
+ if (fFlags & KFSCACHE_LOOKUP_F_NO_INSERT)
+ {
+ *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; /* close enough */
+ return NULL;
+ }
+
/*
* Need to add it. We always keep the drive letters open for the benefit
* of kFsCachePopuplateOrRefreshDir and others.
@@ -1975,7 +2170,8 @@ static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPER
{
HANDLE hDir;
MY_NTSTATUS rcNt;
- rcNt = birdOpenFileUniStr(&NtPath,
+ rcNt = birdOpenFileUniStr(NULL /*hRoot*/,
+ &NtPath,
FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
@@ -2050,7 +2246,13 @@ static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPER
*/
fRc = kFsCacheDirAddChild(pCache, &pCache->RootDir, &pDir->Obj, penmError);
kFsCacheObjRelease(pCache, &pDir->Obj);
- return fRc ? &pDir->Obj : NULL;
+ if (fRc)
+ {
+ pDir->Obj.pNextNameHash = pCache->RootDir.papHashTab[uNameHash];
+ pCache->RootDir.papHashTab[uNameHash] = &pDir->Obj;
+ return &pDir->Obj;
+ }
+ return NULL;
}
g_pfnNtClose(hDir);
@@ -2101,6 +2303,82 @@ static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPER
/**
+ * Slow path that allocates the child hash table and enters the given one.
+ *
+ * Allocation fialures are ignored.
+ *
+ * @param pCache The cache (for stats).
+ * @param pDir The directory.
+ * @param uNameHash The name hash to enter @a pChild under.
+ * @param pChild The child to enter into the hash table.
+ */
+static void kFsCacheDirAllocHashTabAndEnterChild(PKFSCACHE pCache, PKFSDIR pDir, KU32 uNameHash, PKFSOBJ pChild)
+{
+ if (uNameHash != 0) /* paranoia ^ 4! */
+ {
+ /*
+ * Double the current number of children and round up to a multiple of
+ * two so we can avoid division.
+ */
+ KU32 cbHashTab;
+ KU32 cEntries;
+ kHlpAssert(pDir->cChildren > 0);
+ if (pDir->cChildren <= KU32_MAX / 4)
+ {
+#if defined(_MSC_VER) && 1
+ KU32 cEntriesRaw = pDir->cChildren * 2;
+ KU32 cEntriesShift;
+ kHlpAssert(sizeof(cEntries) == (unsigned long));
+ if (_BitScanReverse(&cEntriesShift, cEntriesRaw))
+ {
+ if ( K_BIT32(cEntriesShift) < cEntriesRaw
+ && cEntriesShift < 31U)
+ cEntriesShift++;
+ cEntries = K_BIT32(cEntriesShift);
+ }
+ else
+ {
+ kHlpAssertFailed();
+ cEntries = KU32_MAX / 2 + 1;
+ }
+#else
+ cEntries = pDir->cChildren * 2 - 1;
+ cEntries |= cEntries >> 1;
+ cEntries |= cEntries >> 2;
+ cEntries |= cEntries >> 4;
+ cEntries |= cEntries >> 8;
+ cEntries |= cEntries >> 16;
+ cEntries++;
+#endif
+ }
+ else
+ cEntries = KU32_MAX / 2 + 1;
+ kHlpAssert((cEntries & (cEntries - 1)) == 0);
+
+ cbHashTab = cEntries * sizeof(pDir->papHashTab[0]);
+ pDir->papHashTab = (PKFSOBJ *)kHlpAllocZ(cbHashTab);
+ if (pDir->papHashTab)
+ {
+ KU32 idx;
+ pDir->fHashTabMask = cEntries - 1;
+ pCache->cbObjects += cbHashTab;
+ pCache->cChildHashTabs++;
+ pCache->cChildHashEntriesTotal += cEntries;
+
+ /*
+ * Insert it.
+ */
+ pChild->uNameHash = uNameHash;
+ idx = uNameHash & (pDir->fHashTabMask);
+ pChild->pNextNameHash = pDir->papHashTab[idx];
+ pDir->papHashTab[idx] = pChild;
+ pCache->cChildHashed++;
+ }
+ }
+}
+
+
+/**
* Look up a child node, ANSI version.
*
* @returns Pointer to the child if found, NULL if not.
@@ -2111,18 +2389,52 @@ static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPER
*/
static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char *pchName, KU32 cchName)
{
- /* Check for '.' first. */
+ /*
+ * Check for '.' first ('..' won't appear).
+ */
if (cchName != 1 || *pchName != '.')
{
- KU32 cLeft;
PKFSOBJ *ppCur;
+ KU32 cLeft;
+ KU32 uNameHash;
- if (pParent->paHashTab != NULL)
+ /*
+ * Do hash table lookup.
+ *
+ * This caches previous lookups, which should be useful when looking up
+ * intermediate directories at least.
+ */
+ if (pParent->papHashTab != NULL)
{
- /** @todo directory hash table lookup. */
+ PKFSOBJ pCur;
+ uNameHash = kFsCacheStrHashN(pchName, cchName);
+ pCur = pParent->papHashTab[uNameHash & pParent->fHashTabMask];
+ while (pCur)
+ {
+ if ( pCur->uNameHash == uNameHash
+ && ( ( pCur->cchName == cchName
+ && _mbsnicmp(pCur->pszName, pchName, cchName) == 0)
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ || ( pCur->cchShortName == cchName
+ && pCur->pszShortName != pCur->pszName
+ && _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
+#endif
+ )
+ )
+ {
+ pCache->cChildHashHits++;
+ pCache->cChildSearches++;
+ return pCur;
+ }
+ pCur = pCur->pNextNameHash;
+ }
}
+ else
+ uNameHash = 0;
- /* Linear search. */
+ /*
+ * Do linear search.
+ */
cLeft = pParent->cChildren;
ppCur = pParent->papChildren;
while (cLeft-- > 0)
@@ -2136,8 +2448,37 @@ static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char
&& _mbsnicmp(pCur->pszShortName, pchName, cchName) == 0)
#endif
)
+ {
+ /*
+ * Consider entering it into the parent hash table.
+ * Note! We hash the input, not the name we found.
+ */
+ if ( pCur->uNameHash == 0
+ && pParent->cChildren >= 2)
+ {
+ if (pParent->papHashTab)
+ {
+ if (uNameHash != 0)
+ {
+ KU32 idxNameHash = uNameHash & pParent->fHashTabMask;
+ pCur->uNameHash = uNameHash;
+ pCur->pNextNameHash = pParent->papHashTab[idxNameHash];
+ pParent->papHashTab[idxNameHash] = pCur;
+ if (pCur->pNextNameHash)
+ pCache->cChildHashCollisions++;
+ pCache->cChildHashed++;
+ }
+ }
+ else
+ kFsCacheDirAllocHashTabAndEnterChild(pCache, pParent, kFsCacheStrHashN(pchName, cchName), pCur);
+ }
+
+ pCache->cChildSearches++;
return pCur;
+ }
}
+
+ pCache->cChildSearches++;
return NULL;
}
return &pParent->Obj;
@@ -2155,18 +2496,52 @@ static PKFSOBJ kFsCacheFindChildA(PKFSCACHE pCache, PKFSDIR pParent, const char
*/
static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwcName, KU32 cwcName)
{
- /* Check for '.' first. */
+ /*
+ * Check for '.' first ('..' won't appear).
+ */
if (cwcName != 1 || *pwcName != '.')
{
- KU32 cLeft;
PKFSOBJ *ppCur;
+ KU32 cLeft;
+ KU32 uNameHash;
- if (pParent->paHashTab != NULL)
+ /*
+ * Do hash table lookup.
+ *
+ * This caches previous lookups, which should be useful when looking up
+ * intermediate directories at least.
+ */
+ if (pParent->papHashTab != NULL)
{
- /** @todo directory hash table lookup. */
+ PKFSOBJ pCur;
+ uNameHash = kFsCacheUtf16HashN(pwcName, cwcName);
+ pCur = pParent->papHashTab[uNameHash & pParent->fHashTabMask];
+ while (pCur)
+ {
+ if ( pCur->uNameHash == uNameHash
+ && ( ( pCur->cwcName == cwcName
+ && kFsCacheIAreEqualW(pCur->pwszName, pwcName, cwcName))
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ || ( pCur->cwcShortName == cwcName
+ && pCur->pwszShortName != pCur->pwszName
+ && kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
+#endif
+ )
+ )
+ {
+ pCache->cChildHashHits++;
+ pCache->cChildSearches++;
+ return pCur;
+ }
+ pCur = pCur->pNextNameHash;
+ }
}
+ else
+ uNameHash = 0;
- /* Linear search. */
+ /*
+ * Do linear search.
+ */
cLeft = pParent->cChildren;
ppCur = pParent->papChildren;
while (cLeft-- > 0)
@@ -2180,8 +2555,36 @@ static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar
&& kFsCacheIAreEqualW(pCur->pwszShortName, pwcName, cwcName))
#endif
)
+ {
+ /*
+ * Consider entering it into the parent hash table.
+ * Note! We hash the input, not the name we found.
+ */
+ if ( pCur->uNameHash == 0
+ && pParent->cChildren >= 4)
+ {
+ if (pParent->papHashTab)
+ {
+ if (uNameHash != 0)
+ {
+ KU32 idxNameHash = uNameHash & pParent->fHashTabMask;
+ pCur->uNameHash = uNameHash;
+ pCur->pNextNameHash = pParent->papHashTab[idxNameHash];
+ pParent->papHashTab[idxNameHash] = pCur;
+ if (pCur->pNextNameHash)
+ pCache->cChildHashCollisions++;
+ pCache->cChildHashed++;
+ }
+ }
+ else
+ kFsCacheDirAllocHashTabAndEnterChild(pCache, pParent, kFsCacheUtf16HashN(pwcName, cwcName), pCur);
+ }
+
+ pCache->cChildSearches++;
return pCur;
+ }
}
+ pCache->cChildSearches++;
return NULL;
}
return &pParent->Obj;
@@ -2198,12 +2601,30 @@ static PKFSOBJ kFsCacheFindChildW(PKFSCACHE pCache, PKFSDIR pParent, const wchar
* node.
* @param pCache The cache.
* @param pszPath The path.
+ * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
* @param poff Where to return the root dire.
* @param penmError Where to return details as to why the lookup
* failed.
*/
-static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
+static PKFSOBJ kFsCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 fFlags,
+ KU32 *poff, KFSLOOKUPERROR *penmError)
{
+ /*
+ * Special case: Long path prefix w/ drive letter following it.
+ * Note! Must've been converted from wide char to ANSI.
+ */
+ if ( IS_SLASH(pszPath[0])
+ && IS_SLASH(pszPath[1])
+ && pszPath[2] == '?'
+ && IS_SLASH(pszPath[3])
+ && IS_ALPHA(pszPath[4])
+ && pszPath[5] == ':'
+ && IS_SLASH(pszPath[6]) )
+ {
+ *poff = 4 + 2;
+ return kFsCacheLookupDrive(pCache, pszPath[4], fFlags, penmError);
+ }
+
#if 0 /* later */
KU32 offStartServer;
KU32 offEndServer;
@@ -2249,12 +2670,30 @@ static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, K
* node.
* @param pCache The cache.
* @param pwszPath The path.
- * @param poff Where to return the root dire.
+ * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
+ * @param poff Where to return the root dir.
* @param penmError Where to return details as to why the lookup
* failed.
*/
-static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
+static PKFSOBJ kFsCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 fFlags,
+ KU32 *poff, KFSLOOKUPERROR *penmError)
{
+ /*
+ * Special case: Long path prefix w/ drive letter following it.
+ */
+ if ( IS_SLASH(pwszPath[0])
+ && IS_SLASH(pwszPath[1])
+ && pwszPath[2] == '?'
+ && IS_SLASH(pwszPath[3])
+ && IS_ALPHA(pwszPath[4])
+ && pwszPath[5] == ':'
+ && IS_SLASH(pwszPath[6]) )
+ {
+ *poff = 4 + 2;
+ return kFsCacheLookupDrive(pCache, (char)pwszPath[4], fFlags, penmError);
+ }
+
+
#if 0 /* later */
KU32 offStartServer;
KU32 offEndServer;
@@ -2303,13 +2742,14 @@ static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPat
* @param pParent The directory to start the lookup in.
* @param pszPath The path to walk.
* @param cchPath The length of the path.
+ * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
* @param penmError Where to return details as to why the lookup
* failed.
* @param ppLastAncestor Where to return the last parent element found
* (referenced) in case of error an path/file not
* found problem. Optional.
*/
-PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath,
+PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, KU32 fFlags,
KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
{
/*
@@ -2344,11 +2784,13 @@ PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const ch
/*
* Do we need to populate or refresh this directory first?
*/
- if ( pParent->fPopulated
+ if ( !pParent->fNeedRePopulating
+ && pParent->fPopulated
&& ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
|| pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
{ /* likely */ }
- else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
+ else if ( (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
+ || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
{ /* likely */ }
else
return NULL;
@@ -2364,7 +2806,8 @@ PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const ch
{ /* probably likely */ }
else
{
- if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
+ if ( (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
+ && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT))
pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
{
@@ -2387,6 +2830,7 @@ PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const ch
if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
|| pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
|| pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
+ || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
|| kFsCacheRefreshMissing(pCache, pChild, penmError) )
{ /* likely */ }
else
@@ -2408,7 +2852,8 @@ PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const ch
return NULL;
}
else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
- || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
+ || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
+ || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
{
*penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
if (ppLastAncestor)
@@ -2443,13 +2888,14 @@ PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const ch
* @param pParent The directory to start the lookup in.
* @param pszPath The path to walk. No dot-dot bits allowed!
* @param cchPath The length of the path.
+ * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
* @param penmError Where to return details as to why the lookup
* failed.
* @param ppLastAncestor Where to return the last parent element found
* (referenced) in case of error an path/file not
* found problem. Optional.
*/
-PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath,
+PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
{
/*
@@ -2484,11 +2930,13 @@ PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wc
/*
* Do we need to populate or refresh this directory first?
*/
- if ( pParent->fPopulated
+ if ( !pParent->fNeedRePopulating
+ && pParent->fPopulated
&& ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
|| pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
{ /* likely */ }
- else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
+ else if ( (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
+ || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
{ /* likely */ }
else
return NULL;
@@ -2504,7 +2952,8 @@ PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wc
{ /* probably likely */ }
else
{
- if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
+ if ( (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
+ && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT))
pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError);
if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath)
{
@@ -2527,6 +2976,7 @@ PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wc
if ( pChild->bObjType != KFSOBJ_TYPE_MISSING
|| pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
|| pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
+ || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
|| kFsCacheRefreshMissing(pCache, pChild, penmError) )
{ /* likely */ }
else
@@ -2548,7 +2998,9 @@ PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wc
return NULL;
}
else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
- || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
+ || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
+ || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) )
+
{
*penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
if (ppLastAncestor)
@@ -2582,13 +3034,14 @@ PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wc
* @param pCache The cache.
* @param pszPath The path to walk. No dot-dot bits allowed!
* @param cchPath The length of the path.
+ * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
* @param penmError Where to return details as to why the lookup
* failed.
* @param ppLastAncestor Where to return the last parent element found
* (referenced) in case of error an path/file not
* found problem. Optional.
*/
-static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath,
+static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags,
KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
{
PKFSOBJ pRoot;
@@ -2608,11 +3061,11 @@ static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU
/* Drive letter. */
offEnd = 2;
kHlpAssert(IS_SLASH(pszPath[2]));
- pRoot = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), penmError);
+ pRoot = kFsCacheLookupDrive(pCache, toupper(pszPath[0]), fFlags, penmError);
}
else if ( IS_SLASH(pszPath[0])
&& IS_SLASH(pszPath[1]) )
- pRoot = kFswCacheLookupUncShareA(pCache, pszPath, &offEnd, penmError);
+ pRoot = kFsCacheLookupUncShareA(pCache, pszPath, fFlags, &offEnd, penmError);
else
{
*penmError = KFSLOOKUPERROR_UNSUPPORTED;
@@ -2639,6 +3092,7 @@ static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU
|| pRoot->uCacheGen == ( pRoot->bObjType != KFSOBJ_TYPE_MISSING
? pCache->auGenerations[ pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
: pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
+ || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
|| kFsCacheRefreshObj(pCache, pRoot, penmError))
return kFsCacheObjRetainInternal(pRoot);
return NULL;
@@ -2660,7 +3114,7 @@ static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU
* remainder of the path starting with it.
*/
return kFsCacheLookupRelativeToDirA(pCache, (PKFSDIR)pRoot, &pszPath[offEnd + cchSlashes],
- cchPath - offEnd - cchSlashes, penmError, ppLastAncestor);
+ cchPath - offEnd - cchSlashes, fFlags, penmError, ppLastAncestor);
}
@@ -2676,13 +3130,14 @@ static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU
* @param pCache The cache.
* @param pwszPath The path to walk.
* @param cwcPath The length of the path (in wchar_t's).
+ * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
* @param penmError Where to return details as to why the lookup
* failed.
* @param ppLastAncestor Where to return the last parent element found
* (referenced) in case of error an path/file not
* found problem. Optional.
*/
-static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath,
+static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
{
PKFSDIR pParent = &pCache->RootDir;
@@ -2705,11 +3160,11 @@ static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath
/* Drive letter. */
offEnd = 2;
kHlpAssert(IS_SLASH(pwszPath[2]));
- pRoot = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), penmError);
+ pRoot = kFsCacheLookupDrive(pCache, toupper(pwszPath[0]), fFlags, penmError);
}
else if ( IS_SLASH(pwszPath[0])
&& IS_SLASH(pwszPath[1]) )
- pRoot = kFswCacheLookupUncShareW(pCache, pwszPath, &offEnd, penmError);
+ pRoot = kFsCacheLookupUncShareW(pCache, pwszPath, fFlags, &offEnd, penmError);
else
{
*penmError = KFSLOOKUPERROR_UNSUPPORTED;
@@ -2736,6 +3191,7 @@ static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath
|| pRoot->uCacheGen == (pRoot->bObjType != KFSOBJ_TYPE_MISSING
? pCache->auGenerations[ pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
: pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
+ || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
|| kFsCacheRefreshObj(pCache, pRoot, penmError))
return kFsCacheObjRetainInternal(pRoot);
return NULL;
@@ -2757,7 +3213,7 @@ static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath
* remainder of the path starting with it.
*/
return kFsCacheLookupRelativeToDirW(pCache, (PKFSDIR)pRoot, &pwszPath[offEnd + cwcSlashes],
- cwcPath - offEnd - cwcSlashes, penmError, ppLastAncestor);
+ cwcPath - offEnd - cwcSlashes, fFlags, penmError, ppLastAncestor);
}
@@ -2771,13 +3227,14 @@ static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath
* @param pCache The cache.
* @param pszPath The path.
* @param cchPath The length of the path.
+ * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
* @param penmError Where to return details as to why the lookup
* failed.
* @param ppLastAncestor Where to return the last parent element found
* (referenced) in case of error an path/file not
* found problem. Optional.
*/
-static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath,
+static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags,
KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
{
/*
@@ -2789,22 +3246,8 @@ static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 c
if ( cchFull >= 3
&& cchFull < sizeof(szFull))
{
- PKFSOBJ pFsObj;
KFSCACHE_LOG2(("kFsCacheLookupSlowA(%s)\n", pszPath));
- pFsObj = kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, penmError, ppLastAncestor);
-
-#if 0 /* No need to do this until it's actually queried. */
- /* Cache the resulting path. */
- if ( pFsObj
- || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
- || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
- {
- KU32 uHashPath = kFsCacheStrHash(szFull);
- kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
- uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
- }
-#endif
- return pFsObj;
+ return kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, fFlags, penmError, ppLastAncestor);
}
/* The path is too long! */
@@ -2824,13 +3267,14 @@ static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 c
* @param pCache The cache.
* @param pwszPath The path.
* @param cwcPath The length of the path (in wchar_t's).
+ * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX.
* @param penmError Where to return details as to why the lookup
* failed.
* @param ppLastAncestor Where to return the last parent element found
* (referenced) in case of error an path/file not
* found problem. Optional.
*/
-static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath,
+static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KU32 fFlags,
KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
{
/*
@@ -2842,22 +3286,8 @@ static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU
if ( cwcFull >= 3
&& cwcFull < KFSCACHE_CFG_MAX_PATH)
{
- PKFSOBJ pFsObj;
KFSCACHE_LOG2(("kFsCacheLookupSlowA(%ls)\n", pwszPath));
- pFsObj = kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, penmError, ppLastAncestor);
-
-#if 0 /* No need to do this until it's actually queried. */
- /* Cache the resulting path. */
- if ( pFsObj
- || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
- || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
- {
- KU32 uHashPath = kFsCacheStrHash(szFull);
- kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
- uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
- }
-#endif
- return pFsObj;
+ return kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, fFlags, penmError, ppLastAncestor);
}
/* The path is too long! */
@@ -2881,10 +3311,10 @@ static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU
if (!pHashEntry->pFsObj)
{
if (pHashEntry->fAbsolute)
- pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
+ pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
&pHashEntry->enmError, &pLastAncestor);
else
- pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
+ pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
&pHashEntry->enmError, &pLastAncestor);
}
else
@@ -2899,10 +3329,10 @@ static PKFSHASHA kFsCacheRefreshPathA(PKFSCACHE pCache, PKFSHASHA pHashEntry, KU
{
kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
if (pHashEntry->fAbsolute)
- pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
+ pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
&pHashEntry->enmError, &pLastAncestor);
else
- pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
+ pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
&pHashEntry->enmError, &pLastAncestor);
}
}
@@ -2942,10 +3372,10 @@ static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU
if (!pHashEntry->pFsObj)
{
if (pHashEntry->fAbsolute)
- pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
+ pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
&pHashEntry->enmError, &pLastAncestor);
else
- pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
+ pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
&pHashEntry->enmError, &pLastAncestor);
}
else
@@ -2960,16 +3390,17 @@ static PKFSHASHW kFsCacheRefreshPathW(PKFSCACHE pCache, PKFSHASHW pHashEntry, KU
{
kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
if (pHashEntry->fAbsolute)
- pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
+ pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
&pHashEntry->enmError, &pLastAncestor);
else
- pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
+ pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
&pHashEntry->enmError, &pLastAncestor);
}
}
else
{
fprintf(stderr, "kFsCacheRefreshPathW - refresh failure handling not implemented!\n");
+ fflush(stderr);
__debugbreak();
/** @todo just remove this entry. */
return NULL;
@@ -3068,12 +3499,12 @@ static PKFSOBJ kFsCacheLookupHashedA(PKFSCACHE pCache, const char *pchPath, KU32
&& IS_SLASH(pchPath[1]) ) )
&& !kFsCacheHasDotDotA(pchPath, cchPath) )
{
- pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, penmError, &pLastAncestor);
+ pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor);
fAbsolute = K_TRUE;
}
else
{
- pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, penmError, &pLastAncestor);
+ pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor);
fAbsolute = K_FALSE;
}
if ( pFsObj
@@ -3176,12 +3607,12 @@ static PKFSOBJ kFsCacheLookupHashedW(PKFSCACHE pCache, const wchar_t *pwcPath, K
&& IS_SLASH(pwcPath[1]) ) )
&& !kFsCacheHasDotDotW(pwcPath, cwcPath) )
{
- pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, penmError, &pLastAncestor);
+ pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor);
fAbsolute = K_TRUE;
}
else
{
- pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, penmError, &pLastAncestor);
+ pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor);
fAbsolute = K_FALSE;
}
if ( pFsObj
@@ -3369,18 +3800,21 @@ PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSL
* @returns 0
* @param pCache The cache.
* @param pObj The object.
+ * @param pszWhere Where it was released from.
*/
-KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
+KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj, const char *pszWhere)
{
kHlpAssert(pObj->cRefs == 0);
kHlpAssert(pObj->pParent == NULL);
kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
- KFSCACHE_LOG(("Destroying %s/%s, type=%d\n", pObj->pParent ? pObj->pParent->Obj.pszName : "", pObj->pszName, pObj->bObjType));
+ KFSCACHE_LOG(("Destroying %s/%s, type=%d, pObj=%p, pszWhere=%s\n",
+ pObj->pParent ? pObj->pParent->Obj.pszName : "", pObj->pszName, pObj->bObjType, pObj, pszWhere));
if (pObj->abUnused[1] != 0)
{
fprintf(stderr, "Destroying %s/%s, type=%d, path hash entries: %d!\n", pObj->pParent ? pObj->pParent->Obj.pszName : "",
pObj->pszName, pObj->bObjType, pObj->abUnused[0]);
+ fflush(stderr);
__debugbreak();
}
@@ -3417,7 +3851,7 @@ KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
KU32 cChildren = pDir->cChildren;
pCache->cbObjects -= sizeof(*pDir)
+ K_ALIGN_Z(cChildren, 16) * sizeof(pDir->papChildren)
- + pDir->cHashTab * sizeof(pDir->paHashTab);
+ + (pDir->fHashTabMask + !!pDir->fHashTabMask) * sizeof(pDir->papHashTab[0]);
pDir->cChildren = 0;
while (cChildren-- > 0)
@@ -3425,8 +3859,8 @@ KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
kHlpFree(pDir->papChildren);
pDir->papChildren = NULL;
- kHlpFree(pDir->paHashTab);
- pDir->paHashTab = NULL;
+ kHlpFree(pDir->papHashTab);
+ pDir->papHashTab = NULL;
break;
}
@@ -3457,6 +3891,12 @@ KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
#endif
pCache->cObjects--;
+ if (pObj->pNameAlloc)
+ {
+ pCache->cbObjects -= pObj->pNameAlloc->cb;
+ kHlpFree(pObj->pNameAlloc);
+ }
+
kHlpFree(pObj);
return 0;
}
@@ -3469,6 +3909,7 @@ KU32 kFsCacheObjDestroy(PKFSCACHE pCache, PKFSOBJ pObj)
* @param pCache The cache.
* @param pObj The object.
*/
+#undef kFsCacheObjRelease
KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
{
if (pObj)
@@ -3480,7 +3921,32 @@ KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj)
cRefs = --pObj->cRefs;
if (cRefs)
return cRefs;
- return kFsCacheObjDestroy(pCache, pObj);
+ return kFsCacheObjDestroy(pCache, pObj, "kFsCacheObjRelease");
+ }
+ return 0;
+}
+
+
+/**
+ * Debug version of kFsCacheObjRelease
+ *
+ * @returns New reference count.
+ * @param pCache The cache.
+ * @param pObj The object.
+ * @param pszWhere Where it's invoked from.
+ */
+KU32 kFsCacheObjReleaseTagged(PKFSCACHE pCache, PKFSOBJ pObj, const char *pszWhere)
+{
+ if (pObj)
+ {
+ KU32 cRefs;
+ kHlpAssert(pCache->u32Magic == KFSCACHE_MAGIC);
+ kHlpAssert(pObj->u32Magic == KFSOBJ_MAGIC);
+
+ cRefs = --pObj->cRefs;
+ if (cRefs)
+ return cRefs;
+ return kFsCacheObjDestroy(pCache, pObj, pszWhere);
}
return 0;
}
@@ -3983,6 +4449,64 @@ KBOOL kFsCacheSetupCustomRevisionForTree(PKFSCACHE pCache, PKFSOBJ pRoot)
}
+/**
+ * Invalidates a deleted directory, ANSI version.
+ *
+ * @returns K_TRUE if found and is a non-root directory. Otherwise K_FALSE.
+ * @param pCache The cache.
+ * @param pszDir The directory.
+ */
+KBOOL kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache, const char *pszDir)
+{
+ KU32 cchDir = (KU32)kHlpStrLen(pszDir);
+ KFSLOOKUPERROR enmError;
+ PKFSOBJ pFsObj;
+
+ /* Is absolute without any '..' bits? */
+ if ( cchDir >= 3
+ && ( ( pszDir[1] == ':' /* Drive letter */
+ && IS_SLASH(pszDir[2])
+ && IS_ALPHA(pszDir[0]) )
+ || ( IS_SLASH(pszDir[0]) /* UNC */
+ && IS_SLASH(pszDir[1]) ) )
+ && !kFsCacheHasDotDotA(pszDir, cchDir) )
+ pFsObj = kFsCacheLookupAbsoluteA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH,
+ &enmError, NULL);
+ else
+ pFsObj = kFsCacheLookupSlowA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH,
+ &enmError, NULL);
+ if (pFsObj)
+ {
+ /* Is directory? */
+ if (pFsObj->bObjType == KFSOBJ_TYPE_DIR)
+ {
+ if (pFsObj->pParent != &pCache->RootDir)
+ {
+ PKFSDIR pDir = (PKFSDIR)pFsObj;
+ KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: %s hDir=%p\n", pszDir, pDir->hDir));
+ if (pDir->hDir != INVALID_HANDLE_VALUE)
+ {
+ g_pfnNtClose(pDir->hDir);
+ pDir->hDir = INVALID_HANDLE_VALUE;
+ }
+ pDir->fNeedRePopulating = K_TRUE;
+ pDir->Obj.uCacheGen = pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN] - 1;
+ kFsCacheObjRetainInternal(&pDir->Obj);
+ return K_TRUE;
+ }
+ KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a root directory was deleted! %s\n", pszDir));
+ }
+ else
+ KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a non-directory: bObjType=%d %s\n",
+ pFsObj->bObjType, pszDir));
+ kFsCacheObjRetainInternal(pFsObj);
+ }
+ else
+ KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: '%s' was not found\n", pszDir));
+ return K_FALSE;
+}
+
+
PKFSCACHE kFsCacheCreate(KU32 fFlags)
{
PKFSCACHE pCache;
@@ -4021,29 +4545,37 @@ PKFSCACHE kFsCacheCreate(KU32 fFlags)
pCache->RootDir.cChildrenAllocated = 0;
pCache->RootDir.papChildren = NULL;
pCache->RootDir.hDir = INVALID_HANDLE_VALUE;
- pCache->RootDir.cHashTab = 251;
- pCache->RootDir.paHashTab = (PKFSOBJHASH)kHlpAllocZ( pCache->RootDir.cHashTab
- * sizeof(pCache->RootDir.paHashTab[0]));
- if (pCache->RootDir.paHashTab)
+ pCache->RootDir.fHashTabMask = 255; /* 256: 26 drive letters and 102 UNCs before we're half ways. */
+ pCache->RootDir.papHashTab = (PKFSOBJ *)kHlpAllocZ(256 * sizeof(pCache->RootDir.papHashTab[0]));
+ if (pCache->RootDir.papHashTab)
{
/* The cache itself. */
- pCache->u32Magic = KFSCACHE_MAGIC;
- pCache->fFlags = fFlags;
+ pCache->u32Magic = KFSCACHE_MAGIC;
+ pCache->fFlags = fFlags;
pCache->auGenerations[0] = KU32_MAX / 4;
pCache->auGenerations[1] = KU32_MAX / 32;
pCache->auGenerationsMissing[0] = KU32_MAX / 256;
pCache->auGenerationsMissing[1] = 1;
- pCache->cObjects = 1;
- pCache->cbObjects = sizeof(pCache->RootDir) + pCache->RootDir.cHashTab * sizeof(pCache->RootDir.paHashTab[0]);
- pCache->cPathHashHits = 0;
- pCache->cWalkHits = 0;
- pCache->cAnsiPaths = 0;
- pCache->cAnsiPathCollisions = 0;
- pCache->cbAnsiPaths = 0;
+ pCache->cObjects = 1;
+ pCache->cbObjects = sizeof(pCache->RootDir)
+ + (pCache->RootDir.fHashTabMask + 1) * sizeof(pCache->RootDir.papHashTab[0]);
+ pCache->cPathHashHits = 0;
+ pCache->cWalkHits = 0;
+ pCache->cChildSearches = 0;
+ pCache->cChildHashHits = 0;
+ pCache->cChildHashed = 0;
+ pCache->cChildHashTabs = 1;
+ pCache->cChildHashEntriesTotal = pCache->RootDir.fHashTabMask + 1;
+ pCache->cChildHashCollisions = 0;
+ pCache->cNameChanges = 0;
+ pCache->cNameGrowths = 0;
+ pCache->cAnsiPaths = 0;
+ pCache->cAnsiPathCollisions = 0;
+ pCache->cbAnsiPaths = 0;
#ifdef KFSCACHE_CFG_UTF16
- pCache->cUtf16Paths = 0;
- pCache->cUtf16PathCollisions = 0;
- pCache->cbUtf16Paths = 0;
+ pCache->cUtf16Paths = 0;
+ pCache->cUtf16PathCollisions = 0;
+ pCache->cbUtf16Paths = 0;
#endif
return pCache;
}
diff --git a/src/lib/nt/kFsCache.h b/src/lib/nt/kFsCache.h
index 2f306d7..e2d1bbe 100644
--- a/src/lib/nt/kFsCache.h
+++ b/src/lib/nt/kFsCache.h
@@ -1,4 +1,4 @@
-/* $Id: kFsCache.h 2868 2016-09-04 01:28:12Z bird $ */
+/* $Id: kFsCache.h 2967 2016-09-26 18:14:13Z bird $ */
/** @file
* kFsCache.c - NT directory content cache.
*/
@@ -47,7 +47,7 @@
#define KFSCACHE_CFG_SHORT_NAMES 1
/** @def KFSCACHE_CFG_PATH_HASH_TAB_SIZE
* Size of the path hash table. */
-#define KFSCACHE_CFG_PATH_HASH_TAB_SIZE 16381
+#define KFSCACHE_CFG_PATH_HASH_TAB_SIZE 99991
/** The max length paths we consider. */
#define KFSCACHE_CFG_MAX_PATH 1024
/** The max ANSI name length. */
@@ -111,20 +111,6 @@ typedef struct KFSDIR *PKFSDIR;
typedef struct KFSOBJHASH *PKFSOBJHASH;
-/**
- * Directory hash table entry.
- *
- * There can be two of these per directory entry when the short name differs
- * from the long name.
- */
-typedef struct KFSOBJHASH
-{
- /** Pointer to the next entry with the same hash. */
- PKFSOBJHASH pNext;
- /** Pointer to the object. */
- PKFSOBJ pObj;
-} KFSOBJHASH;
-
/** Pointer to a user data item. */
typedef struct KFSUSERDATA *PKFSUSERDATA;
@@ -143,6 +129,21 @@ typedef struct KFSUSERDATA
/**
+ * Storage for name strings for the unlikely event that they should grow in
+ * length after the KFSOBJ was created.
+ */
+typedef struct KFSOBJNAMEALLOC
+{
+ /** Size of the allocation. */
+ KU32 cb;
+ /** The space for names. */
+ char abSpace[1];
+} KFSOBJNAMEALLOC;
+/** Name growth allocation. */
+typedef KFSOBJNAMEALLOC *PKFSOBJNAMEALLOC;
+
+
+/**
* Base cache node.
*/
typedef struct KFSOBJ
@@ -162,6 +163,17 @@ typedef struct KFSOBJ
/** Flags, KFSOBJ_F_XXX. */
KU32 fFlags;
+ /** Hash value of the name inserted into the parent hash table.
+ * This is 0 if not inserted. Names are only hashed and inserted as they are
+ * first found thru linear searching of its siblings, and which name it is
+ * dependens on the lookup function (W or A) and whether the normal name or
+ * short name seems to have matched.
+ *
+ * @note It was ruled out as too much work to hash and track all four names,
+ * so instead this minimalist approach was choosen in stead. */
+ KU32 uNameHash;
+ /** Pointer to the next child with the same name hash value. */
+ PKFSOBJ pNextNameHash;
/** Pointer to the parent (directory).
* This is only NULL for a root. */
PKFSDIR pParent;
@@ -204,6 +216,9 @@ typedef struct KFSOBJ
# endif
#endif
+ /** Allocation for handling name length increases. */
+ PKFSOBJNAMEALLOC pNameAlloc;
+
/** Pointer to the first user data item */
PKFSUSERDATA pUserDataHead;
@@ -230,13 +245,16 @@ typedef struct KFSDIR
/** The allocated size of papChildren. */
KU32 cChildrenAllocated;
- /** Pointer to the hash table.
- * @todo this isn't quite there yet, structure wise. sigh. */
- PKFSOBJHASH paHashTab;
- /** The size of the hash table.
- * @remarks The hash table is optional and only used when there are a lot of
- * entries in the directory. */
- KU32 cHashTab;
+ /** Pointer to the child hash table. */
+ PKFSOBJ *papHashTab;
+ /** The mask shift of the hash table.
+ * Hash table size is a power of two, this is the size minus one.
+ *
+ * @remarks The hash table is optional and populated by lookup hits. The
+ * assumption being that a lookup is repeated and will choose a good
+ * name to hash on. We've got up to 4 different hashes, so this
+ * was the easy way out. */
+ KU32 fHashTabMask;
/** Handle to the directory (we generally keep it open). */
#ifndef DECLARE_HANDLE
@@ -400,6 +418,23 @@ typedef struct KFSCACHE
KSIZE cPathHashHits;
/** Number of hits walking the file system hierarchy. */
KSIZE cWalkHits;
+ /** Number of child searches. */
+ KSIZE cChildSearches;
+ /** Number of cChildLookups resolved thru hash table hits. */
+ KSIZE cChildHashHits;
+ /** The number of child hash tables. */
+ KSIZE cChildHashTabs;
+ /** The sum of all child hash table sizes. */
+ KSIZE cChildHashEntriesTotal;
+ /** Number of children inserted into the hash tables. */
+ KSIZE cChildHashed;
+ /** Number of collisions in the child hash tables. */
+ KSIZE cChildHashCollisions;
+ /** Number times a object name changed. */
+ KSIZE cNameChanges;
+ /** Number times a object name grew and needed KFSOBJNAMEALLOC.
+ * (Subset of cNameChanges) */
+ KSIZE cNameGrowths;
/** The root directory. */
KFSDIR RootDir;
@@ -456,17 +491,29 @@ PKFSOBJ kFsCacheCreateObjectW(PKFSCACHE pCache, PKFSDIR pParent, wchar_t con
KU8 bObjType, KFSLOOKUPERROR *penmError);
PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError);
PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError);
-PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath,
+PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, KU32 fFlags,
KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor);
-PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath,
+PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor);
PKFSOBJ kFsCacheLookupWithLengthA(PKFSCACHE pCache, const char *pchPath, KSIZE cchPath, KFSLOOKUPERROR *penmError);
PKFSOBJ kFsCacheLookupWithLengthW(PKFSCACHE pCache, const wchar_t *pwcPath, KSIZE cwcPath, KFSLOOKUPERROR *penmError);
PKFSOBJ kFsCacheLookupNoMissingA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError);
PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError);
+/** @name KFSCACHE_LOOKUP_F_XXX - lookup flags
+ * @{ */
+/** No inserting new cache entries.
+ * This effectively prevent directories from being repopulated too. */
+#define KFSCACHE_LOOKUP_F_NO_INSERT KU32_C(1)
+/** No refreshing cache entries. */
+#define KFSCACHE_LOOKUP_F_NO_REFRESH KU32_C(2)
+/** @} */
KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj);
+KU32 kFsCacheObjReleaseTagged(PKFSCACHE pCache, PKFSOBJ pObj, const char *pszWhere);
+#ifndef NDEBUG /* enable to debug object release. */
+# define kFsCacheObjRelease(a_pCache, a_pObj) kFsCacheObjReleaseTagged(a_pCache, a_pObj, __FUNCTION__)
+#endif
KU32 kFsCacheObjRetain(PKFSOBJ pObj);
PKFSUSERDATA kFsCacheObjAddUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey, KSIZE cbUserData);
PKFSUSERDATA kFsCacheObjGetUserData(PKFSCACHE pCache, PKFSOBJ pObj, KUPTR uKey);
@@ -484,5 +531,6 @@ void kFsCacheInvalidateAll(PKFSCACHE pCache);
void kFsCacheInvalidateCustomMissing(PKFSCACHE pCache);
void kFsCacheInvalidateCustomBoth(PKFSCACHE pCache);
KBOOL kFsCacheSetupCustomRevisionForTree(PKFSCACHE pCache, PKFSOBJ pRoot);
+KBOOL kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache, const char *pszDir);
#endif
diff --git a/src/lib/nt/ntdir.c b/src/lib/nt/ntdir.c
index a80e53a..3ea166e 100644
--- a/src/lib/nt/ntdir.c
+++ b/src/lib/nt/ntdir.c
@@ -1,10 +1,10 @@
-/* $Id: ntdir.c 2708 2013-11-21 10:26:40Z bird $ */
+/* $Id: ntdir.c 2985 2016-11-01 18:26:35Z bird $ */
/** @file
* MSC + NT opendir, readdir, telldir, seekdir, and closedir.
*/
/*
- * Copyright (c) 2005-2013 knut st. osmundsen <bird-kBuild-spamx at anduin.net>
+ * Copyright (c) 2005-2016 knut st. osmundsen <bird-kBuild-spamx at anduin.net>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -35,6 +35,7 @@
#include <stdio.h>
#include <errno.h>
#include <malloc.h>
+#include <assert.h>
#include "ntstuff.h"
#include "nthlp.h"
@@ -42,61 +43,103 @@
/**
- * Internal worker for birdStatModTimeOnly.
+ * Implements opendir.
*/
-static BirdDir_T *birdDirOpenInternal(const char *pszPath, const char *pszFilter, int fMinimalInfo)
+BirdDir_T *birdDirOpen(const char *pszPath)
{
- HANDLE hFile = birdOpenFile(pszPath,
- FILE_READ_DATA | SYNCHRONIZE,
- 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,
- OBJ_CASE_INSENSITIVE);
- if (hFile != INVALID_HANDLE_VALUE)
+ HANDLE hDir = birdOpenFile(pszPath,
+ FILE_READ_DATA | SYNCHRONIZE,
+ 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,
+ OBJ_CASE_INSENSITIVE);
+ if (hDir != INVALID_HANDLE_VALUE)
{
- /*
- * Allocate a handle.
- */
- BirdDir_T *pDir = (BirdDir_T *)birdMemAlloc(sizeof(*pDir));
+ BirdDir_T *pDir = birdDirOpenFromHandle((void *)hDir, NULL, BIRDDIR_F_CLOSE_HANDLE);
if (pDir)
- {
- pDir->uMagic = BIRD_DIR_MAGIC;
- pDir->pvHandle = (void *)hFile;
- pDir->uDev = 0;
- pDir->offPos = 0;
- pDir->fHaveData = 0;
- pDir->fFirst = 1;
- pDir->iInfoClass = fMinimalInfo ? MyFileNamesInformation : MyFileIdFullDirectoryInformation;
- pDir->offBuf = 0;
- pDir->cbBuf = 0;
- pDir->pabBuf = NULL;
return pDir;
- }
-
- birdCloseFile(hFile);
- birdSetErrnoToNoMem();
+ birdCloseFile(hDir);
}
-
return NULL;
}
/**
- * Implements opendir.
+ * Alternative opendir, with extra stat() info returned by readdir().
*/
-BirdDir_T *birdDirOpen(const char *pszPath)
+BirdDir_T *birdDirOpenExtraInfo(const char *pszPath)
{
- return birdDirOpenInternal(pszPath, NULL, 1 /*fMinimalInfo*/);
+ HANDLE hDir = birdOpenFile(pszPath,
+ FILE_READ_DATA | SYNCHRONIZE,
+ 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,
+ OBJ_CASE_INSENSITIVE);
+ if (hDir != INVALID_HANDLE_VALUE)
+ {
+ BirdDir_T *pDir = birdDirOpenFromHandle((void *)hDir, NULL, BIRDDIR_F_CLOSE_HANDLE | BIRDDIR_F_EXTRA_INFO);
+ if (pDir)
+ return pDir;
+ birdCloseFile(hDir);
+ }
+ return NULL;
+}
+
+
+BirdDir_T *birdDirOpenExW(void *hRoot, const wchar_t *pwszPath, const wchar_t *pwszFilter, unsigned fFlags)
+{
+ HANDLE hDir = birdOpenFileExW((HANDLE)hRoot,
+ pwszPath,
+ FILE_READ_DATA | SYNCHRONIZE,
+ 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,
+ OBJ_CASE_INSENSITIVE);
+ if (hDir != INVALID_HANDLE_VALUE)
+ {
+ BirdDir_T *pDir = birdDirOpenFromHandle((void *)hDir, pwszFilter, fFlags | BIRDDIR_F_CLOSE_HANDLE);
+ if (pDir)
+ return pDir;
+ birdCloseFile(hDir);
+ }
+ return NULL;
}
/**
- * Alternative opendir, with extra stat() info returned by readdir().
+ * Internal worker for birdStatModTimeOnly.
*/
-BirdDir_T *birdDirOpenExtraInfo(const char *pszPath)
+BirdDir_T *birdDirOpenFromHandle(void *pvHandle, const void *pvReserved, unsigned fFlags)
{
- return birdDirOpenInternal(pszPath, NULL, 0 /*fMinimalInfo*/);
+ if (!pvReserved)
+ {
+ /*
+ * Allocate and initialize the directory enum handle.
+ */
+ BirdDir_T *pDir = (BirdDir_T *)birdMemAlloc(sizeof(*pDir));
+ if (pDir)
+ {
+ pDir->uMagic = BIRD_DIR_MAGIC;
+ pDir->fFlags = fFlags;
+ pDir->pvHandle = pvHandle;
+ pDir->uDev = 0;
+ pDir->offPos = 0;
+ pDir->fHaveData = 0;
+ pDir->fFirst = 1;
+ pDir->iInfoClass = fFlags & BIRDDIR_F_EXTRA_INFO ? MyFileIdFullDirectoryInformation : MyFileNamesInformation;
+ pDir->offBuf = 0;
+ pDir->cbBuf = 0;
+ pDir->pabBuf = NULL;
+ return pDir;
+ }
+ }
+ else
+ assert(pvReserved == NULL);
+ birdSetErrnoToNoMem();
+ return NULL;
}
@@ -157,7 +200,7 @@ static int birdDirReadMore(BirdDir_T *pDir)
(MY_FILE_INFORMATION_CLASS)pDir->iInfoClass,
FALSE, /* fReturnSingleEntry */
NULL, /* Filter / restart pos. */
- FALSE); /* fRestartScan */
+ pDir->fFlags & BIRDDIR_F_RESTART_SCAN ? TRUE : FALSE); /* fRestartScan */
if (!MY_NT_SUCCESS(rcNt))
{
int rc;
@@ -172,6 +215,7 @@ static int birdDirReadMore(BirdDir_T *pDir)
pDir->offBuf = 0;
pDir->fHaveData = 1;
+ pDir->fFlags &= ~BIRDDIR_F_RESTART_SCAN;
return 0;
}
@@ -354,7 +398,8 @@ int birdDirClose(BirdDir_T *pDir)
return birdSetErrnoToBadFileNo();
pDir->uMagic++;
- birdCloseFile((HANDLE)pDir->pvHandle);
+ if (pDir->fFlags & BIRDDIR_F_CLOSE_HANDLE)
+ birdCloseFile((HANDLE)pDir->pvHandle);
pDir->pvHandle = (void *)INVALID_HANDLE_VALUE;
birdMemFree(pDir->pabBuf);
pDir->pabBuf = NULL;
diff --git a/src/lib/nt/ntdir.h b/src/lib/nt/ntdir.h
index f84cf51..dd8eee2 100644
--- a/src/lib/nt/ntdir.h
+++ b/src/lib/nt/ntdir.h
@@ -1,4 +1,4 @@
-/* $Id: ntdir.h 2708 2013-11-21 10:26:40Z bird $ */
+/* $Id: ntdir.h 2985 2016-11-01 18:26:35Z bird $ */
/** @file
* MSC + NT opendir, readdir, closedir and friends.
*/
@@ -64,10 +64,24 @@ typedef struct dirent
#define DT_WHT 14
/** @} */
+/** @name BIRDDIR_F_XXX - birdDirOpenFromHandle & BirdDir_T::fFlags
+ * @{ */
+/** birdDirClose should also close pvHandle. */
+#define BIRDDIR_F_CLOSE_HANDLE 1U
+/** birdDirClose should not close the handle. */
+#define BIRDDIR_F_KEEP_HANDLE 0U
+/** Provide extra info (stat). */
+#define BIRDDIR_F_EXTRA_INFO 2U
+/** Whether to restart the scan. */
+#define BIRDDIR_F_RESTART_SCAN 4U
+/** @} */
+
typedef struct BirdDir
{
/** Magic value. */
unsigned uMagic;
+ /** Flags. */
+ unsigned fFlags;
/** The directory handle. */
void *pvHandle;
/** The device number (st_dev). */
@@ -97,6 +111,8 @@ typedef struct BirdDir
BirdDir_T *birdDirOpen(const char *pszPath);
BirdDir_T *birdDirOpenExtraInfo(const char *pszPath);
+BirdDir_T *birdDirOpenExW(void *hRoot, const wchar_t *pwszPath, const wchar_t *pwszFilter, unsigned fFlags);
+BirdDir_T *birdDirOpenFromHandle(void *hDir, const void *pvReserved, unsigned fFlags);
BirdDirEntry_T *birdDirRead(BirdDir_T *pDir);
long birdDirTell(BirdDir_T *pDir);
void birdDirSeek(BirdDir_T *pDir, long offDir);
diff --git a/src/lib/nt/nthlp.h b/src/lib/nt/nthlp.h
index 467daaf..4199cc2 100644
--- a/src/lib/nt/nthlp.h
+++ b/src/lib/nt/nthlp.h
@@ -1,4 +1,4 @@
-/* $Id: nthlp.h 2862 2016-09-02 02:39:56Z bird $ */
+/* $Id: nthlp.h 2997 2016-11-01 23:28:02Z bird $ */
/** @file
* MSC + NT helper functions.
*/
@@ -54,16 +54,27 @@ int birdSetErrnoToInvalidArg(void);
int birdSetErrnoToBadFileNo(void);
-HANDLE birdOpenFile(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess,
- ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs);
-HANDLE birdOpenParentDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess,
- ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
+HANDLE birdOpenFile(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs);
+HANDLE birdOpenFileW(const wchar_t *pwszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs);
+HANDLE birdOpenFileEx(HANDLE hRoot, const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs);
+HANDLE birdOpenFileExW(HANDLE hRoot, const wchar_t *pwszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs);
+HANDLE birdOpenParentDir(HANDLE hRoot, const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
MY_UNICODE_STRING *pNameUniStr);
-MY_NTSTATUS birdOpenFileUniStr(MY_UNICODE_STRING *pNtPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+HANDLE birdOpenParentDirW(HANDLE hRoot, const wchar_t *pwszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
+ MY_UNICODE_STRING *pNameUniStr);
+MY_NTSTATUS birdOpenFileUniStr(HANDLE hRoot, MY_UNICODE_STRING *pNtPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
HANDLE *phFile);
+HANDLE birdOpenCurrentDirectory(void);
void birdCloseFile(HANDLE hFile);
int birdDosToNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath);
+int birdDosToRelativeNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath);
void birdFreeNtPath(MY_UNICODE_STRING *pNtPath);
diff --git a/src/lib/nt/nthlpcore.c b/src/lib/nt/nthlpcore.c
index a6e0d7e..b4d77eb 100644
--- a/src/lib/nt/nthlpcore.c
+++ b/src/lib/nt/nthlpcore.c
@@ -1,4 +1,4 @@
-/* $Id: nthlpcore.c 2862 2016-09-02 02:39:56Z bird $ */
+/* $Id: nthlpcore.c 2985 2016-11-01 18:26:35Z bird $ */
/** @file
* MSC + NT core helpers functions and globals.
*/
@@ -45,6 +45,8 @@ MY_NTSTATUS (WINAPI *g_pfnNtClose)(HANDLE);
MY_NTSTATUS (WINAPI *g_pfnNtCreateFile)(PHANDLE, MY_ACCESS_MASK, MY_OBJECT_ATTRIBUTES *, MY_IO_STATUS_BLOCK *,
PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG);
MY_NTSTATUS (WINAPI *g_pfnNtDeleteFile)(MY_OBJECT_ATTRIBUTES *);
+MY_NTSTATUS (WINAPI *g_pfnNtDuplicateObject)(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, HANDLE *phRet,
+ MY_ACCESS_MASK fDesiredAccess, ULONG fAttribs, ULONG fOptions);
MY_NTSTATUS (WINAPI *g_pfnNtReadFile)(HANDLE hFile, HANDLE hEvent, MY_IO_APC_ROUTINE *pfnApc, PVOID pvApcCtx,
MY_IO_STATUS_BLOCK *, PVOID pvBuf, ULONG cbToRead, PLARGE_INTEGER poffFile,
PULONG puKey);
@@ -61,6 +63,9 @@ MY_NTSTATUS (WINAPI *g_pfnRtlAnsiStringToUnicodeString)(MY_UNICODE_STRING *, MY_
BOOLEAN (WINAPI *g_pfnRtlEqualUnicodeString)(MY_UNICODE_STRING const *, MY_UNICODE_STRING const *, BOOLEAN);
BOOLEAN (WINAPI *g_pfnRtlEqualString)(MY_ANSI_STRING const *, MY_ANSI_STRING const *, BOOLEAN);
UCHAR (WINAPI *g_pfnRtlUpperChar)(UCHAR uch);
+ULONG (WINAPI *g_pfnRtlNtStatusToDosError)(MY_NTSTATUS rcNt);
+VOID (WINAPI *g_pfnRtlAcquirePebLock)(VOID);
+VOID (WINAPI *g_pfnRtlReleasePebLock)(VOID);
static struct
@@ -72,6 +77,7 @@ static struct
{ (FARPROC *)&g_pfnNtClose, "NtClose" },
{ (FARPROC *)&g_pfnNtCreateFile, "NtCreateFile" },
{ (FARPROC *)&g_pfnNtDeleteFile, "NtDeleteFile" },
+ { (FARPROC *)&g_pfnNtDuplicateObject, "NtDuplicateObject" },
{ (FARPROC *)&g_pfnNtReadFile, "NtReadFile" },
{ (FARPROC *)&g_pfnNtQueryInformationFile, "NtQueryInformationFile" },
{ (FARPROC *)&g_pfnNtQueryVolumeInformationFile, "NtQueryVolumeInformationFile" },
@@ -84,6 +90,9 @@ static struct
{ (FARPROC *)&g_pfnRtlEqualUnicodeString, "RtlEqualUnicodeString" },
{ (FARPROC *)&g_pfnRtlEqualString, "RtlEqualString" },
{ (FARPROC *)&g_pfnRtlUpperChar, "RtlUpperChar" },
+ { (FARPROC *)&g_pfnRtlNtStatusToDosError, "RtlNtStatusToDosError" },
+ { (FARPROC *)&g_pfnRtlAcquirePebLock, "RtlAcquirePebLock" },
+ { (FARPROC *)&g_pfnRtlReleasePebLock, "RtlReleasePebLock" },
};
/** Set to 1 if we've successfully resolved the imports, otherwise 0. */
int g_fResolvedNtImports = 0;
@@ -155,7 +164,6 @@ int birdErrnoFromNtStatus(MY_NTSTATUS rcNt)
{
/* EPERM = 1 */
case STATUS_CANNOT_DELETE:
- case STATUS_DELETE_PENDING:
return EPERM;
/* ENOENT = 2 */
case STATUS_NOT_FOUND:
@@ -172,6 +180,7 @@ int birdErrnoFromNtStatus(MY_NTSTATUS rcNt)
case STATUS_BAD_NETWORK_PATH:
case STATUS_DFS_EXIT_PATH_FOUND:
case RPC_NT_OBJECT_NOT_FOUND:
+ case STATUS_DELETE_PENDING:
return ENOENT;
/* ESRCH = 3 */
case STATUS_PROCESS_NOT_IN_JOB:
@@ -377,6 +386,13 @@ int birdErrnoFromNtStatus(MY_NTSTATUS rcNt)
int birdSetErrnoFromNt(MY_NTSTATUS rcNt)
{
errno = birdErrnoFromNtStatus(rcNt);
+#if 0
+ {
+ ULONG rcWin32;
+ _doserrno = rcWin32 = g_pfnRtlNtStatusToDosError(rcNt);
+ SetLastError(rcWin32);
+ }
+#endif
return -1;
}
diff --git a/src/lib/nt/nthlpfs.c b/src/lib/nt/nthlpfs.c
index d2e7f45..c51fe07 100644
--- a/src/lib/nt/nthlpfs.c
+++ b/src/lib/nt/nthlpfs.c
@@ -1,4 +1,4 @@
-/* $Id: nthlpfs.c 2713 2013-11-21 21:11:00Z bird $ */
+/* $Id: nthlpfs.c 2997 2016-11-01 23:28:02Z bird $ */
/** @file
* MSC + NT helpers for file system related functions.
*/
@@ -33,6 +33,10 @@
* Header Files *
*******************************************************************************/
#include "nthlp.h"
+#include <stddef.h>
+#include <string.h>
+#include <wchar.h>
+#include <errno.h>
/*******************************************************************************
@@ -60,6 +64,24 @@ static int birdHasTrailingSlash(const char *pszPath)
}
+static int birdHasTrailingSlashW(const wchar_t *pwszPath)
+{
+ wchar_t wc, wc2;
+
+ /* Skip leading slashes. */
+ while ((wc = *pwszPath) == '/' || wc == '\\')
+ pwszPath++;
+ if (wc == '\0')
+ return 0;
+
+ /* Find the last char. */
+ while ((wc2 = *++pwszPath) != '\0')
+ wc = wc2;
+
+ return wc == '/' || wc == '\\' || wc == ':';
+}
+
+
static int birdIsPathDirSpec(const char *pszPath)
{
char ch, ch2;
@@ -77,6 +99,23 @@ static int birdIsPathDirSpec(const char *pszPath)
}
+static int birdIsPathDirSpecW(const wchar_t *pwszPath)
+{
+ wchar_t wc, wc2;
+
+ /* Check for empty string. */
+ wc = *pwszPath;
+ if (wc == '\0')
+ return 0;
+
+ /* Find the last char. */
+ while ((wc2 = *++pwszPath) != '\0')
+ wc = wc2;
+
+ return wc == '/' || wc == '\\' || wc == ':';
+}
+
+
int birdDosToNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath)
{
MY_NTSTATUS rcNt;
@@ -118,6 +157,129 @@ int birdDosToNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath)
}
+int birdDosToNtPathW(const wchar_t *pwszPath, MY_UNICODE_STRING *pNtPath)
+{
+ birdResolveImports();
+
+ pNtPath->Length = pNtPath->MaximumLength = 0;
+ pNtPath->Buffer = NULL;
+
+ /*
+ * Convert the wide DOS path to an NT path.
+ */
+ if (g_pfnRtlDosPathNameToNtPathName_U(pwszPath, pNtPath, NULL, FALSE))
+ return 0;
+ return birdSetErrnoFromNt(STATUS_NO_MEMORY);
+}
+
+
+/**
+ * Converts UNIX slashes to DOS ones.
+ *
+ * @returns 0
+ * @param pNtPath The relative NT path to fix up.
+ */
+static int birdFixRelativeNtPathSlashesAndReturn0(MY_UNICODE_STRING *pNtPath)
+{
+ size_t cwcLeft = pNtPath->Length / sizeof(wchar_t);
+ wchar_t *pwcStart = pNtPath->Buffer;
+ wchar_t *pwcHit;
+
+ /* Convert slashes. */
+ while ((pwcHit = wmemchr(pwcStart, '/', cwcLeft)) != NULL)
+ {
+ *pwcHit = '\\';
+ cwcLeft -= pwcHit - pwcStart;
+ pwcHit = pwcStart;
+ }
+
+#if 0
+ /* Strip trailing slashes (NT doesn't like them). */
+ while ( pNtPath->Length >= sizeof(wchar_t)
+ && pNtPath->Buffer[(pNtPath->Length - sizeof(wchar_t)) / sizeof(wchar_t)] == '\\')
+ {
+ pNtPath->Length -= sizeof(wchar_t);
+ pNtPath->Buffer[pNtPath->Length / sizeof(wchar_t)] = '\0';
+ }
+
+ /* If it was all trailing slashes we convert it to a dot path. */
+ if ( pNtPath->Length == 0
+ && pNtPath->MaximumLength >= sizeof(wchar_t) * 2)
+ {
+ pNtPath->Length = sizeof(wchar_t);
+ pNtPath->Buffer[0] = '.';
+ pNtPath->Buffer[1] = '\0';
+ }
+#endif
+
+ return 0;
+}
+
+
+/**
+ * Similar to birdDosToNtPath, but it does call RtlDosPathNameToNtPathName_U.
+ *
+ * @returns 0 on success, -1 + errno on failure.
+ * @param pszPath The relative path.
+ * @param pNtPath Where to return the NT path. Call birdFreeNtPath when done.
+ */
+int birdDosToRelativeNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath)
+{
+ MY_NTSTATUS rcNt;
+ MY_ANSI_STRING Src;
+
+ birdResolveImports();
+
+ /*
+ * Just convert to wide char.
+ */
+ pNtPath->Length = pNtPath->MaximumLength = 0;
+ pNtPath->Buffer = NULL;
+
+ Src.Buffer = (PCHAR)pszPath;
+ Src.MaximumLength = Src.Length = (USHORT)strlen(pszPath);
+
+ rcNt = g_pfnRtlAnsiStringToUnicodeString(pNtPath, &Src, TRUE /* Allocate */);
+ if (MY_NT_SUCCESS(rcNt))
+ return birdFixRelativeNtPathSlashesAndReturn0(pNtPath);
+ return birdSetErrnoFromNt(rcNt);
+}
+
+
+/**
+ * Similar to birdDosToNtPathW, but it does call RtlDosPathNameToNtPathName_U.
+ *
+ * @returns 0 on success, -1 + errno on failure.
+ * @param pwszPath The relative path.
+ * @param pNtPath Where to return the NT path. Call birdFreeNtPath when done.
+ */
+int birdDosToRelativeNtPathW(const wchar_t *pwszPath, MY_UNICODE_STRING *pNtPath)
+{
+ size_t cwcPath = wcslen(pwszPath);
+ if (cwcPath < 0xfffe)
+ {
+ pNtPath->Length = (USHORT)(cwcPath * sizeof(wchar_t));
+ pNtPath->MaximumLength = pNtPath->Length + sizeof(wchar_t);
+ pNtPath->Buffer = HeapAlloc(GetProcessHeap(), 0, pNtPath->MaximumLength);
+ if (pNtPath->Buffer)
+ {
+ memcpy(pNtPath->Buffer, pwszPath, pNtPath->MaximumLength);
+ return birdFixRelativeNtPathSlashesAndReturn0(pNtPath);
+ }
+ errno = ENOMEM;
+ }
+ else
+ errno = ENAMETOOLONG;
+ return -1;
+}
+
+
+/**
+ * Frees a string returned by birdDosToNtPath, birdDosToNtPathW or
+ * birdDosToRelativeNtPath.
+ *
+ * @param pNtPath The the NT path to free.
+ */
void birdFreeNtPath(MY_UNICODE_STRING *pNtPath)
{
HeapFree(GetProcessHeap(), 0, pNtPath->Buffer);
@@ -127,7 +289,7 @@ void birdFreeNtPath(MY_UNICODE_STRING *pNtPath)
}
-MY_NTSTATUS birdOpenFileUniStr(MY_UNICODE_STRING *pNtPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+MY_NTSTATUS birdOpenFileUniStr(HANDLE hRoot, MY_UNICODE_STRING *pNtPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
HANDLE *phFile)
{
@@ -143,7 +305,7 @@ MY_NTSTATUS birdOpenFileUniStr(MY_UNICODE_STRING *pNtPath, ACCESS_MASK fDesiredA
Ios.Information = -1;
Ios.u.Status = 0;
- MyInitializeObjectAttributes(&ObjAttr, pNtPath, fObjAttribs, NULL /*hRoot*/, NULL /*pSecAttr*/);
+ MyInitializeObjectAttributes(&ObjAttr, pNtPath, fObjAttribs, hRoot, NULL /*pSecAttr*/);
rcNt = g_pfnNtCreateFile(phFile,
fDesiredAccess,
@@ -184,8 +346,8 @@ MY_NTSTATUS birdOpenFileUniStr(MY_UNICODE_STRING *pNtPath, ACCESS_MASK fDesiredA
}
-HANDLE birdOpenFile(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess,
- ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs)
+HANDLE birdOpenFile(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs)
{
MY_UNICODE_STRING NtPath;
MY_NTSTATUS rcNt;
@@ -197,20 +359,46 @@ HANDLE birdOpenFile(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFile
fCreateOptions |= FILE_DIRECTORY_FILE;
/*
- * Call the NT API directly.
+ * Convert the path and call birdOpenFileUniStr to do the real work.
*/
if (birdDosToNtPath(pszPath, &NtPath) == 0)
{
HANDLE hFile;
- rcNt = birdOpenFileUniStr(&NtPath, fDesiredAccess, fFileAttribs, fShareAccess,
+ rcNt = birdOpenFileUniStr(NULL /*hRoot*/, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess,
fCreateDisposition, fCreateOptions, fObjAttribs, &hFile);
+ birdFreeNtPath(&NtPath);
if (MY_NT_SUCCESS(rcNt))
- {
- birdFreeNtPath(&NtPath);
return hFile;
- }
+ birdSetErrnoFromNt(rcNt);
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+
+HANDLE birdOpenFileW(const wchar_t *pwszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs)
+{
+ MY_UNICODE_STRING NtPath;
+ MY_NTSTATUS rcNt;
+
+ /*
+ * Adjust inputs.
+ */
+ if (birdIsPathDirSpecW(pwszPath))
+ fCreateOptions |= FILE_DIRECTORY_FILE;
+
+ /*
+ * Convert the path and call birdOpenFileUniStr to do the real work.
+ */
+ if (birdDosToNtPathW(pwszPath, &NtPath) == 0)
+ {
+ HANDLE hFile;
+ rcNt = birdOpenFileUniStr(NULL /*hRoot*/, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess,
+ fCreateDisposition, fCreateOptions, fObjAttribs, &hFile);
birdFreeNtPath(&NtPath);
+ if (MY_NT_SUCCESS(rcNt))
+ return hFile;
birdSetErrnoFromNt(rcNt);
}
@@ -218,9 +406,8 @@ HANDLE birdOpenFile(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFile
}
-HANDLE birdOpenParentDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess,
- ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
- MY_UNICODE_STRING *pNameUniStr)
+HANDLE birdOpenFileEx(HANDLE hRoot, const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs)
{
MY_UNICODE_STRING NtPath;
MY_NTSTATUS rcNt;
@@ -228,76 +415,219 @@ HANDLE birdOpenParentDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG
/*
* Adjust inputs.
*/
- fCreateOptions |= FILE_DIRECTORY_FILE;
+ if (birdIsPathDirSpec(pszPath))
+ fCreateOptions |= FILE_DIRECTORY_FILE;
/*
- * Convert the path and split off the filename.
+ * Convert the path and call birdOpenFileUniStr to do the real work.
*/
- if (birdDosToNtPath(pszPath, &NtPath) == 0)
+ if (hRoot == INVALID_HANDLE_VALUE)
+ hRoot = NULL;
+ if ((hRoot != NULL ? birdDosToRelativeNtPath(pszPath, &NtPath) : birdDosToNtPath(pszPath, &NtPath)) == 0)
{
- USHORT offName = NtPath.Length / sizeof(WCHAR);
- USHORT cwcName = offName;
- WCHAR wc = 0;
-
- while ( offName > 0
- && (wc = NtPath.Buffer[offName - 1]) != '\\'
- && wc != '/'
- && wc != ':')
- offName--;
- if (offName > 0)
- {
- cwcName -= offName;
+ HANDLE hFile;
+ rcNt = birdOpenFileUniStr(hRoot, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess,
+ fCreateDisposition, fCreateOptions, fObjAttribs, &hFile);
+ birdFreeNtPath(&NtPath);
+ if (MY_NT_SUCCESS(rcNt))
+ return hFile;
+ birdSetErrnoFromNt(rcNt);
+ }
+
+ return INVALID_HANDLE_VALUE;
+}
+
+
+HANDLE birdOpenFileExW(HANDLE hRoot, const wchar_t *pwszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs)
+{
+ MY_UNICODE_STRING NtPath;
+ MY_NTSTATUS rcNt;
+
+ /*
+ * Adjust inputs.
+ */
+ if (birdIsPathDirSpecW(pwszPath))
+ fCreateOptions |= FILE_DIRECTORY_FILE;
+
+ /*
+ * Convert the path (could save ourselves this if pwszPath is perfect) and
+ * call birdOpenFileUniStr to do the real work.
+ */
+ if (hRoot == INVALID_HANDLE_VALUE)
+ hRoot = NULL;
+ if ((hRoot != NULL ? birdDosToRelativeNtPathW(pwszPath, &NtPath) : birdDosToNtPathW(pwszPath, &NtPath)) == 0)
+ {
+ HANDLE hFile;
+ rcNt = birdOpenFileUniStr(hRoot, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess,
+ fCreateDisposition, fCreateOptions, fObjAttribs, &hFile);
+ birdFreeNtPath(&NtPath);
+ if (MY_NT_SUCCESS(rcNt))
+ return hFile;
+ birdSetErrnoFromNt(rcNt);
+ }
+
+ return INVALID_HANDLE_VALUE;
+}
- /* Make a copy of the file name, if requested. */
- rcNt = STATUS_SUCCESS;
- if (pNameUniStr)
+
+static HANDLE birdOpenParentDirCommon(HANDLE hRoot, MY_UNICODE_STRING *pNtPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
+ MY_UNICODE_STRING *pNameUniStr)
+{
+ MY_NTSTATUS rcNt;
+
+ /*
+ * Strip the path down to the directory.
+ */
+ USHORT offName = pNtPath->Length / sizeof(WCHAR);
+ USHORT cwcName = offName;
+ WCHAR wc = 0;
+ while ( offName > 0
+ && (wc = pNtPath->Buffer[offName - 1]) != '\\'
+ && wc != '/'
+ && wc != ':')
+ offName--;
+ if ( offName > 0
+ || (hRoot != NULL && cwcName > 0))
+ {
+ cwcName -= offName;
+
+ /* Make a copy of the file name, if requested. */
+ rcNt = STATUS_SUCCESS;
+ if (pNameUniStr)
+ {
+ pNameUniStr->Length = cwcName * sizeof(WCHAR);
+ pNameUniStr->MaximumLength = pNameUniStr->Length + sizeof(WCHAR);
+ pNameUniStr->Buffer = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, pNameUniStr->MaximumLength);
+ if (pNameUniStr->Buffer)
{
- pNameUniStr->Length = cwcName * sizeof(WCHAR);
- pNameUniStr->MaximumLength = pNameUniStr->Length + sizeof(WCHAR);
- pNameUniStr->Buffer = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, pNameUniStr->MaximumLength);
- if (pNameUniStr->Buffer)
- {
- memcpy(pNameUniStr->Buffer, &NtPath.Buffer[offName],pNameUniStr->Length);
- pNameUniStr->Buffer[cwcName] = '\0';
- }
- else
- rcNt = STATUS_NO_MEMORY;
+ memcpy(pNameUniStr->Buffer, &pNtPath->Buffer[offName], pNameUniStr->Length);
+ pNameUniStr->Buffer[cwcName] = '\0';
}
+ else
+ rcNt = STATUS_NO_MEMORY;
+ }
- /* Chop, chop. */
- // Bad idea, breaks \\?\c:\pagefile.sys. //while ( offName > 0
- // Bad idea, breaks \\?\c:\pagefile.sys. // && ( (wc = NtPath.Buffer[offName - 1]) == '\\'
- // Bad idea, breaks \\?\c:\pagefile.sys. // || wc == '/'))
- // Bad idea, breaks \\?\c:\pagefile.sys. // offName--;
- NtPath.Length = offName * sizeof(WCHAR);
- NtPath.Buffer[offName] = '\0';
+ /* Chop, chop. */
+ // Bad idea, breaks \\?\c:\pagefile.sys. //while ( offName > 0
+ // Bad idea, breaks \\?\c:\pagefile.sys. // && ( (wc = pNtPath->Buffer[offName - 1]) == '\\'
+ // Bad idea, breaks \\?\c:\pagefile.sys. // || wc == '/'))
+ // Bad idea, breaks \\?\c:\pagefile.sys. // offName--;
+ if (offName == 0)
+ pNtPath->Buffer[offName++] = '.'; /* Hack for dir handle + dir entry name. */
+ pNtPath->Length = offName * sizeof(WCHAR);
+ pNtPath->Buffer[offName] = '\0';
+ if (MY_NT_SUCCESS(rcNt))
+ {
+ /*
+ * Finally, try open the directory.
+ */
+ HANDLE hFile;
+ fCreateOptions |= FILE_DIRECTORY_FILE;
+ rcNt = birdOpenFileUniStr(hRoot, pNtPath, fDesiredAccess, fFileAttribs, fShareAccess,
+ fCreateDisposition, fCreateOptions, fObjAttribs, &hFile);
if (MY_NT_SUCCESS(rcNt))
{
- /*
- * Finally, try open the directory.
- */
- HANDLE hFile;
- rcNt = birdOpenFileUniStr(&NtPath, fDesiredAccess, fFileAttribs, fShareAccess,
- fCreateDisposition, fCreateOptions, fObjAttribs, &hFile);
- if (MY_NT_SUCCESS(rcNt))
- {
- birdFreeNtPath(&NtPath);
- return hFile;
- }
+ birdFreeNtPath(pNtPath);
+ return hFile;
}
-
- if (pNameUniStr)
- birdFreeNtPath(pNameUniStr);
}
- birdFreeNtPath(&NtPath);
- birdSetErrnoFromNt(rcNt);
+ if (pNameUniStr)
+ birdFreeNtPath(pNameUniStr);
}
+ else
+ rcNt = STATUS_INVALID_PARAMETER;
+
+ birdFreeNtPath(pNtPath);
+ birdSetErrnoFromNt(rcNt);
+ return INVALID_HANDLE_VALUE;
+}
+
+
+HANDLE birdOpenParentDir(HANDLE hRoot, const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
+ MY_UNICODE_STRING *pNameUniStr)
+{
+ /*
+ * Convert the path and join up with the UTF-16 version (it'll free NtPath).
+ */
+ MY_UNICODE_STRING NtPath;
+ if (hRoot == INVALID_HANDLE_VALUE)
+ hRoot = NULL;
+ if ( hRoot == NULL
+ ? birdDosToNtPath(pszPath, &NtPath) == 0
+ : birdDosToRelativeNtPath(pszPath, &NtPath) == 0)
+ return birdOpenParentDirCommon(hRoot, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess,
+ fCreateDisposition, fCreateOptions, fObjAttribs, pNameUniStr);
+ return INVALID_HANDLE_VALUE;
+}
+
+HANDLE birdOpenParentDirW(HANDLE hRoot, const wchar_t *pwszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs,
+ ULONG fShareAccess, ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
+ MY_UNICODE_STRING *pNameUniStr)
+{
+ /*
+ * Convert the path and join up with the ansi version (it'll free NtPath).
+ */
+ MY_UNICODE_STRING NtPath;
+ if (hRoot == INVALID_HANDLE_VALUE)
+ hRoot = NULL;
+ if ( hRoot == NULL
+ ? birdDosToNtPathW(pwszPath, &NtPath) == 0
+ : birdDosToRelativeNtPathW(pwszPath, &NtPath) == 0)
+ return birdOpenParentDirCommon(hRoot, &NtPath, fDesiredAccess, fFileAttribs, fShareAccess,
+ fCreateDisposition, fCreateOptions, fObjAttribs, pNameUniStr);
return INVALID_HANDLE_VALUE;
}
+/**
+ * Returns a handle to the current working directory of the process.
+ *
+ * @returns CWD handle with FILE_TRAVERSE and SYNCHRONIZE access. May return
+ * INVALID_HANDLE_VALUE w/ errno for invalid CWD.
+ */
+HANDLE birdOpenCurrentDirectory(void)
+{
+ PMY_RTL_USER_PROCESS_PARAMETERS pProcParams;
+ MY_NTSTATUS rcNt;
+ HANDLE hRet = INVALID_HANDLE_VALUE;
+
+ birdResolveImports();
+
+ /*
+ * We'll try get this from the PEB.
+ */
+ g_pfnRtlAcquirePebLock();
+ pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)MY_NT_CURRENT_PEB()->ProcessParameters;
+ if (pProcParams != NULL)
+ rcNt = g_pfnNtDuplicateObject(MY_NT_CURRENT_PROCESS, pProcParams->CurrentDirectory.Handle,
+ MY_NT_CURRENT_PROCESS, &hRet,
+ FILE_TRAVERSE | SYNCHRONIZE,
+ 0 /*fAttribs*/,
+ 0 /*fOptions*/);
+ else
+ rcNt = STATUS_INVALID_PARAMETER;
+ g_pfnRtlReleasePebLock();
+ if (MY_NT_SUCCESS(rcNt))
+ return hRet;
+
+ /*
+ * Fallback goes thru birdOpenFileW.
+ */
+ return birdOpenFileW(L".",
+ FILE_TRAVERSE | SYNCHRONIZE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN,
+ FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ OBJ_CASE_INSENSITIVE);
+}
+
+
void birdCloseFile(HANDLE hFile)
{
birdResolveImports();
diff --git a/src/lib/nt/ntstat.c b/src/lib/nt/ntstat.c
index 0c85692..8778cda 100644
--- a/src/lib/nt/ntstat.c
+++ b/src/lib/nt/ntstat.c
@@ -1,4 +1,4 @@
-/* $Id: ntstat.c 2880 2016-09-05 20:36:26Z bird $ */
+/* $Id: ntstat.c 2993 2016-11-01 22:41:26Z bird $ */
/** @file
* MSC + NT stat, lstat and fstat.
*/
@@ -45,12 +45,24 @@
static int birdIsExecutableExtension(const char *pszExt)
{
- return !strcmp(pszExt, "exe")
- || !strcmp(pszExt, "cmd")
- || !strcmp(pszExt, "bat")
- || !strcmp(pszExt, "vbs")
- || !strcmp(pszExt, "com")
- ;
+ switch (pszExt[0])
+ {
+ default:
+ return 0;
+
+ case 'e': /* exe */
+ return pszExt[1] == 'x' && pszExt[2] == 'e' && pszExt[3] == '\0';
+
+ case 'b': /* bat */
+ return pszExt[1] == 'a' && pszExt[2] == 't' && pszExt[3] == '\0';
+
+ case 'v': /* vbs */
+ return pszExt[1] == 'v' && pszExt[2] == 's' && pszExt[3] == '\0';
+
+ case 'c': /* com and cmd */
+ return (pszExt[1] == 'o' && pszExt[2] == 'm' && pszExt[3] == '\0')
+ || (pszExt[1] == 'm' && pszExt[2] == 'd' && pszExt[3] == '\0');
+ }
}
@@ -78,12 +90,16 @@ static int birdIsFileExecutable(const char *pszName)
if (cchExt != 3)
return 0;
- /* Copy the extension out and lower case it. */
+ /* Copy the extension out and lower case it. Fail immediately on non-alpha chars. */
for (i = 0; i < cchExt; i++, pszExt++)
{
ch = *pszExt;
- if (ch >= 'A' && ch <= 'Z')
+ if (ch >= 'a' && ch <= 'z')
+ { /* likely */ }
+ else if (ch >= 'A' && ch <= 'Z')
ch += 'a' - 'A';
+ else
+ return 0;
szExt[i] = ch;
}
szExt[i] = '\0';
@@ -92,7 +108,10 @@ static int birdIsFileExecutable(const char *pszName)
}
-static int birdIsFileExecutableW(WCHAR const *pwcName, ULONG cwcName)
+/**
+ * @a pwcName could be the full path.
+ */
+static int birdIsFileExecutableW(WCHAR const *pwcName, size_t cwcName)
{
char szExt[8];
unsigned cchExt;
@@ -109,15 +128,17 @@ static int birdIsFileExecutableW(WCHAR const *pwcName, ULONG cwcName)
else
return 0;
- /* Copy the extension out and lower case it. */
+ /* Copy the extension out and lower case it. Fail immediately on non-alpha chars. */
pwc = &pwcName[cwcName - cchExt];
for (i = 0; i < cchExt; i++, pwc++)
{
WCHAR wc = *pwc;
- if (wc >= 'A' && wc <= 'Z')
+ if (wc >= 'a' && wc <= 'z')
+ { /* likely */ }
+ else if (wc >= 'A' && wc <= 'Z')
wc += 'a' - 'A';
- else if (wc > 255)
- wc = 255;
+ else
+ return 0;
szExt[i] = (char)wc;
}
szExt[i] = '\0';
@@ -127,7 +148,7 @@ static int birdIsFileExecutableW(WCHAR const *pwcName, ULONG cwcName)
static unsigned short birdFileInfoToMode(HANDLE hFile, ULONG fAttribs, const char *pszName,
- MY_FILE_NAME_INFORMATION *pNameInfo, __int16 *pfIsDirSymlink)
+ const wchar_t *pwszName, size_t cbNameW, __int16 *pfIsDirSymlink)
{
unsigned short fMode;
@@ -166,9 +187,9 @@ static unsigned short birdFileInfoToMode(HANDLE hFile, ULONG fAttribs, const cha
if (!(fAttribs & FILE_ATTRIBUTE_READONLY))
fMode |= S_IWOTH | S_IWGRP | S_IWUSR;
if ( (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
- || (pszName
- ? birdIsFileExecutable(pszName)
- : birdIsFileExecutableW(pNameInfo->FileName, pNameInfo->FileNameLength)) )
+ || (pwszName
+ ? birdIsFileExecutableW(pwszName, cbNameW)
+ : birdIsFileExecutable(pszName)) )
fMode |= S_IXOTH | S_IXGRP | S_IXUSR;
return fMode;
@@ -185,8 +206,8 @@ static unsigned short birdFileInfoToMode(HANDLE hFile, ULONG fAttribs, const cha
*/
void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf, const char *pszPath)
{
- pStat->st_mode = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes, pszPath,
- NULL, &pStat->st_dirsymlink);
+ pStat->st_mode = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes,
+ pszPath, pBuf->FileName, pBuf->FileNameLength, &pStat->st_dirsymlink);
pStat->st_padding0[0] = 0;
pStat->st_padding0[1] = 0;
pStat->st_size = pBuf->EndOfFile.QuadPart;
@@ -217,8 +238,8 @@ void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_IN
*/
void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf, const char *pszPath)
{
- pStat->st_mode = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes, pszPath,
- NULL, &pStat->st_dirsymlink);
+ pStat->st_mode = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes,
+ pszPath, pBuf->FileName, pBuf->FileNameLength, &pStat->st_dirsymlink);
pStat->st_padding0[0] = 0;
pStat->st_padding0[1] = 0;
pStat->st_size = pBuf->EndOfFile.QuadPart;
@@ -249,8 +270,8 @@ void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_IN
*/
void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf, const char *pszPath)
{
- pStat->st_mode = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes, pszPath,
- NULL, &pStat->st_dirsymlink);
+ pStat->st_mode = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes,
+ pszPath, pBuf->FileName, pBuf->FileNameLength, &pStat->st_dirsymlink);
pStat->st_padding0[0] = 0;
pStat->st_padding0[1] = 0;
pStat->st_size = pBuf->EndOfFile.QuadPart;
@@ -271,7 +292,7 @@ void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMA
}
-int birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath)
+int birdStatHandle2(HANDLE hFile, BirdStat_T *pStat, const char *pszPath, const wchar_t *pwszPath)
{
int rc;
MY_NTSTATUS rcNt;
@@ -289,7 +310,8 @@ int birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath)
if (MY_NT_SUCCESS(rcNt))
{
pStat->st_mode = birdFileInfoToMode(hFile, pAll->BasicInformation.FileAttributes, pszPath,
- &pAll->NameInformation, &pStat->st_dirsymlink);
+ pAll->NameInformation.FileNamepAll->NameInformation.FileNameLength,
+ &pStat->st_dirsymlink);
pStat->st_padding0[0] = 0;
pStat->st_padding0[1] = 0;
pStat->st_size = pAll->StandardInformation.EndOfFile.QuadPart;
@@ -351,7 +373,7 @@ int birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath)
rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &InternalInfo, sizeof(InternalInfo), MyFileInternalInformation);
if (MY_NT_SUCCESS(rcNt))
rcNt = Ios.u.Status;
- if (MY_NT_SUCCESS(rcNt) && !pszPath)
+ if (MY_NT_SUCCESS(rcNt) && !pszPath && !pwszPath)
{
cbNameInfo = 0x10020;
pNameInfo = (MY_FILE_NAME_INFORMATION *)alloca(cbNameInfo);
@@ -363,7 +385,10 @@ int birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath)
if (MY_NT_SUCCESS(rcNt))
{
pStat->st_mode = birdFileInfoToMode(hFile, BasicInfo.FileAttributes, pszPath,
- pNameInfo, &pStat->st_dirsymlink);
+ pNameInfo ? pNameInfo->FileName : pwszPath,
+ pNameInfo ? pNameInfo->FileNameLength
+ : pwszPath ? wcslen(pwszPath) * sizeof(wchar_t) : 0,
+ &pStat->st_dirsymlink);
pStat->st_padding0[0] = 0;
pStat->st_padding0[1] = 0;
pStat->st_size = StdInfo.EndOfFile.QuadPart;
@@ -412,6 +437,12 @@ int birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath)
}
+int birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath)
+{
+ return birdStatHandle2(hFile, pStat, pszPath, NULL);
+}
+
+
/**
* Generates a device number from the volume information.
*
@@ -459,19 +490,19 @@ MY_NTSTATUS birdQueryVolumeDeviceNumber(HANDLE hFile, MY_FILE_FS_VOLUME_INFORMAT
}
-static int birdStatInternal(const char *pszPath, BirdStat_T *pStat, int fFollow)
+static int birdStatInternal(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat, int fFollow)
{
int rc;
- HANDLE hFile = birdOpenFile(pszPath,
- FILE_READ_ATTRIBUTES,
- FILE_ATTRIBUTE_NORMAL,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- FILE_OPEN,
- FILE_OPEN_FOR_BACKUP_INTENT | (fFollow ? 0 : FILE_OPEN_REPARSE_POINT),
- OBJ_CASE_INSENSITIVE);
+ HANDLE hFile = birdOpenFileEx(hRoot, pszPath,
+ FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT | (fFollow ? 0 : FILE_OPEN_REPARSE_POINT),
+ OBJ_CASE_INSENSITIVE);
if (hFile != INVALID_HANDLE_VALUE)
{
- rc = birdStatHandle(hFile, pStat, pszPath);
+ rc = birdStatHandle2(hFile, pStat, pszPath, NULL);
birdCloseFile(hFile);
#if 0
@@ -499,7 +530,7 @@ static int birdStatInternal(const char *pszPath, BirdStat_T *pStat, int fFollow)
&& strchr(pszPath, '?') == NULL)
{
MY_UNICODE_STRING NameUniStr;
- hFile = birdOpenParentDir(pszPath,
+ hFile = birdOpenParentDir(hRoot, pszPath,
FILE_READ_DATA | SYNCHRONIZE,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
@@ -551,6 +582,84 @@ static int birdStatInternal(const char *pszPath, BirdStat_T *pStat, int fFollow)
}
+static int birdStatInternalW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *pStat, int fFollow)
+{
+ int rc;
+ HANDLE hFile = birdOpenFileExW(hRoot, pwszPath,
+ FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT | (fFollow ? 0 : FILE_OPEN_REPARSE_POINT),
+ OBJ_CASE_INSENSITIVE);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ rc = birdStatHandle2(hFile, pStat, NULL, pwszPath);
+ birdCloseFile(hFile);
+ }
+ else
+ {
+ /*
+ * On things like pagefile.sys we may get sharing violation. We fall
+ * back on directory enumeration for dealing with that.
+ */
+ if ( errno == ETXTBSY
+ && wcschr(pwszPath, '*') == NULL /* Serious paranoia... */
+ && wcschr(pwszPath, '?') == NULL)
+ {
+ MY_UNICODE_STRING NameUniStr;
+ hFile = birdOpenParentDirW(hRoot, pwszPath,
+ FILE_READ_DATA | SYNCHRONIZE,
+ 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,
+ OBJ_CASE_INSENSITIVE,
+ &NameUniStr);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ MY_FILE_ID_FULL_DIR_INFORMATION *pBuf;
+ ULONG cbBuf = sizeof(*pBuf) + NameUniStr.MaximumLength + 1024;
+ MY_IO_STATUS_BLOCK Ios;
+ MY_NTSTATUS rcNt;
+
+ pBuf = (MY_FILE_ID_FULL_DIR_INFORMATION *)alloca(cbBuf);
+ Ios.u.Status = -1;
+ Ios.Information = -1;
+ rcNt = g_pfnNtQueryDirectoryFile(hFile, NULL, NULL, NULL, &Ios, pBuf, cbBuf,
+ MyFileIdFullDirectoryInformation, FALSE, &NameUniStr, TRUE);
+ if (MY_NT_SUCCESS(rcNt))
+ rcNt = Ios.u.Status;
+ if (MY_NT_SUCCESS(rcNt))
+ {
+ /*
+ * Convert the data.
+ */
+ birdStatFillFromFileIdFullDirInfo(pStat, pBuf, NULL);
+
+ /* Get the serial number, reusing the buffer from above. */
+ rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev);
+ if (MY_NT_SUCCESS(rcNt))
+ rc = 0;
+ else
+ rc = birdSetErrnoFromNt(rcNt);
+ }
+
+ birdFreeNtPath(&NameUniStr);
+ birdCloseFile(hFile);
+
+ if (MY_NT_SUCCESS(rcNt))
+ return 0;
+ birdSetErrnoFromNt(rcNt);
+ }
+ }
+ rc = -1;
+ }
+
+ return rc;
+}
+
+
/**
* Implements UNIX fstat().
*/
@@ -569,7 +678,7 @@ int birdStatOnFd(int fd, BirdStat_T *pStat)
switch (fFileType)
{
case FILE_TYPE_DISK:
- rc = birdStatHandle(hFile, pStat, NULL);
+ rc = birdStatHandle2(hFile, pStat, NULL, NULL);
break;
case FILE_TYPE_CHAR:
@@ -656,7 +765,16 @@ int birdStatOnFdJustSize(int fd, __int64 *pcbFile)
*/
int birdStatFollowLink(const char *pszPath, BirdStat_T *pStat)
{
- return birdStatInternal(pszPath, pStat, 1 /*fFollow*/);
+ return birdStatInternal(NULL, pszPath, pStat, 1 /*fFollow*/);
+}
+
+
+/**
+ * Implements UNIX stat().
+ */
+int birdStatFollowLinkW(const wchar_t *pwszPath, BirdStat_T *pStat)
+{
+ return birdStatInternalW(NULL, pwszPath, pStat, 1 /*fFollow*/);
}
@@ -665,7 +783,46 @@ int birdStatFollowLink(const char *pszPath, BirdStat_T *pStat)
*/
int birdStatOnLink(const char *pszPath, BirdStat_T *pStat)
{
- return birdStatInternal(pszPath, pStat, 0 /*fFollow*/);
+ return birdStatInternal(NULL, pszPath, pStat, 0 /*fFollow*/);
+}
+
+
+/**
+ * Implements UNIX lstat().
+ */
+int birdStatOnLinkW(const wchar_t *pwszPath, BirdStat_T *pStat)
+{
+ return birdStatInternalW(NULL, pwszPath, pStat, 0 /*fFollow*/);
+}
+
+
+/**
+ * Implements an API like UNIX fstatat().
+ *
+ * @returns 0 on success, -1 and errno on failure.
+ * @param hRoot NT handle pwszPath is relative to.
+ * @param pszPath The path.
+ * @param pStat Where to return stats.
+ * @param fFollowLink Whether to follow links.
+ */
+int birdStatAt(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat, int fFollowLink)
+{
+ return birdStatInternal(hRoot, pszPath, pStat, fFollowLink != 0);
+}
+
+
+/**
+ * Implements an API like UNIX fstatat().
+ *
+ * @returns 0 on success, -1 and errno on failure.
+ * @param hRoot NT handle pwszPath is relative to.
+ * @param pwszPath The path.
+ * @param pStat Where to return stats.
+ * @param fFollowLink Whether to follow links.
+ */
+int birdStatAtW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *pStat, int fFollowLink)
+{
+ return birdStatInternalW(hRoot, pwszPath, pStat, fFollowLink != 0);
}
diff --git a/src/lib/nt/ntstat.h b/src/lib/nt/ntstat.h
index 35bd4b4..ca18c3b 100644
--- a/src/lib/nt/ntstat.h
+++ b/src/lib/nt/ntstat.h
@@ -1,4 +1,4 @@
-/* $Id: ntstat.h 2858 2016-09-01 15:12:24Z bird $ */
+/* $Id: ntstat.h 2985 2016-11-01 18:26:35Z bird $ */
/** @file
* MSC + NT stat, lstat and fstat implementation and wrappers.
*/
@@ -76,7 +76,11 @@ typedef struct BirdStat
#define st_birthtime st_birthtim.tv_sec
int birdStatFollowLink(const char *pszPath, BirdStat_T *pStat);
+int birdStatFollowLinkW(const wchar_t *pwszPath, BirdStat_T *pStat);
int birdStatOnLink(const char *pszPath, BirdStat_T *pStat);
+int birdStatOnLinkW(const wchar_t *pwszPath, BirdStat_T *pStat);
+int birdStatAt(void *hRoot, const char *pszPath, BirdStat_T *pStat, int fFollowLink);
+int birdStatAtW(void *hRoot, const wchar_t *pwszPath, BirdStat_T *pStat, int fFollowLink);
int birdStatOnFd(int fd, BirdStat_T *pStat);
int birdStatOnFdJustSize(int fd, __int64 *pcbFile);
int birdStatModTimeOnly(const char *pszPath, BirdTimeSpec_T *pTimeSpec, int fFollowLink);
diff --git a/src/lib/nt/ntstuff.h b/src/lib/nt/ntstuff.h
index f9ed594..c1a9be3 100644
--- a/src/lib/nt/ntstuff.h
+++ b/src/lib/nt/ntstuff.h
@@ -1,4 +1,4 @@
-/* $Id: ntstuff.h 2900 2016-09-09 14:42:06Z bird $ */
+/* $Id: ntstuff.h 2985 2016-11-01 18:26:35Z bird $ */
/** @file
* Definitions, types, prototypes and globals for NT.
*/
@@ -40,6 +40,8 @@
#include <ntstatus.h>
#undef timeval
+#include <k/kTypes.h>
+
/** @defgroup grp_nt_ntstuff NT Stuff
* @{ */
@@ -466,6 +468,14 @@ typedef struct MY_RTL_RELATIVE_NAME_U
# define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000U
#endif
+#ifndef DUPLICATE_CLOSE_SOURCE /* For the misnomer NtDuplicateObject. */
+# define DUPLICATE_CLOSE_SOURCE 0x00000001U
+# define DUPLICATE_SAME_ACCESS 0x00000002U
+#endif
+#ifndef DUPLICATE_SAME_ATTRIBUTES
+# define DUPLICATE_SAME_ATTRIBUTES 0x00000004U
+#endif
+
/** @name NT status codes and associated macros.
* @{ */
@@ -479,6 +489,47 @@ typedef struct MY_RTL_RELATIVE_NAME_U
#define MY_STATUS_OBJECT_PATH_SYNTAX_BAD ((MY_NTSTATUS)0xc000003b)
/** @} */
+/** The pseudohandle for the current process. */
+#define MY_NT_CURRENT_PROCESS ((HANDLE)~(uintptr_t)0)
+/** The pseudohandle for the current thread. */
+#define MY_NT_CURRENT_THREAD ((HANDLE)~(uintptr_t)1)
+
+typedef struct MY_CLIENT_ID
+{
+ HANDLE UniqueProcess;
+ HANDLE UniqueThread;
+} MY_CLIENT_ID;
+
+/** Partial TEB. */
+typedef struct MY_PARTIAL_TEB
+{
+ NT_TIB NtTib;
+ PVOID EnvironmentPointer;
+ MY_CLIENT_ID ClientId;
+ PVOID ActiveRpcHandle;
+ PVOID ThreadLocalStoragePointer;
+ PPEB ProcessEnvironmentBlock;
+ KU32 LastErrorValue;
+ KU32 CountOfOwnedCriticalSections;
+ PVOID CsrClientThread;
+ PVOID Win32ThreadInfo;
+} MY_PARTIAL_TEB;
+
+/** Internal macro for reading uintptr_t sized TEB members. */
+#if K_ARCH == K_ARCH_AMD64
+# define MY_NT_READ_TEB_WORKER(a_offTebMember) ( __readgsqword(a_offTebMember) )
+#elif K_ARCH == K_ARCH_X86
+# define MY_NT_READ_TEB_WORKER(a_offTebMember) ( __readfsdword(a_offTebMember) )
+#else
+# else "Port me!"
+#endif
+/** Get the PEB pointer.
+ * @remark Needs stddef.h. */
+#define MY_NT_CURRENT_PEB() ( (PPEB)MY_NT_READ_TEB_WORKER(offsetof(MY_PARTIAL_TEB, ProcessEnvironmentBlock)) )
+/** Get the TEB pointer.
+ * @remark Needs stddef.h. */
+#define MY_NT_CURRENT_TEB() ( (PTEB)MY_NT_READ_TEB_WORKER(offsetof(NT_TIB, Self)) )
+
/*******************************************************************************
* Global Variables *
@@ -487,6 +538,8 @@ extern MY_NTSTATUS (WINAPI * g_pfnNtClose)(HANDLE);
extern MY_NTSTATUS (WINAPI * g_pfnNtCreateFile)(PHANDLE, MY_ACCESS_MASK, MY_OBJECT_ATTRIBUTES *, MY_IO_STATUS_BLOCK *,
PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG);
extern MY_NTSTATUS (WINAPI * g_pfnNtDeleteFile)(MY_OBJECT_ATTRIBUTES *);
+extern MY_NTSTATUS (WINAPI * g_pfnNtDuplicateObject)(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, HANDLE *phRet,
+ MY_ACCESS_MASK fDesiredAccess, ULONG fAttribs, ULONG fOptions);
extern MY_NTSTATUS (WINAPI * g_pfnNtReadFile)(HANDLE hFile, HANDLE hEvent, MY_IO_APC_ROUTINE *pfnApc, PVOID pvApcCtx,
MY_IO_STATUS_BLOCK *, PVOID pvBuf, ULONG cbToRead, PLARGE_INTEGER poffFile,
PULONG puKey);
@@ -507,6 +560,9 @@ extern BOOLEAN (WINAPI * g_pfnRtlEqualUnicodeString)(MY_UNICODE_STRING const
extern BOOLEAN (WINAPI * g_pfnRtlEqualString)(MY_ANSI_STRING const *pAnsiStr1, MY_ANSI_STRING const *pAnsiStr2,
BOOLEAN fCaseInsensitive);
extern UCHAR (WINAPI * g_pfnRtlUpperChar)(UCHAR uch);
+extern ULONG (WINAPI * g_pfnRtlNtStatusToDosError)(MY_NTSTATUS rcNt);
+extern VOID (WINAPI * g_pfnRtlAcquirePebLock)(VOID);
+extern VOID (WINAPI * g_pfnRtlReleasePebLock)(VOID);
/** @} */
diff --git a/src/lib/nt/ntunlink.c b/src/lib/nt/ntunlink.c
index b417767..622f47e 100644
--- a/src/lib/nt/ntunlink.c
+++ b/src/lib/nt/ntunlink.c
@@ -1,4 +1,4 @@
-/* $Id: ntunlink.c 2713 2013-11-21 21:11:00Z bird $ */
+/* $Id: ntunlink.c 2997 2016-11-01 23:28:02Z bird $ */
/** @file
* MSC + NT unlink and variations.
*/
@@ -45,7 +45,8 @@ static MY_NTSTATUS birdMakeWritable(MY_UNICODE_STRING *pNtPath)
MY_NTSTATUS rcNt;
HANDLE hFile;
- rcNt = birdOpenFileUniStr(pNtPath,
+ rcNt = birdOpenFileUniStr(NULL /*hRoot*/,
+ pNtPath,
FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
@@ -81,12 +82,17 @@ static MY_NTSTATUS birdMakeWritable(MY_UNICODE_STRING *pNtPath)
}
-static int birdUnlinkInternal(const char *pszFile, int fReadOnlyToo, int fFast)
+static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, int fReadOnlyToo, int fFast)
{
MY_UNICODE_STRING NtPath;
int rc;
- rc = birdDosToNtPath(pszFile, &NtPath);
+ if (hRoot == INVALID_HANDLE_VALUE)
+ hRoot = NULL;
+ if (hRoot == NULL)
+ rc = birdDosToNtPath(pszFile, &NtPath);
+ else
+ rc = birdDosToRelativeNtPath(pszFile, &NtPath);
if (rc == 0)
{
MY_NTSTATUS rcNt;
@@ -94,7 +100,7 @@ static int birdUnlinkInternal(const char *pszFile, int fReadOnlyToo, int fFast)
{
/* This uses FILE_DELETE_ON_CLOSE. Probably only suitable when in a hurry... */
MY_OBJECT_ATTRIBUTES ObjAttr;
- MyInitializeObjectAttributes(&ObjAttr, &NtPath, OBJ_CASE_INSENSITIVE, NULL /*hRoot*/, NULL /*pSecAttr*/);
+ MyInitializeObjectAttributes(&ObjAttr, &NtPath, OBJ_CASE_INSENSITIVE, hRoot, NULL /*pSecAttr*/);
rcNt = g_pfnNtDeleteFile(&ObjAttr);
/* In case some file system does things differently than NTFS. */
@@ -111,7 +117,8 @@ static int birdUnlinkInternal(const char *pszFile, int fReadOnlyToo, int fFast)
int fMayTryAgain = 1;
for (;;)
{
- rcNt = birdOpenFileUniStr(&NtPath,
+ rcNt = birdOpenFileUniStr(hRoot,
+ &NtPath,
DELETE,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
@@ -154,18 +161,36 @@ static int birdUnlinkInternal(const char *pszFile, int fReadOnlyToo, int fFast)
int birdUnlink(const char *pszFile)
{
- return birdUnlinkInternal(pszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
+ return birdUnlinkInternal(NULL /*hRoot*/, pszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
+}
+
+
+int birdUnlinkEx(void *hRoot, const char *pszFile)
+{
+ return birdUnlinkInternal((HANDLE)hRoot, pszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
}
int birdUnlinkForced(const char *pszFile)
{
- return birdUnlinkInternal(pszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
+ return birdUnlinkInternal(NULL /*hRoot*/, pszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
+}
+
+
+int birdUnlinkForcedEx(void *hRoot, const char *pszFile)
+{
+ return birdUnlinkInternal((HANDLE)hRoot, pszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
}
int birdUnlinkForcedFast(const char *pszFile)
{
- return birdUnlinkInternal(pszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
+ return birdUnlinkInternal(NULL /*hRoot*/, pszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
+}
+
+
+int birdUnlinkForcedFastEx(void *hRoot, const char *pszFile)
+{
+ return birdUnlinkInternal((HANDLE)hRoot, pszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
}
diff --git a/src/lib/nt/ntunlink.h b/src/lib/nt/ntunlink.h
index ec7d82a..195ef7f 100644
--- a/src/lib/nt/ntunlink.h
+++ b/src/lib/nt/ntunlink.h
@@ -1,4 +1,4 @@
-/* $Id: ntunlink.h 2713 2013-11-21 21:11:00Z bird $ */
+/* $Id: ntunlink.h 2997 2016-11-01 23:28:02Z bird $ */
/** @file
* MSC + NT unlink and variations.
*/
@@ -34,8 +34,11 @@
#include "nttypes.h"
int birdUnlink(const char *pszFile);
+int birdUnlinkEx(void *hRoot, const char *pszFile);
int birdUnlinkForced(const char *pszFile);
+int birdUnlinkForcedEx(void *hRoot, const char *pszFile);
int birdUnlinkForcedFast(const char *pszFile);
+int birdUnlinkForcedFastEx(void *hRoot, const char *pszFile);
#undef unlink
#define unlink(a_pszPath) birdUnlinkForced(a_pszPath)
diff --git a/src/lib/nt/tstNtFts.c b/src/lib/nt/tstNtFts.c
new file mode 100644
index 0000000..d2fed4c
--- /dev/null
+++ b/src/lib/nt/tstNtFts.c
@@ -0,0 +1,244 @@
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifndef USE_OLD_FTS
+# include "fts-nt.h"
+#else
+# include "kmkbuiltin/ftsfake.h"
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+
+static int usage(const char *argv0)
+{
+ printf("usage: %s [options] <dirs & files>\n", argv0);
+ printf("\n"
+ "options:\n"
+ " -d, --see-dot\n"
+ " FTS_SEEDOT\n"
+ " -p, --physical\n"
+ " FTS_PHYSICAL\n"
+ " -l, --logical\n"
+ " FTS_LOGICAL\n"
+ " -H, --dereference-command-line\n"
+ " FTS_COMFOLLOW\n"
+ " -L, --dereference\n"
+ " Follow symbolic links while scanning directories.\n"
+ " -P, --no-dereference\n"
+ " Do not follow symbolic links while scanning directories.\n"
+ " -c, --no-chdir\n"
+ " FTS_NOCHDIR\n"
+ " -s, --no-stat\n"
+ " FTS_NOSTAT\n"
+ " -x, --one-file-system\n"
+ " FTS_XDEV\n"
+ " -q, --quiet\n"
+ " Quiet operation, no output.\n"
+ " -v, --verbose\n"
+ " Verbose operation (default).\n"
+ );
+ return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ FTS *pFts;
+ int i;
+ int rcExit = 0;
+ int cVerbosity = 1;
+ int fFollowLinks = 0;
+ int fFtsFlags = 0;
+ unsigned fDoneOptions = 0;
+ unsigned cFtsArgs = 0;
+ char const **papszFtsArgs = calloc(argc + 1, sizeof(char *));
+
+ /*
+ * Parse options and heap up non-options.
+ */
+ for (i = 1; i < argc; i++)
+ {
+ const char *pszArg = argv[i];
+ if (*pszArg == '-' && !fDoneOptions)
+ {
+ char chOpt = *++pszArg;
+ pszArg++;
+ if (chOpt == '-')
+ {
+ chOpt = *pszArg++;
+ if (!chOpt)
+ {
+ fDoneOptions = 1;
+ continue;
+ }
+ if (strcmp(pszArg, "help") == 0)
+ chOpt = 'h';
+ else if (strcmp(pszArg, "version") == 0)
+ chOpt = 'V';
+ else if (strcmp(pszArg, "see-dot") == 0)
+ chOpt = 'd';
+ else if (strcmp(pszArg, "physical") == 0)
+ chOpt = 'p';
+ else if (strcmp(pszArg, "logical") == 0)
+ chOpt = 'l';
+ else if (strcmp(pszArg, "dereference-command-line") == 0)
+ chOpt = 'H';
+ else if (strcmp(pszArg, "no-chdir") == 0)
+ chOpt = 'c';
+ else if (strcmp(pszArg, "no-stat") == 0)
+ chOpt = 's';
+ else if (strcmp(pszArg, "one-file-system") == 0)
+ chOpt = 'x';
+ else if (strcmp(pszArg, "quiet") == 0)
+ chOpt = 'q';
+ else if (strcmp(pszArg, "verbose") == 0)
+ chOpt = 'v';
+ else
+ {
+ fprintf(stderr, "syntax error: Unknown option: --%s\n", pszArg);
+ return 2;
+ }
+ pszArg = "";
+ }
+ do
+ {
+ switch (chOpt)
+ {
+ case '?':
+ case 'h':
+ return usage(argv[0]);
+ case 'V':
+ printf("v0.0.0\n");
+ return 0;
+
+ case 'd':
+ fFtsFlags |= FTS_SEEDOT;
+ break;
+ case 'l':
+ fFtsFlags |= FTS_LOGICAL;
+ break;
+ case 'p':
+ fFtsFlags |= FTS_PHYSICAL;
+ break;
+ case 'H':
+ fFtsFlags |= FTS_COMFOLLOW;
+ break;
+ case 'c':
+ fFtsFlags |= FTS_NOCHDIR;
+ break;
+ case 's':
+ fFtsFlags |= FTS_NOSTAT;
+ break;
+ case 'x':
+ fFtsFlags |= FTS_XDEV;
+ break;
+ case 'L':
+ fFollowLinks = 1;
+ break;
+ case 'P':
+ fFollowLinks = 0;
+ break;
+
+ case 'q':
+ cVerbosity = 0;
+ break;
+ case 'v':
+ cVerbosity++;
+ break;
+
+ default:
+ fprintf(stderr, "syntax error: Unknown option: -%c (%s)\n", chOpt, argv[i]);
+ return 2;
+ }
+ chOpt = *pszArg++;
+ } while (chOpt != '\0');
+ }
+ else
+ papszFtsArgs[cFtsArgs++] = pszArg;
+ }
+
+#ifdef USE_OLD_FTS
+ if (papszFtsArgs[0] == NULL)
+ {
+ fprintf(stderr, "Nothing to do\n");
+ return 1;
+ }
+#endif
+
+ /*
+ * Do the traversal.
+ */
+ errno = 0;
+ pFts = fts_open((char **)papszFtsArgs, fFtsFlags, NULL /*pfnCompare*/);
+ if (pFts)
+ {
+ for (;;)
+ {
+ FTSENT *pFtsEnt = fts_read(pFts);
+ if (pFtsEnt)
+ {
+ const char *pszState;
+ switch (pFtsEnt->fts_info)
+ {
+ case FTS_D: pszState = "D"; break;
+ case FTS_DC: pszState = "DC"; break;
+ case FTS_DEFAULT: pszState = "DEFAULT"; break;
+ case FTS_DNR: pszState = "DNR"; break;
+ case FTS_DOT: pszState = "DOT"; break;
+ case FTS_DP: pszState = "DP"; break;
+ case FTS_ERR: pszState = "ERR"; break;
+ case FTS_F: pszState = "F"; break;
+ case FTS_INIT: pszState = "INIT"; break;
+ case FTS_NS: pszState = "NS"; break;
+ case FTS_NSOK: pszState = "NSOK"; break;
+ case FTS_SL: pszState = "SL"; break;
+ case FTS_SLNONE: pszState = "SLNONE"; break;
+ default:
+ pszState = "Invalid";
+ rcExit = 1;
+ break;
+ }
+
+ if (cVerbosity > 0)
+ printf("%8s %s\n", pszState, pFtsEnt->fts_accpath);
+ if ( pFtsEnt->fts_info == FTS_SL
+ && pFtsEnt->fts_number == 0
+ && fFollowLinks
+ && ( (fFtsFlags & FTS_COMFOLLOW)
+ || pFtsEnt->fts_level > FTS_ROOTLEVEL) ) {
+ pFtsEnt->fts_number++;
+ fts_set(pFts, pFtsEnt, FTS_FOLLOW);
+ }
+ }
+ else
+ {
+ if (errno != 0)
+ {
+ fprintf(stderr, "fts_read failed: errno=%d\n", errno);
+ rcExit = 1;
+ }
+ break;
+ }
+ } /* enum loop */
+
+ errno = 0;
+ i = fts_close(pFts);
+ if (i != 0)
+ {
+ fprintf(stderr, "fts_close failed: errno=%d\n", errno);
+ rcExit = 1;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "fts_open failed: errno=%d (cFtsArgs=%u)\n", errno, cFtsArgs);
+ rcExit = 1;
+ }
+
+ return rcExit;
+}
diff --git a/src/lib/quote_argv.c b/src/lib/quote_argv.c
index 016f945..243c48f 100644
--- a/src/lib/quote_argv.c
+++ b/src/lib/quote_argv.c
@@ -1,4 +1,4 @@
-/* $Id: quote_argv.c 2894 2016-09-08 13:27:56Z bird $ */
+/* $Id: quote_argv.c 2912 2016-09-14 13:36:15Z bird $ */
/** @file
* quote_argv - Correctly quote argv for spawn, windows specific.
*/
@@ -79,6 +79,7 @@ static int isWatcomPassThruOption(const char *pszArg)
* For details on how MSC parses the command line, see "Parsing C Command-Line
* Arguments": http://msdn.microsoft.com/en-us/library/a1y7w461.aspx
*
+ * @returns 0 on success, -1 if out of memory.
* @param argc The argument count.
* @param argv The argument vector.
* @param fWatcomBrainDamage Set if we're catering for wcc, wcc386 or similar
@@ -89,7 +90,7 @@ static int isWatcomPassThruOption(const char *pszArg)
* depends on which argv you're working on.
* Suggest doing the latter if it's main()'s argv.
*/
-void quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak)
+int quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak)
{
int i;
for (i = 0; i < argc; i++)
@@ -121,6 +122,8 @@ void quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak)
int fComplicated = pszQuotes || (cchOrg > 0 && pszOrg[cchOrg - 1] == '\\');
size_t cchNew = fComplicated ? cchOrg * 2 + 2 : cchOrg + 2;
char *pszNew = (char *)malloc(cchNew + 1 /*term*/ + 3 /*passthru hack*/);
+ if (!pszNew)
+ return -1;
argv[i] = pszNew;
@@ -207,5 +210,6 @@ void quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak)
}
/*for (i = 0; i < argc; i++) fprintf(stderr, "argv[%u]=%s;;\n", i, argv[i]);*/
+ return 0;
}
diff --git a/src/lib/quote_argv.h b/src/lib/quote_argv.h
index 3f6b42e..6e0a13e 100644
--- a/src/lib/quote_argv.h
+++ b/src/lib/quote_argv.h
@@ -1,4 +1,4 @@
-/* $Id: quote_argv.h 2851 2016-08-31 17:30:52Z bird $ */
+/* $Id: quote_argv.h 2912 2016-09-14 13:36:15Z bird $ */
/** @file
* quote_argv - Correctly quote argv for spawn, windows specific.
*/
@@ -33,7 +33,7 @@
#define ___quote_argv_h___
#include "mytypes.h"
-extern void quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak);
+extern int quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak);
#endif
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-virtualbox/kbuild.git
More information about the Pkg-virtualbox-commits
mailing list