[Pkg-virtualbox-commits] [kbuild] 01/10: Imported Upstream version 0.1.9998svn3025+dfsg
Gianfranco Costamagna
locutusofborg at moszumanska.debian.org
Thu Oct 19 15:44:39 UTC 2017
This is an automated email from the git hooks/post-receive script.
locutusofborg pushed a commit to branch experimental
in repository kbuild.
commit 10af2d8272974667288cd5e1ca802bf1df5e4c1e
Author: Gianfranco Costamagna <locutusofborg at debian.org>
Date: Fri Jan 13 17:46:59 2017 +0100
Imported Upstream version 0.1.9998svn3025+dfsg
---
SlickEdit/kdev.e | 56 ++-
SlickEdit/kkeys.e | 8 +-
kBuild/tools/VAC308.kmk | 12 +-
kBuild/units/qt5.kmk | 17 +-
src/Makefile.kmk | 3 +-
src/{ => kDeDup}/Makefile.kmk | 23 +-
src/kDeDup/kDeDup.c | 838 ++++++++++++++++++++++++++++++++++++++++++
src/kmk/dir-nt-bird.c | 53 ++-
src/lib/nt/fts-nt.c | 786 +++++++++++++++++++++++++++++----------
src/lib/nt/fts-nt.h | 18 +-
src/lib/nt/kFsCache.c | 21 +-
src/lib/nt/ntdir.c | 290 ++++++++++++++-
src/lib/nt/ntdir.h | 27 +-
src/lib/nt/nthlp.h | 4 +-
src/lib/nt/nthlpcore.c | 5 +-
src/lib/nt/ntstat.c | 237 +++++++-----
src/lib/nt/ntstat.h | 11 +-
src/lib/nt/ntstuff.h | 7 +-
src/lib/nt/ntunlink.c | 66 +++-
src/lib/nt/ntunlink.h | 8 +-
src/lib/nt/tstNtFts.c | 19 +-
src/lib/nt/tstNtStat.c | 5 +-
22 files changed, 2139 insertions(+), 375 deletions(-)
diff --git a/SlickEdit/kdev.e b/SlickEdit/kdev.e
index 5e883ae..46d7940 100644
--- a/SlickEdit/kdev.e
+++ b/SlickEdit/kdev.e
@@ -1,4 +1,4 @@
-/* $Id: kdev.e 2826 2016-08-14 13:58:02Z bird $ -*- tab-width: 4 c-indent-level: 4 -*- */
+/* $Id: kdev.e 3018 2017-01-07 00:06:39Z bird $ -*- tab-width: 4 c-indent-level: 4 -*- */
/** @file
* Visual SlickEdit Documentation Macros.
*/
@@ -190,45 +190,71 @@ static boolean k_commentconfig(_str &sLeft, _str &sRight, int &iColumn, _str sEx
if (sLexer)
{
/* multiline */
-#if __VERSION__ >= 14.0
+#if __VERSION__ >= 21.0
+ COMMENT_TYPE aComments[];
+ GetComments(aComments, "M", sLexer);
+ for (i = 0; i < aComments._length(); i++)
+ if (!aComments[i].isDocumentation)
+ {
+ sLeft = aComments[i].delim1;
+ sRight = aComments[i].delim2;
+ iColumn = aComments[i].startcol;
+ if (sLeft != '' && sRight != '')
+ return true;
+ }
+#else
+# if __VERSION__ >= 14.0
_str aComments[] = null;
GetComments(aComments, "mlcomment", sLexer);
for (i = 0; i < aComments._length(); i++)
- if (!pos("documentation", aComments[i]) > 0)
+ if (pos("documentation", aComments[i]) <= 0)
{
sLine = aComments[i];
break;
}
if (sLine != '')
-#else
+# else
rc = _ini_get_value(slick_path_search("user.vlx"), sLexer, 'mlcomment', sLine);
if (rc)
rc = _ini_get_value(slick_path_search("vslick.vlx"), sLexer, 'mlcomment', sLine);
if (!rc)
-#endif
+# endif
{
sLeft = strip(word(sLine, 1));
sRight = strip(word(sLine, 2));
if (sLeft != '' && sRight != '')
return true;
}
+#endif
/* failed, try single line. */
-#if __VERSION__ >= 14.0
+#if __VERSION__ >= 21.0
+ GetComments(aComments, "L", sLexer);
+ for (i = 0; i < aComments._length(); i++)
+ if (!aComments[i].isDocumentation)
+ {
+ sLeft = aComments[i].delim1;
+ sRight = '';
+ iColumn = aComments[i].startcol;
+ if (sLeft != '')
+ return true;
+ }
+#else
+# if __VERSION__ >= 14.0
GetComments(aComments, "linecomment", sLexer)
for (i = 0; i < aComments._length(); i++)
- if (!pos("documentation", aComments[i]) > 0)
+ if (pos("documentation", aComments[i]) <= 0)
{
sLine = aComments[i];
break;
}
if (sLine != '')
-#else
+# else
rc = _ini_get_value(slick_path_search("user.vlx"), sLexer, 'linecomment', sLine);
if (rc)
rc = _ini_get_value(slick_path_search("vslick.vlx"), sLexer, 'linecomment', sLine);
if (!rc)
-#endif
+# endif
{
sLeft = strip(word(sLine, 1));
sRight = '';
@@ -239,6 +265,7 @@ static boolean k_commentconfig(_str &sLeft, _str &sRight, int &iColumn, _str sEx
if (sLeft != '')
return true;
}
+#endif
}
/*
@@ -2447,6 +2474,11 @@ static int k_style_emacs_var(_str sVar, _str sVal)
return -1;
//say 'k_style_emacs_var: 'sVar'='sVal;
+#if __VERSION__ >= 21.0
+ /** @todo figure out p_index. */
+ return 0;
+#else
+
/*
* Unpack the mode style parameters.
*/
@@ -2804,6 +2836,7 @@ static int k_style_emacs_var(_str sVar, _str sVal)
}
return 0;
+#endif
}
@@ -3570,6 +3603,10 @@ _command void kdev_load_settings()
LanguageSettings.setSyntaxIndent(sLangId, 4);
/* C/C++ setup, wrap at column 80 not 64. */
+# if __VERSION__ >= 21.0
+ if (_LangGetPropertyInt32(sLangId, VSLANGPROPNAME_CW_FIXED_RIGHT_COLUMN) < 80)
+ _LangSetPropertyInt32(sLangId, VSLANGPROPNAME_CW_FIXED_RIGHT_COLUMN, 80);
+# else
sTmp = LanguageSettings.getCommentWrapOptions(sLangId);
if (length(sTmp) > 10)
{
@@ -3581,6 +3618,7 @@ _command void kdev_load_settings()
//replace_def_data("def-comment-wrap-c",'0 1 0 1 1 80 0 0 80 0 80 0 80 0 0 0 '); - disabled
//replace_def_data("def-comment-wrap-c",'1 1 0 1 1 80 0 0 80 0 80 0 80 0 0 1 '); - enable block comment wrap.
}
+# endif
/* set the encoding to UTF-8 without any friggin useless signatures. */
idxExt = name_match('def-lang-for-ext-', 1, MISC_TYPE);
diff --git a/SlickEdit/kkeys.e b/SlickEdit/kkeys.e
index d447ef3..2614376 100644
--- a/SlickEdit/kkeys.e
+++ b/SlickEdit/kkeys.e
@@ -1,4 +1,4 @@
-/* $Id: kkeys.e 2558 2012-02-13 12:36:03Z bird $ */
+/* $Id: kkeys.e 3015 2016-11-29 10:14:56Z bird $ */
/** @file
* Bird's key additions to Visual Slickedit.
*/
@@ -299,6 +299,7 @@ _command kkeys_push_ref()
if (sProjTagFile != '')
{
+# if __VERSION__ < 21.0 /** @todo fix me? */
/* HACK ALERT: Make sure gtag_filelist_last_ext has the right value. */
_update_tag_filelist_ext(sLangId);
@@ -309,14 +310,17 @@ _command kkeys_push_ref()
/* HACK ALERT: Replace the tag file list for this language. */
gtag_filelist_ext._makeempty();
gtag_filelist_ext[0] = sProjTagFile;
- saved_gtag_filelist_cache_updated = true;
+ gtag_filelist_cache_updated = true;
+# endif
/* Do the reference searching. */
push_ref('-e ' :+ sLangId);
+# if __VERSION__ < 21.0
/* restore*/
gtag_filelist_cache_updated = saved_gtag_filelist_cache_updated;
gtag_filelist_ext = saved_gtag_filelist_ext;
+# endif
}
else
push_ref();
diff --git a/kBuild/tools/VAC308.kmk b/kBuild/tools/VAC308.kmk
index 8a12242..8899f19 100644
--- a/kBuild/tools/VAC308.kmk
+++ b/kBuild/tools/VAC308.kmk
@@ -1,4 +1,4 @@
-# $Id: VAC308.kmk 2750 2015-01-23 12:24:02Z bird $
+# $Id: VAC308.kmk 3023 2017-01-07 17:24:57Z bird $
## @file
# kBuild Tool Config - VisualAge for C++ v3.08.
#
@@ -66,7 +66,7 @@ ifneq ($(PATH_TOOL_VAC308),)
TOOL_VAC308_ENV_SETUP ?= $(REDIRECT) \
-E 'BEGINLIBPATH=$(PATH_TOOL_VAC308_DLL);$(BEGINLIBPATH)' \
-E 'DPATH=$(PATH_TOOL_VAC308_LOCALE);$(PATH_TOOL_VAC308_HELP);$(DPATH)' \
- -E 'LIB=' \
+ -E 'LIB=$1' \
-E 'INCLUDE=' \
--
TOOL_VAC308_CC ?= $(PATH_TOOL_VAC308_BIN)/icc$(HOSTSUFF_EXE)
@@ -79,7 +79,7 @@ else
# Pathless, relies on the environment.
TOOL_VAC308_PATHLESS := yes
TOOL_VAC308_ENV_SETUP ?= $(REDIRECT) \
- -E 'LIB=' \
+ -E 'LIB=$1' \
-E 'INCLUDE=' \
--
TOOL_VAC308_CC ?= icc$(HOSTSUFF_EXE)
@@ -176,7 +176,7 @@ TOOL_VAC308_LINK_PROGRAM_OUTPUT = $(outbase).map
TOOL_VAC308_LINK_PROGRAM_DEPEND = $(foreach lib,$(libs),$(if $(findstring $(lib),$(subst /,x,$(lib))),, $(lib))) $(othersrc)
TOOL_VAC308_LINK_PROGRAM_DEPORD =
define TOOL_VAC308_LINK_PROGRAM_CMDS
- $(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst $(SP),;,$(libpath))) \
+ $(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst ;$(SP),;,$(foreach one,$(libpath),$(one);))) \
$(TOOL_VAC308_LD) $(flags) -Fe$(out) -Fm$(outbase).map $(filter-out %.res,$(objs)) $(libs) $(othersrc)
$(if $(filter %.res,$(objs)), $(QUIET)$(call TOOL_VAC308_ENV_SETUP) \
$(TOOL_VAC308_RC) $(filter %.res,$(objs)) $(out))
@@ -186,7 +186,7 @@ TOOL_VAC308_LINK_DLL_OUTPUT = $(outbase).map
TOOL_VAC308_LINK_DLL_DEPEND = $(foreach lib,$(libs),$(if $(findstring $(lib),$(subst /,x,$(lib))),, $(lib))) $(othersrc)
TOOL_VAC308_LINK_DLL_DEPORD =
define TOOL_VAC308_LINK_DLL_CMDS
- $(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst $(SP),;,$(libpath))) \
+ $(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst ;$(SP),;,$(foreach one,$(libpath),$(one);))) \
$(TOOL_VAC308_LD) /B"/DLL" $(flags) -Fe$(out) -Fm$(outbase).map $(filter-out %.res,$(objs)) $(libs) $(othersrc)
$(if $(filter %.res,$(objs)), $(QUIET)$(call TOOL_VAC308_ENV_SETUP) \
$(TOOL_VAC308_RC) $(filter %.res,$(objs)) $(out))
@@ -196,7 +196,7 @@ TOOL_VAC308_LINK_SYSMOD_OUTPUT = $(outbase).map
TOOL_VAC308_LINK_SYSMOD_DEPEND = $(foreach lib,$(libs),$(if $(findstring $(lib),$(subst /,x,$(lib))),, $(lib))) $(othersrc)
TOOL_VAC308_LINK_SYSMOD_DEPORD =
define TOOL_VAC308_LINK_SYSMOD_CMDS
- $(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst $(SP),;,$(libpath))) \
+ $(QUIET)$(call TOOL_VAC308_ENV_SETUP,$(subst ;$(SP),;,$(foreach one,$(libpath),$(one);))) \
$(TOOL_VAC308_LD) $(flags) -Fe$(out) -Fm$(outbase).map $(filter-out %.res,$(objs)) $(libs) $(othersrc)
$(if $(filter %.res,$(objs)), $(QUIET)$(call TOOL_VAC308_ENV_SETUP) \
$(TOOL_VAC308_RC) $(filter %.res,$(objs)) $(out))
diff --git a/kBuild/units/qt5.kmk b/kBuild/units/qt5.kmk
index 67e5baf..e7e7af9 100644
--- a/kBuild/units/qt5.kmk
+++ b/kBuild/units/qt5.kmk
@@ -1,4 +1,4 @@
-# $Id: qt5.kmk 2980 2016-09-27 14:40:53Z bird $
+# $Id: qt5.kmk 3016 2017-01-04 12:51:53Z bird $
## @file
# Qt 5 unit.
#
@@ -113,15 +113,26 @@ ifndef PATH_SDK_QT5
ifneq ($(PATH_SDK_QT5),)
export PATH_SDK_QT5
- # Locate the include files.
+ # Locate the include files. Check for qglobalstatic.h (since 5.1) first.
ifeq ($(PATH_SDK_QT5_INC),)
- PATH_SDK_QT5_INC := $(patsubst %/QtCore/qglobal.h,%,$(firstword $(wildcard \
+ PATH_SDK_QT5_INC := $(patsubst %/QtCore/qglobalstatic.h,%,$(firstword $(wildcard \
+ $(foreach type,$(KBUILD_OSARCH_2_GNU_SYSTEM_TYPES.$(KBUILD_TARGET_DOT_ARCH)),/usr/include/$(type)/qt5/QtCore/qglobalstatic.h) \
+ $(PATH_SDK_QT5)/include/qt5/QtCore/qglobalstatic.h \
+ /usr/include/qt5/QtCore/qtglobalstatic.h \
+ /usr/local/include/qt5/QtCore/qtglobalstatic.h \
+ $(PATH_SDK_QT5)/include/QtCore/qglobalstatic.h \
+ /usr/include/qt/QtCore/qglobalstatic.h \
+ /usr/local/include/qt/QtCore/qglobalstatic.h \
+ )))
+ ifeq ($(PATH_SDK_QT5_INC),)
+ PATH_SDK_QT5_INC := $(patsubst %/QtCore/qglobal.h,%,$(firstword $(wildcard \
$(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 \
)))
+ endif
ifneq ($(PATH_SDK_QT5_INC),)
export PATH_SDK_QT5_INC
endif
diff --git a/src/Makefile.kmk b/src/Makefile.kmk
index a951bb3..d9aca68 100644
--- a/src/Makefile.kmk
+++ b/src/Makefile.kmk
@@ -1,4 +1,4 @@
-# $Id: Makefile.kmk 2846 2016-08-30 12:48:33Z bird $
+# $Id: Makefile.kmk 3013 2016-11-07 11:54:02Z bird $
## @file
# Sub-makefile for the source directory.
#
@@ -35,6 +35,7 @@ include $(PATH_SUB_CURRENT)/kObjCache/Makefile.kmk
include $(PATH_SUB_CURRENT)/misc/Makefile.kmk
ifeq ($(KBUILD_TARGET),win)
include $(PATH_SUB_CURRENT)/kLibTweaker/Makefile.kmk
+ include $(PATH_SUB_CURRENT)/kDeDup/Makefile.kmk
include $(PATH_SUB_CURRENT)/kWorker/Makefile.kmk
endif
diff --git a/src/Makefile.kmk b/src/kDeDup/Makefile.kmk
similarity index 53%
copy from src/Makefile.kmk
copy to src/kDeDup/Makefile.kmk
index a951bb3..5fe5213 100644
--- a/src/Makefile.kmk
+++ b/src/kDeDup/Makefile.kmk
@@ -1,10 +1,10 @@
-# $Id: Makefile.kmk 2846 2016-08-30 12:48:33Z bird $
+# $Id: Makefile.kmk 3012 2016-11-07 11:53:11Z bird $
## @file
-# Sub-makefile for the source directory.
+# Sub-makefile for kDeDup.
#
#
-# Copyright (c) 2004-2010 knut st. osmundsen <bird-kBuild-spamx at anduin.net>
+# Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx at anduin.net>
#
# This file is part of kBuild.
#
@@ -23,20 +23,13 @@
#
#
-SUB_DEPTH = ..
+SUB_DEPTH = ../..
include $(KBUILD_PATH)/subheader.kmk
-include $(PATH_SUB_CURRENT)/lib/Makefile.kmk
-include $(PATH_SUB_CURRENT)/sed/Makefile.kmk
-include $(PATH_SUB_CURRENT)/kmk/Makefile.kmk
-include $(PATH_SUB_CURRENT)/kash/Makefile.kmk
-include $(PATH_SUB_CURRENT)/kDepPre/Makefile.kmk
-include $(PATH_SUB_CURRENT)/kObjCache/Makefile.kmk
-include $(PATH_SUB_CURRENT)/misc/Makefile.kmk
-ifeq ($(KBUILD_TARGET),win)
- include $(PATH_SUB_CURRENT)/kLibTweaker/Makefile.kmk
- include $(PATH_SUB_CURRENT)/kWorker/Makefile.kmk
-endif
+PROGRAMS += kDeDup
+kDeDup_TEMPLATE = BIN
+kDeDup_LIBS = $(LIB_KUTIL)
+kDeDup_SOURCES = kDeDup.c
include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/kDeDup/kDeDup.c b/src/kDeDup/kDeDup.c
new file mode 100644
index 0000000..3faf47d
--- /dev/null
+++ b/src/kDeDup/kDeDup.c
@@ -0,0 +1,838 @@
+/* $Id: kDeDup.c 3012 2016-11-07 11:53:11Z bird $ */
+/** @file
+ * kDeDup - Utility that finds duplicate files, optionally hardlinking them.
+ */
+
+/*
+ * Copyright (c) 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 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
+ *
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include <k/kTypes.h>
+//#include <stdlib.h>
+#include <wchar.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "md5.h"
+//#include "sha2.h"
+
+#include "nt/ntstuff.h"
+#include "nt/ntstat.h"
+#include "nt/fts-nt.h"
+#include "nt/nthlp.h"
+#include "nt/ntunlink.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The key is made up of two cryptographic hashes, collisions are
+ * highly unlikely (once SHA2 is implemented).
+ */
+typedef struct KDUPFILENODEKEY
+{
+ /** The MD5 digest of the file. */
+ KU8 abMd5[16];
+ /** The 256-bit SHA-2 digest of the file. */
+ KU8 abSha2[32];
+} KDUPFILENODEKEY;
+/** Pointer to a file node.*/
+typedef struct KDUPFILENODE *PKDUPFILENODE;
+/**
+ * Hash tree node.
+ */
+typedef struct KDUPFILENODE
+{
+ /** The is made up of two hashes. */
+ KDUPFILENODEKEY mKey;
+ /** Left branch. */
+ PKDUPFILENODE mpLeft;
+ /** Right branch. */
+ PKDUPFILENODE mpRight;
+ /** Tree height (hmm). */
+ KU8 mHeight;
+
+ /** The inode number. */
+ KU64 uInode;
+ /** The device number. */
+ KU64 uDev;
+
+ /** Pointer to next hard linked node (same inode and udev values). */
+ PKDUPFILENODE pNextHardLink;
+ /** Pointer to next duplicate node. */
+ PKDUPFILENODE pNextDup;
+ /** Pointer to next duplicate node on the global list. */
+ PKDUPFILENODE pNextGlobalDup;
+
+ /** The path to this file (variable size). */
+ wchar_t wszPath[1];
+} KDUPFILENODE;
+
+/*#define KAVL_EQUAL_ALLOWED*/
+#define KAVL_CHECK_FOR_EQUAL_INSERT
+#define KAVL_MAX_STACK 32
+/*#define KAVL_RANGE */
+/*#define KAVL_OFFSET */
+/*#define KAVL_STD_KEY_COMP*/
+#define KAVLKEY KDUPFILENODEKEY
+#define KAVLNODE KDUPFILENODE
+#define KAVL_FN(name) kDupFileTree_ ## name
+#define KAVL_TYPE(prefix,name) prefix ## KDUPFILENODE ## name
+#define KAVL_INT(name) KDUPFILENODEINT ## name
+#define KAVL_DECL(rettype) static rettype
+#define KAVL_G(key1, key2) ( memcmp(&(key1), &(key2), sizeof(KDUPFILENODEKEY)) > 0 )
+#define KAVL_E(key1, key2) ( memcmp(&(key1), &(key2), sizeof(KDUPFILENODEKEY)) == 0 )
+#define KAVL_NE(key1, key2) ( memcmp(&(key1), &(key2), sizeof(KDUPFILENODEKEY)) != 0 )
+
+#define register
+#include <k/kAvlTmpl/kAvlBase.h>
+#include <k/kAvlTmpl/kAvlDoWithAll.h>
+//#include <k/kAvlTmpl/kAvlEnum.h> - busted
+#include <k/kAvlTmpl/kAvlGet.h>
+#include <k/kAvlTmpl/kAvlGetBestFit.h>
+#include <k/kAvlTmpl/kAvlGetWithParent.h>
+#include <k/kAvlTmpl/kAvlRemove2.h>
+#include <k/kAvlTmpl/kAvlRemoveBestFit.h>
+#include <k/kAvlTmpl/kAvlUndef.h>
+#undef register
+
+
+/** Pointer to a size tree node. */
+typedef struct KDUPSIZENODE *PKDUPSIZENODE;
+/**
+ * Size tree node.
+ */
+typedef struct KDUPSIZENODE
+{
+ /** The file size. */
+ KU64 mKey;
+ /** Left branch. */
+ PKDUPSIZENODE mpLeft;
+ /** Right branch. */
+ PKDUPSIZENODE mpRight;
+ /** Tree height (hmm). */
+ KU8 mHeight;
+ /** Number of files. */
+ KU32 cFiles;
+ /** Tree with same sized files.
+ * When cFiles is 1 the root node does not have hashes calculated yet. */
+ KDUPFILENODEROOT FileRoot;
+} KDUPSIZENODE;
+
+/*#define KAVL_EQUAL_ALLOWED*/
+#define KAVL_CHECK_FOR_EQUAL_INSERT
+#define KAVL_MAX_STACK 32
+/*#define KAVL_RANGE */
+/*#define KAVL_OFFSET */
+#define KAVL_STD_KEY_COMP
+#define KAVLKEY KU64
+#define KAVLNODE KDUPSIZENODE
+#define KAVL_FN(name) kDupSizeTree_ ## name
+#define KAVL_TYPE(prefix,name) prefix ## KDUPSIZENODE ## name
+#define KAVL_INT(name) KDUPSIZENODEINT ## name
+#define KAVL_DECL(rettype) static rettype
+
+#include <k/kAvlTmpl/kAvlBase.h>
+#include <k/kAvlTmpl/kAvlDoWithAll.h>
+//#include <k/kAvlTmpl/kAvlEnum.h> - busted
+#include <k/kAvlTmpl/kAvlGet.h>
+#include <k/kAvlTmpl/kAvlGetBestFit.h>
+#include <k/kAvlTmpl/kAvlGetWithParent.h>
+#include <k/kAvlTmpl/kAvlRemove2.h>
+#include <k/kAvlTmpl/kAvlRemoveBestFit.h>
+#include <k/kAvlTmpl/kAvlUndef.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The verbosity level. */
+static unsigned g_cVerbosity = 2;
+
+/** Whether to recurse into subdirectories. */
+static KBOOL g_fRecursive = K_FALSE;
+/** Whether to recurse into symlinked subdirectories. */
+static KBOOL g_fRecursiveViaSymlinks = K_FALSE;
+/** Whether to follow symbolicly linked files. */
+static KBOOL g_fFollowSymlinkedFiles = K_TRUE;
+
+/** Minimum file size to care about. */
+static KU64 g_cbMinFileSize = 1;
+/** Maximum file size to care about. */
+static KU64 g_cbMaxFileSize = KU64_MAX;
+
+/** The root of the size tree. */
+static KDUPSIZENODEROOT g_SizeRoot;
+
+/** Global list of duplicate file with duplicates.
+ * @remarks This only contains the files in the hash tree, not the ones on
+ * the KDUPFILENODE::pNextDup list. */
+static PKDUPFILENODE g_pDuplicateHead = NULL;
+/** Where to insert the next file with duplicates. */
+static PKDUPFILENODE *g_ppNextDuplicate = &g_pDuplicateHead;
+
+/** Number of files we're tracking. */
+static KU64 g_cFiles = 0;
+/** Number of hardlinked files or files entered more than once. */
+static KU64 g_cHardlinked = 0;
+/** Number of duplicates files (not hardlinked). */
+static KU64 g_cDuplicates = 0;
+/** Number of duplicates files that can be hardlinked. */
+static KU64 g_cDuplicatesSaved = 0;
+/** Size that could be saved if the duplicates were hardlinked. */
+static KU64 g_cbDuplicatesSaved = 0;
+
+
+
+/**
+ * Wrapper around malloc() that complains when out of memory.
+ *
+ * @returns Pointer to allocated memory
+ * @param cb The size of the memory to allocate.
+ */
+static void *kDupAlloc(KSIZE cb)
+{
+ void *pvRet = malloc(cb);
+ if (pvRet)
+ return pvRet;
+ fprintf(stderr, "kDeDup: error: out of memory! (cb=%#z)\n", cb);
+ return NULL;
+}
+
+/** Wrapper around free() for symmetry. */
+#define kDupFree(ptr) free(ptr)
+
+
+static void kDupHashFile(PKDUPFILENODE pFileNode, FTSENT *pFtsEnt)
+{
+ KSIZE i;
+ PKDUPFILENODE *ppHash;
+
+ /*
+ * Open the file.
+ */
+ HANDLE hFile;
+ if (pFtsEnt && pFtsEnt->fts_parent && pFtsEnt->fts_parent->fts_dirfd != INVALID_HANDLE_VALUE)
+ hFile = birdOpenFileExW(pFtsEnt->fts_parent->fts_dirfd, pFtsEnt->fts_wcsname,
+ FILE_READ_DATA | SYNCHRONIZE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ OBJ_CASE_INSENSITIVE);
+ else
+ hFile = birdOpenFileExW(NULL, pFileNode->wszPath,
+ FILE_READ_DATA | SYNCHRONIZE,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT,
+ OBJ_CASE_INSENSITIVE);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Init the hash calculation contexts.
+ */
+ struct MD5Context Md5Ctx;
+ //SHA256CONTEXT Sha256Ctx;
+ MD5Init(&Md5Ctx);
+ //Sha256Init(&Sha256Ctx);
+
+ /*
+ * Process the file chunk by chunk.
+ *
+ * We could complicate this by memory mapping medium sized files, but
+ * those kind of complications can wait.
+ */
+ for (;;)
+ {
+ static KU8 s_abBuffer[2*1024*1024];
+ MY_NTSTATUS rcNt;
+ MY_IO_STATUS_BLOCK Ios;
+ Ios.Information = -1;
+ Ios.u.Status = -1;
+ rcNt = g_pfnNtReadFile(hFile, NULL /*hEvent*/, NULL /*pfnApc*/, NULL /*pvApcCtx*/,
+ &Ios, s_abBuffer, sizeof(s_abBuffer), NULL /*poffFile*/, NULL /*puKey*/);
+ if (MY_NT_SUCCESS(rcNt))
+ {
+ MD5Update(&Md5Ctx, s_abBuffer, (unsigned)Ios.Information);
+ //SHA256Update(&Sha256Ctx, s_abBuffer, Ios.Information);
+ }
+ else if (rcNt != STATUS_END_OF_FILE)
+ {
+ fprintf(stderr, "kDeDup: warning: Error reading '%ls': %#x\n", pFileNode->wszPath, rcNt);
+ break;
+ }
+
+ /* Check for end of file. */
+ if ( rcNt == STATUS_END_OF_FILE
+ || Ios.Information < sizeof(s_abBuffer))
+ {
+ MD5Final(pFileNode->mKey.abMd5, &Md5Ctx);
+ //Sha256Final(pFileNode->mKey.abSha2, &Sha256Ctx);
+
+ birdCloseFile(hFile);
+ return;
+ }
+ }
+
+ birdCloseFile(hFile);
+ }
+ else
+ fprintf(stderr, "kDeDup: warning: Failed to open '%ls': %s (%d)\n", pFileNode->wszPath, strerror(errno), errno);
+
+ /*
+ * Hashing failed. We fake the digests by repeating the node pointer value
+ * again and again, holding a collision with both SHA2 and MD5 with similar
+ * digest pattern for highly unlikely.
+ */
+ ppHash = (PKDUPFILENODE *)&pFileNode->mKey;
+ i = sizeof(pFileNode->mKey) / sizeof(*ppHash);
+ while (i-- > 0)
+ *ppHash++ = pFileNode;
+}
+
+
+/**
+ * Deal with one file, adding it to the tree if it matches the criteria.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param pFtsEnt The FTS entry for the file.
+ */
+static int kDupDoFile(FTSENT *pFtsEnt)
+{
+ KU64 cbFile;
+
+ if (g_cVerbosity >= 2)
+ printf("debug: kDupDoFile(%ls)\n", pFtsEnt->fts_wcsaccpath);
+
+ /*
+ * Check that it's within the size range.
+ */
+ cbFile = pFtsEnt->fts_stat.st_size;
+ if ( cbFile >= g_cbMinFileSize
+ && cbFile <= g_cbMaxFileSize)
+ {
+ /*
+ * Start out treating this like a unique file with a unique size, i.e.
+ * allocate all the structures we might possibly need.
+ */
+ size_t cbAccessPath = (wcslen(pFtsEnt->fts_wcsaccpath) + 1) * sizeof(wchar_t);
+ PKDUPFILENODE pFileNode = (PKDUPFILENODE)kDupAlloc(sizeof(*pFileNode) + cbAccessPath);
+ PKDUPSIZENODE pSizeNode = (PKDUPSIZENODE)kDupAlloc(sizeof(*pSizeNode));
+ if (!pFileNode || !pSizeNode)
+ return 3;
+ g_cFiles++;
+
+ memset(&pFileNode->mKey, 0, sizeof(pFileNode->mKey));
+ pFileNode->pNextHardLink = NULL;
+ pFileNode->pNextDup = NULL;
+ pFileNode->pNextGlobalDup = NULL;
+ pFileNode->uDev = pFtsEnt->fts_stat.st_dev;
+ pFileNode->uInode = pFtsEnt->fts_stat.st_ino;
+ memcpy(pFileNode->wszPath, pFtsEnt->fts_wcsaccpath, cbAccessPath);
+
+ pSizeNode->mKey = cbFile;
+ pSizeNode->cFiles = 1;
+ kDupFileTree_Init(&pSizeNode->FileRoot);
+ kDupFileTree_Insert(&pSizeNode->FileRoot, pFileNode);
+
+ /*
+ * Try insert it.
+ */
+ if (kDupSizeTree_Insert(&g_SizeRoot, pSizeNode))
+ { /* unique size, nothing more to do for now. */ }
+ else
+ {
+ /*
+ * More than one file with this size. We may need to hash the
+ * hash the file we encountered with this size, if this is the
+ * second one. In that case we should check for hardlinked or
+ * double entering of the file first as well.
+ */
+ kDupFree(pSizeNode);
+ pSizeNode = kDupSizeTree_Get(&g_SizeRoot, cbFile);
+ if (pSizeNode->cFiles == 1)
+ {
+ PKDUPFILENODE pFirstFileNode = pSizeNode->FileRoot.mpRoot;
+ if ( pFirstFileNode->uInode == pFileNode->uInode
+ && pFileNode->uInode != 0
+ && pFirstFileNode->uDev == pFileNode->uDev)
+ {
+ pFileNode->pNextHardLink = pFirstFileNode->pNextHardLink;
+ pFirstFileNode->pNextHardLink = pFileNode;
+ if (g_cVerbosity >= 1)
+ printf("Found hardlinked: '%ls' -> '%ls' (ino:%#" KX64_PRI " dev:%#" KX64_PRI ")\n",
+ pFileNode->wszPath, pFirstFileNode->wszPath, pFileNode->uInode, pFileNode->uDev);
+ g_cHardlinked += 1;
+ return 0;
+ }
+
+ kDupHashFile(pFirstFileNode, NULL);
+ }
+ kDupHashFile(pFileNode, pFtsEnt);
+
+ if (kDupFileTree_Insert(&pSizeNode->FileRoot, pFileNode))
+ { /* great, unique content */ }
+ else
+ {
+ /*
+ * Duplicate content. Could be hardlinked or a duplicate entry.
+ */
+ PKDUPFILENODE pDupFileNode = kDupFileTree_Get(&pSizeNode->FileRoot, pFileNode->mKey);
+ if ( pDupFileNode->uInode == pFileNode->uInode
+ && pFileNode->uInode != 0
+ && pDupFileNode->uDev == pFileNode->uDev)
+ {
+ pFileNode->pNextHardLink = pDupFileNode->pNextHardLink;
+ pDupFileNode->pNextHardLink = pFileNode;
+ if (g_cVerbosity >= 1)
+ printf("Found hardlinked: '%ls' -> '%ls' (ino:%#" KX64_PRI " dev:%#" KX64_PRI ")\n",
+ pFileNode->wszPath, pDupFileNode->wszPath, pFileNode->uInode, pFileNode->uDev);
+ g_cHardlinked += 1;
+ }
+ else
+ {
+ KBOOL fDifferentDev;
+
+ /* Genuinly duplicate (or inode numbers are busted). */
+ if (!pDupFileNode->pNextDup)
+ {
+ *g_ppNextDuplicate = pDupFileNode;
+ g_ppNextDuplicate = &pDupFileNode->pNextGlobalDup;
+ }
+
+ /* The list is sorted by device to better facility hardlinking later. */
+ while ( (fDifferentDev = pDupFileNode->uDev != pFileNode->uDev)
+ && pDupFileNode->pNextDup)
+ pDupFileNode = pDupFileNode->pNextDup;
+
+ pFileNode->pNextDup = pDupFileNode->pNextDup;
+ pDupFileNode->pNextDup = pFileNode;
+
+ g_cDuplicates += 1;
+ if (!fDifferentDev)
+ {
+ g_cDuplicatesSaved += 1;
+ g_cbDuplicatesSaved += pFtsEnt->fts_stat.st_blocks * BIRD_STAT_BLOCK_SIZE;
+ if (g_cVerbosity >= 1)
+ printf("Found duplicate: '%ls' <-> '%ls'\n", pFileNode->wszPath, pDupFileNode->wszPath);
+ }
+ else if (g_cVerbosity >= 1)
+ printf("Found duplicate: '%ls' <-> '%ls' (devices differ).\n", pFileNode->wszPath, pDupFileNode->wszPath);
+ }
+ }
+ }
+ }
+ else if (g_cVerbosity >= 1)
+ printf("Skipping '%ls' because %" KU64_PRI " bytes is outside the size range.\n",
+ pFtsEnt->fts_wcsaccpath, cbFile);
+ return 0;
+}
+
+
+/**
+ * Process the non-option arguments, creating the file tree.
+ *
+ * @returns 0 on success, non-zero on failure.
+ * @param papwszFtsArgs The input in argv style.
+ * @param fFtsOptions The FTS options.
+ */
+static int kDupReadAll(wchar_t **papwszFtsArgs, unsigned fFtsOptions)
+{
+ int rcExit = 0;
+ FTS *pFts = nt_fts_openw(papwszFtsArgs, fFtsOptions, NULL /*pfnCompare*/);
+ if (pFts != NULL)
+ {
+ for (;;)
+ {
+ FTSENT *pFtsEnt = nt_fts_read(pFts);
+ if (pFtsEnt)
+ {
+ switch (pFtsEnt->fts_info)
+ {
+ case FTS_F:
+ rcExit = kDupDoFile(pFtsEnt);
+ if (rcExit == 0)
+ continue;
+ break;
+
+ case FTS_D:
+ if ( g_fRecursive
+ || pFtsEnt->fts_level == FTS_ROOTLEVEL) /* enumerate dirs on the command line */
+ continue;
+ rcExit = nt_fts_set(pFts, pFtsEnt, FTS_SKIP);
+ if (rcExit == 0)
+ continue;
+ fprintf(stderr, "kDeDup: internal error: nt_fts_set failed!\n");
+ rcExit = 1;
+ break;
+
+ case FTS_DP:
+ /* nothing to do here. */
+ break;
+
+ case FTS_SL:
+ /* The nice thing on windows is that we already know whether it's a
+ directory or file when encountering the symbolic link. */
+ if ( (pFtsEnt->fts_stat.st_isdirsymlink ? g_fRecursiveViaSymlinks : g_fFollowSymlinkedFiles)
+ && pFtsEnt->fts_number == 0)
+ {
+ pFtsEnt->fts_number++;
+ rcExit = nt_fts_set(pFts, pFtsEnt, FTS_FOLLOW);
+ if (rcExit == 0)
+ continue;
+ fprintf(stderr, "kDeDup: internal error: nt_fts_set failed!\n");
+ rcExit = 1;
+ }
+ break;
+
+ case FTS_DC:
+ fprintf(stderr, "kDeDup: warning: Ignoring cycle '%ls'!\n", pFtsEnt->fts_wcsaccpath);
+ continue;
+
+ case FTS_NS:
+ fprintf(stderr, "kDeDup: warning: Failed to stat '%ls': %s (%d)\n",
+ pFtsEnt->fts_wcsaccpath, strerror(pFtsEnt->fts_errno), pFtsEnt->fts_errno);
+ continue;
+
+ case FTS_DNR:
+ fprintf(stderr, "kDeDup: error: Error reading directory '%ls': %s (%d)\n",
+ pFtsEnt->fts_wcsaccpath, strerror(pFtsEnt->fts_errno), pFtsEnt->fts_errno);
+ rcExit = 1;
+ break;
+
+ case FTS_ERR:
+ fprintf(stderr, "kDeDup: error: Error on '%ls': %s (%d)\n",
+ pFtsEnt->fts_wcsaccpath, strerror(pFtsEnt->fts_errno), pFtsEnt->fts_errno);
+ rcExit = 1;
+ break;
+
+
+ /* ignore */
+ case FTS_SLNONE:
+ case FTS_DEFAULT:
+ break;
+
+ /* Not supposed to get here. */
+ default:
+ fprintf(stderr, "kDeDup: internal error: fts_info=%d - '%ls'\n",
+ pFtsEnt->fts_info, pFtsEnt->fts_wcsaccpath);
+ rcExit = 1;
+ break;
+ }
+ }
+ else if (errno == 0)
+ break;
+ else
+ {
+ fprintf(stderr, "kDeDup: error: nt_fts_read failed: %s (%d)\n", strerror(errno), errno);
+ rcExit = 1;
+ break;
+ }
+ }
+
+ if (nt_fts_close(pFts) != 0)
+ {
+ fprintf(stderr, "kDeDup: error: nt_fts_close failed: %s (%d)\n", strerror(errno), errno);
+ rcExit = 1;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "kDeDup: error: nt_fts_openw failed: %s (%d)\n", strerror(errno), errno);
+ rcExit = 1;
+ }
+
+ return rcExit;
+}
+
+
+/**
+ * Hardlink duplicates.
+ */
+static int kDupHardlinkDuplicates(void)
+{
+ int rcExit = 0;
+ PKDUPFILENODE pFileNode;
+ for (pFileNode = g_pDuplicateHead; pFileNode != NULL; pFileNode = pFileNode->pNextGlobalDup)
+ {
+ PKDUPFILENODE pTargetFile = pFileNode;
+ PKDUPFILENODE pDupFile;
+ for (pDupFile = pFileNode->pNextDup; pDupFile != NULL; pDupFile = pDupFile->pNextDup)
+ {
+ /*
+ * Can only hard link if the files are on the same device.
+ */
+ if (pDupFile->uDev == pTargetFile->uDev)
+ {
+ /** @todo compare the files? */
+ if (1)
+ {
+ /*
+ * Start by renaming the orinal file before we try create the hard link.
+ */
+ static const wchar_t s_wszBackupSuffix[] = L".kDepBackup";
+ wchar_t wszBackup[0x4000];
+ size_t cwcPath = wcslen(pDupFile->wszPath);
+ if (cwcPath + sizeof(s_wszBackupSuffix) / sizeof(wchar_t) < K_ELEMENTS(wszBackup))
+ {
+ memcpy(wszBackup, pDupFile->wszPath, cwcPath * sizeof(wchar_t));
+ memcpy(&wszBackup[cwcPath], s_wszBackupSuffix, sizeof(s_wszBackupSuffix));
+ if (MoveFileW(pDupFile->wszPath, wszBackup))
+ {
+ if (CreateHardLinkW(pDupFile->wszPath, pTargetFile->wszPath, NULL))
+ {
+ if (birdUnlinkForcedW(wszBackup) == 0)
+ {
+ if (g_cVerbosity >= 1)
+ printf("Hardlinked '%ls' to '%ls'.\n", pDupFile->wszPath, pTargetFile->wszPath);
+ }
+ else
+ {
+ fprintf(stderr, "kDeDup: fatal: failed to delete '%ls' after hardlinking: %s (%d)\n",
+ wszBackup, strerror(errno), errno);
+ return 8;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "kDeDup: error: failed to hard link '%ls' to '%ls': %u\n",
+ pDupFile->wszPath, wszBackup, GetLastError());
+ if (!MoveFileW(wszBackup, pDupFile->wszPath))
+ {
+ fprintf(stderr, "kDeDup: fatal: Restore back '%ls' to '%ls' after hardlinking faild: %u\n",
+ wszBackup, pDupFile->wszPath, GetLastError());
+ return 8;
+ }
+ rcExit = 1;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "kDeDup: error: failed to rename '%ls' to '%ls': %u\n",
+ pDupFile->wszPath, wszBackup, GetLastError());
+ rcExit = 1;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "kDeDup: error: too long backup path: '%ls'\n", pDupFile->wszPath);
+ rcExit = 1;
+ }
+ }
+ }
+ /*
+ * Since the list is sorted by uDev, we now change the target file.
+ */
+ else
+ pTargetFile = pDupFile;
+ }
+ }
+ return rcExit;
+}
+
+
+static int usage(const char *pszName, FILE *pOut)
+{
+ fprintf(pOut,
+ "usage: %s [options] <path1> [path2 [..]]\n"
+ "usage: %s <-V|--version>\n"
+ "usage: %s <-h|--help>\n"
+ , pszName, pszName, pszName);
+ fprintf(pOut,
+ "\n"
+ "Options:\n"
+ " -H, --dereference-command-line, --no-dereference-command-line\n"
+ " Follow symbolic links on the command line.\n"
+ " -L, --dereference\n"
+ " Follow symbolic links while scanning directories.\n"
+ " -P, --no-dereference\n"
+ " Do not follow symbolic links while scanning directories.\n"
+ " -r, --recursive\n"
+ " Recurse into subdirectories, but do not follow links to them.\n"
+ " -R, --recursive-dereference\n"
+ " Same as -r, but also follow into symlinked subdirectories.\n"
+ " -x, --one-file-system\n"
+ " Do not consider other file system (volumes), either down thru a\n"
+ " mount point or via a symbolic link to a directory.\n"
+ " --no-one-file-system, --cross-file-systems\n"
+ " Reverses the effect of --one-file-system.\n"
+ );
+ return 0;
+}
+
+
+int wmain(int argc, wchar_t **argv)
+{
+ int rcExit;
+
+ /*
+ * Process parameters. Position.
+ */
+ wchar_t **papwszFtsArgs = (wchar_t **)calloc(argc + 1, sizeof(wchar_t *));
+ unsigned cFtsArgs = 0;
+ unsigned fFtsOptions = FTS_NOCHDIR | FTS_NO_ANSI;
+ KBOOL fEndOfOptions = K_FALSE;
+ KBOOL fHardlinkDups = K_FALSE;
+ int i;
+ for (i = 1; i < argc; i++)
+ {
+ wchar_t *pwszArg = argv[i];
+ if ( *pwszArg == '-'
+ && !fEndOfOptions)
+ {
+ wchar_t wcOpt = *++pwszArg;
+ pwszArg++;
+ if (wcOpt == '-')
+ {
+ /* Translate long options. */
+ if (wcscmp(pwszArg, L"help") == 0)
+ wcOpt = 'h';
+ else if (wcscmp(pwszArg, L"version") == 0)
+ wcOpt = 'V';
+ else if (wcscmp(pwszArg, L"recursive") == 0)
+ wcOpt = 'r';
+ else if (wcscmp(pwszArg, L"dereference-recursive") == 0)
+ wcOpt = 'R';
+ else if (wcscmp(pwszArg, L"dereference") == 0)
+ wcOpt = 'L';
+ else if (wcscmp(pwszArg, L"dereference-command-line") == 0)
+ wcOpt = 'H';
+ else if (wcscmp(pwszArg, L"one-file-system") == 0)
+ wcOpt = 'x';
+ /* Process long options. */
+ else if (*pwszArg == '\0')
+ {
+ fEndOfOptions = K_TRUE;
+ continue;
+ }
+ else if (wcscmp(pwszArg, L"no-recursive") == 0)
+ {
+ g_fRecursive = g_fRecursiveViaSymlinks = K_FALSE;
+ continue;
+ }
+ else if (wcscmp(pwszArg, L"no-dereference-command-line") == 0)
+ {
+ fFtsOptions &= ~FTS_COMFOLLOW;
+ continue;
+ }
+ else if ( wcscmp(pwszArg, L"no-one-file-system") == 0
+ || wcscmp(pwszArg, L"cross-file-systems") == 0)
+ {
+ fFtsOptions &= ~FTS_XDEV;
+ continue;
+ }
+ else if (wcscmp(pwszArg, L"hardlink-duplicates") == 0)
+ {
+ fHardlinkDups = K_TRUE;
+ continue;
+ }
+ else
+ {
+ fprintf(stderr, "kDeDup: syntax error: Unknown option '--%ls'\n", pwszArg);
+ return 2;
+ }
+ }
+
+ /* Process one or more short options. */
+ do
+ {
+ switch (wcOpt)
+ {
+ case 'r': /* --recursive */
+ g_fRecursive = K_TRUE;
+ break;
+
+ case 'R': /* --dereference-recursive */
+ g_fRecursive = g_fRecursiveViaSymlinks = K_TRUE;
+ break;
+
+ case 'H': /* --dereference-command-line */
+ fFtsOptions |= FTS_COMFOLLOW;
+ break;
+
+ case 'L': /* --dereference*/
+ g_fFollowSymlinkedFiles = K_TRUE;
+ break;
+
+ case 'x': /* --one-file-system*/
+ fFtsOptions |= FTS_XDEV;
+ break;
+
+ case 'q':
+ g_cVerbosity = 0;
+ break;
+
+ case 'v':
+ g_cVerbosity++;
+ break;
+
+
+ case 'h':
+ case '?':
+ return usage("kDeDup", stdout);
+
+ case 'V':
+ printf("0.0.1\n");
+ return 0;
+
+ default:
+ fprintf(stderr, "kDeDup: syntax error: Unknown option '-%lc'\n", wcOpt);
+ return 2;
+ }
+
+ wcOpt = *pwszArg++;
+ } while (wcOpt != '\0');
+ }
+ else
+ {
+ /*
+ * Append non-option arguments to the FTS argument vector.
+ */
+ papwszFtsArgs[cFtsArgs] = pwszArg;
+ cFtsArgs++;
+ }
+ }
+
+ /*
+ * Do the FTS processing.
+ */
+ kDupSizeTree_Init(&g_SizeRoot);
+ rcExit = kDupReadAll(papwszFtsArgs, fFtsOptions);
+ if (rcExit == 0)
+ {
+ /*
+ * Display the result.
+ */
+ printf("Found %" KU64_PRI " duplicate files, out which %" KU64_PRI " can be hardlinked saving %" KU64_PRI " bytes\n",
+ g_cDuplicates, g_cDuplicatesSaved, g_cbDuplicatesSaved);
+
+ if (fHardlinkDups)
+ rcExit = kDupHardlinkDuplicates();
+ }
+
+ return rcExit;
+}
+
diff --git a/src/kmk/dir-nt-bird.c b/src/kmk/dir-nt-bird.c
index 3724833..69d5369 100644
--- a/src/kmk/dir-nt-bird.c
+++ b/src/kmk/dir-nt-bird.c
@@ -1,4 +1,4 @@
-/* $Id: dir-nt-bird.c 2948 2016-09-20 15:36:07Z bird $ */
+/* $Id: dir-nt-bird.c 3024 2017-01-07 17:46:13Z bird $ */
/** @file
* Reimplementation of dir.c for NT using kFsCache.
*
@@ -250,8 +250,11 @@ static struct dirent *dir_glob_readdir(__ptr_t pvDir)
/* Don't return missing objects. */
if (pEntry->bObjType != KFSOBJ_TYPE_MISSING)
{
- /* Copy the name that fits. If neither fits, skip the name. */
- if (pEntry->cchName < sizeof(pDir->DirEnt.d_name))
+ /* Copy the name that fits, trying to avoid names with spaces.
+ If neither fits, skip the name. */
+ if ( pEntry->cchName < sizeof(pDir->DirEnt.d_name)
+ && ( pEntry->pszShortName == pEntry->pszName
+ || memchr(pEntry->pszName, ' ', pEntry->cchName) == NULL))
{
pDir->DirEnt.d_namlen = pEntry->cchName;
memcpy(pDir->DirEnt.d_name, pEntry->pszName, pEntry->cchName + 1);
@@ -455,7 +458,8 @@ void print_dir_data_base(void)
}
-/* duplicated in kWorker.c */
+/* duplicated in kWorker.c
+ * Note! Tries avoid to produce a result with spaces since they aren't supported by makefiles. */
void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
{
KFSLOOKUPERROR enmError;
@@ -470,7 +474,7 @@ void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
{
PKFSDIR pAncestor;
- pszFull[off + pPathObj->cchName] = '\0';
+ pszFull[offEnd] = '\0';
memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
@@ -479,9 +483,38 @@ void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
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);
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ if ( pAncestor->Obj.pszName == pAncestor->Obj.pszShortName
+ || memchr(pAncestor->Obj.pszName, ' ', pAncestor->Obj.cchName) == NULL)
+#endif
+ {
+ off -= pAncestor->Obj.cchName;
+ kHlpAssert(pAncestor->Obj.cchParent == off);
+ memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
+ }
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+ else
+ {
+ /*
+ * The long name constains a space, so use the alternative name instead.
+ * Most likely the alternative name differs in length, usually it's shorter,
+ * so we have to shift the part of the path we've already assembled
+ * accordingly.
+ */
+ KSSIZE cchDelta = (KSSIZE)pAncestor->Obj.cchShortName - (KSSIZE)pAncestor->Obj.cchName;
+ if (cchDelta != 0)
+ {
+ if ((KSIZE)(offEnd + cchDelta) >= cbFull)
+ goto l_fallback;
+ memmove(&pszFull[off + cchDelta], &pszFull[off], offEnd + 1 - off);
+ off += cchDelta;
+ offEnd += cchDelta;
+ }
+ off -= pAncestor->Obj.cchShortName;
+ kHlpAssert(pAncestor->Obj.cchParent == off);
+ memcpy(&pszFull[off], pAncestor->Obj.pszShortName, pAncestor->Obj.cchShortName);
+ }
+#endif
}
kFsCacheObjRelease(g_pFsCache, pPathObj);
return;
@@ -491,6 +524,7 @@ void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
{
if ((size_t)pPathObj->cchName + 1 < cbFull)
{
+ /* Assume no spaces here. */
memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
pszFull[pPathObj->cchName] = '/';
pszFull[pPathObj->cchName + 1] = '\0';
@@ -501,6 +535,9 @@ void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
}
/* do fallback. */
+#ifdef KFSCACHE_CFG_SHORT_NAMES
+l_fallback:
+#endif
kHlpAssertFailed();
kFsCacheObjRelease(g_pFsCache, pPathObj);
}
diff --git a/src/lib/nt/fts-nt.c b/src/lib/nt/fts-nt.c
index 1b815a1..d86b6e2 100644
--- a/src/lib/nt/fts-nt.c
+++ b/src/lib/nt/fts-nt.c
@@ -1,4 +1,4 @@
-/* $Id: fts-nt.c 2992 2016-11-01 22:06:08Z bird $ */
+/* $Id: fts-nt.c 3009 2016-11-07 02:21:59Z bird $ */
/** @file
* Source for the NT port of BSD fts.c.
*
@@ -84,14 +84,20 @@ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
#include <assert.h>
#include "nthlp.h"
#include "ntdir.h"
+#include <stdio.h>//debug
-static FTSENT *fts_alloc(FTS *, char *, size_t);
+static FTSENT *fts_alloc(FTS *sp, char const *name, size_t namelen, wchar_t const *wcsname, size_t cwcname);
+static FTSENT *fts_alloc_ansi(FTS *sp, char const *name, size_t namelen);
+static FTSENT *fts_alloc_utf16(FTS *sp, wchar_t const *wcsname, size_t cwcname);
+static void nt_fts_free_alloc_cache(FTS *sp);
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 size_t fts_maxarglenw(wchar_t * const *);
static void fts_padjust(FTS *, FTSENT *);
-static int fts_palloc(FTS *, size_t);
+static void fts_padjustw(FTS *, FTSENT *);
+static int fts_palloc(FTS *, size_t, 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 *);
@@ -111,9 +117,18 @@ static int fts_process_stats(FTSENT *, BirdStat_T const *);
#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 */
+/** Enables BirdDir_T reuse. (Saves malloc and free calls.) */
+#define FTS_WITH_DIRHANDLE_REUSE
+/** Enables allocation statistics. */
+//#define FTS_WITH_STATISTICS
+/** Enables FTSENT allocation cache. */
+#define FTS_WITH_ALLOC_CACHE
+/** Number of size buckets for the FTSENT allocation cache. */
+#define FTS_NUM_FREE_BUCKETS 64
+/** Shift for converting size to free bucket index. */
+#define FTS_FREE_BUCKET_SHIFT 4
+/** The FTSENT allocation alignment. */
+#define FTS_ALIGN_FTSENT (1U << FTS_FREE_BUCKET_SHIFT)
/*
* Internal representation of an FTS, including extra implementation
@@ -122,11 +137,30 @@ static int fts_process_stats(FTSENT *, BirdStat_T const *);
*/
struct _fts_private {
FTS ftsp_fts;
+#ifdef FTS_WITH_DIRHANDLE_REUSE
+ /** Statically allocate directory handle. */
+ BirdDir_T dirhandle;
+#endif
+#ifdef FTS_WITH_ALLOC_CACHE
+ /** Number of free entries in the above buckets. */
+ size_t numfree;
+# ifdef FTS_WITH_STATISTICS
+ size_t allocs;
+ size_t hits;
+ size_t misses;
+# endif
+ /** Free FTSENT buckets (by size).
+ * This is to avoid hitting the heap, which is a little sluggish on windows. */
+ struct
+ {
+ FTSENT *head;
+ } freebuckets[FTS_NUM_FREE_BUCKETS];
+#endif
};
-FTS * FTSCALL
-nt_fts_open(char * const *argv, int options,
+static FTS * FTSCALL
+nt_fts_open_common(char * const *argv, wchar_t * const *wcsargv, int options,
int (*compar)(const FTSENT * const *, const FTSENT * const *))
{
struct _fts_private *priv;
@@ -135,6 +169,8 @@ nt_fts_open(char * const *argv, int options,
FTSENT *parent, *tmp;
size_t len, nitems;
+ birdResolveImports();
+
/* Options check. */
if (options & ~FTS_OPTIONMASK) {
errno = EINVAL;
@@ -142,7 +178,7 @@ nt_fts_open(char * const *argv, int options,
}
/* fts_open() requires at least one path */
- if (*argv == NULL) {
+ if (wcsargv ? *wcsargv == NULL : *argv == NULL) {
errno = EINVAL;
return (NULL);
}
@@ -162,43 +198,70 @@ nt_fts_open(char * const *argv, int options,
* 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)))
+ if (fts_palloc(sp, MAX(argv ? fts_maxarglen(argv) : 1, MAXPATHLEN),
+ MAX(wcsargv ? fts_maxarglenw(wcsargv) : 1, MAXPATHLEN)) )
goto mem1;
/* Allocate/initialize root's parent. */
- if ((parent = fts_alloc(sp, "", 0)) == NULL)
+ if ((parent = fts_alloc(sp, NULL, 0, NULL, 0)) == NULL)
goto mem2;
parent->fts_level = FTS_ROOTPARENTLEVEL;
/* Allocate/initialize root(s). */
- for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
+ for (root = NULL, nitems = 0; wcsargv ? *wcsargv != NULL : *argv != NULL; ++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);
+ wchar_t *wcslash;
+
+ if (wcsargv) {
+ len = wcslen(*wcsargv);
+ if (len == 2 && wcsargv[0][1] == ':') {
+ wchar_t wcsdrive[4];
+ wcsdrive[0] = wcsargv[0][0];
+ wcsdrive[1] = ':';
+ wcsdrive[2] = '.';
+ wcsdrive[3] = '\0';
+ p = fts_alloc_utf16(sp, wcsdrive, 3);
+ } else {
+ p = fts_alloc_utf16(sp, *wcsargv, len);
+ }
+ wcsargv++;
} else {
- p = fts_alloc(sp, *argv, len);
+ len = strlen(*argv);
+ if (len == 2 && argv[0][1] == ':') {
+ char szdrive[4];
+ szdrive[0] = argv[0][0];
+ szdrive[1] = ':';
+ szdrive[2] = '.';
+ szdrive[3] = '\0';
+ p = fts_alloc_ansi(sp, szdrive, 3);
+ } else {
+ p = fts_alloc_ansi(sp, *argv, len);
+ }
+ argv++;
}
-#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, '\\');
+
+ wcslash = wcschr(p->fts_wcsname, '\\');
+ while (wcslash != NULL) {
+ *wcslash++ = '/';
+ wcslash = wcschr(p->fts_wcsname, '\\');
+ }
+
+ if (p->fts_name) {
+ char *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_wcsaccpath = p->fts_wcsname;
p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), INVALID_HANDLE_VALUE);
/* Command-line "." and ".." are real directories. */
@@ -230,26 +293,49 @@ nt_fts_open(char * const *argv, int options,
* 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)
+ if ((sp->fts_cur = fts_alloc(sp, NULL, 0, NULL, 0)) == NULL)
goto mem3;
sp->fts_cur->fts_link = root;
sp->fts_cur->fts_info = FTS_INIT;
return (sp);
-mem3: fts_lfree(root);
+mem3:
+ fts_lfree(root);
free(parent);
-mem2: free(sp->fts_path);
-mem1: free(sp);
+mem2:
+ free(sp->fts_path);
+ free(sp->fts_wcspath);
+mem1:
+ free(sp);
return (NULL);
}
+FTS * FTSCALL
+nt_fts_open(char * const *argv, int options,
+ int (*compar)(const FTSENT * const *, const FTSENT * const *))
+{
+ return nt_fts_open_common(argv, NULL, options, compar);
+}
+
+
+FTS * FTSCALL
+nt_fts_openw(wchar_t * const *argv, int options,
+ int (*compar)(const FTSENT * const *, const FTSENT * const *))
+{
+ return nt_fts_open_common(NULL, argv, options, compar);
+}
+
+
+/**
+ * Called by fts_read for FTS_ROOTLEVEL entries only.
+ */
static void
fts_load(FTS *sp, FTSENT *p)
{
size_t len;
- char *cp;
+ wchar_t *pwc;
/*
* Load the stream structure for the next traversal. Since we don't
@@ -258,17 +344,33 @@ fts_load(FTS *sp, FTSENT *p)
* 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;
+ if (!(sp->fts_options & FTS_NO_ANSI)) {
+ char *cp;
+ len = p->fts_pathlen = p->fts_namelen;
+ memmove(sp->fts_path, p->fts_name, len + 1);
+ cp = strrchr(p->fts_name, '/');
+ if (cp != NULL && (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;
+ }
+
+ len = p->fts_cwcpath = p->fts_cwcname;
+ memmove(sp->fts_wcspath, p->fts_wcsname, (len + 1) * sizeof(wchar_t));
+ pwc = wcsrchr(p->fts_wcsname, '/');
+ if (pwc != NULL && (pwc != p->fts_wcsname || pwc[1])) {
+ len = wcslen(++pwc);
+ memmove(p->fts_wcsname, pwc, (len + 1) * sizeof(wchar_t));
+ p->fts_cwcname = len;
+ }
+ p->fts_wcsaccpath = p->fts_wcspath = sp->fts_wcspath;
+
sp->fts_dev = p->fts_dev;
}
+
int FTSCALL
nt_fts_close(FTS *sp)
{
@@ -295,52 +397,99 @@ nt_fts_close(FTS *sp)
if (sp->fts_array)
free(sp->fts_array);
free(sp->fts_path);
+ free(sp->fts_wcspath);
+#ifdef FTS_WITH_ALLOC_CACHE
+# ifdef FTS_WITH_STATISTICS
+ {
+ struct _fts_private *priv = (struct _fts_private *)sp;
+ fprintf(stderr, "numfree=%u allocs=%u hits=%u (%uppt) misses=%u (%uppt) other=%u\n",
+ priv->numfree, priv->allocs,
+ priv->hits, (unsigned)((double)priv->hits * 1000.0 / priv->allocs),
+ priv->misses, (unsigned)((double)priv->misses * 1000.0 / priv->allocs),
+ priv->allocs - priv->misses - priv->hits);
+ }
+# endif
+#endif
+ nt_fts_free_alloc_cache(sp);
+#ifdef FTS_WITH_DIRHANDLE_REUSE
+ birdDirClose(&((struct _fts_private *)sp)->dirhandle);
+#endif
/* 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)
+/**
+ * Frees a FTSENT structure by way of the allocation cache.
+ */
static void
-fts_free_entry(FTSENT *tmp)
+fts_free_entry(FTS *sp, FTSENT *tmp)
{
- if (tmp != NULL) {
- if (tmp->fts_dirfd != INVALID_HANDLE_VALUE) {
+ if (tmp != NULL) {
+ struct _fts_private *priv = (struct _fts_private *)sp;
+#ifdef FTS_WITH_ALLOC_CACHE
+ size_t idx;
+#endif
+
+ if (tmp->fts_dirfd == INVALID_HANDLE_VALUE) {
+ /* There are probably more files than directories out there. */
+ } else {
birdCloseFile(tmp->fts_dirfd);
tmp->fts_dirfd = INVALID_HANDLE_VALUE;
}
+
+#ifdef FTS_WITH_ALLOC_CACHE
+ idx = (tmp->fts_alloc_size - sizeof(FTSENT)) >> FTS_FREE_BUCKET_SHIFT;
+ if (idx < FTS_NUM_FREE_BUCKETS) {
+ tmp->fts_link = priv->freebuckets[idx].head;
+ priv->freebuckets[idx].head = tmp;
+ } else {
+ tmp->fts_link = priv->freebuckets[FTS_NUM_FREE_BUCKETS - 1].head;
+ priv->freebuckets[FTS_NUM_FREE_BUCKETS - 1].head = tmp;
+ }
+
+ priv->numfree++;
+#else
free(tmp);
- }
+#endif
+ }
}
+
+/*
+ * 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_pathlen - (p->fts_path[p->fts_pathlen - 1] == '/') )
+#define NAPPENDW(p) ( p->fts_cwcpath - (p->fts_wcspath[p->fts_cwcpath - 1] == L'/') )
+
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);
+ wchar_t *pwc;
/* Set current node pointer. */
p = sp->fts_cur;
+ /* If finished or unrecoverable error, return NULL. */
+ if (p != NULL && !ISSET(FTS_STOP)) {
+ /* likely */
+ } else {
+ return (NULL);
+ }
+
/* 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) {
+ if (instr != FTS_AGAIN) {
+ /* likely */
+ } else {
p->fts_info = fts_stat(sp, p, 0, INVALID_HANDLE_VALUE);
return (p);
}
@@ -351,15 +500,15 @@ nt_fts_read(FTS *sp)
* 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.
+ * NT: Since we don't change directory, we just set FTS_SYMFOLLOW
+ * here in case a API client checks it.
*/
- if (instr == FTS_FOLLOW &&
- (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
+ if ( instr != FTS_FOLLOW
+ || (p->fts_info != FTS_SL && p->fts_info != FTS_SLNONE)) {
+ /* likely */
+ } else {
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);
@@ -368,11 +517,8 @@ nt_fts_read(FTS *sp)
/* 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 ( instr == FTS_SKIP
+ || (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
if (sp->fts_child) {
fts_lfree(sp->fts_child);
sp->fts_child = NULL;
@@ -400,15 +546,20 @@ nt_fts_read(FTS *sp)
* 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);
+ if (sp->fts_child == NULL) {
+ p = fts_build(sp, BREAD);
+ if (p != NULL) {
+ /* likely */
+ } else {
+ if (ISSET(FTS_STOP))
+ return (NULL);
+ return sp->fts_cur;
+ }
+
+ } else {
+ p = sp->fts_child;
+ sp->fts_child = NULL;
}
- p = sp->fts_child;
- sp->fts_child = NULL;
goto name;
}
@@ -419,8 +570,10 @@ next: tmp = p;
* 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);
+ if (p->fts_level != FTS_ROOTLEVEL) {
+ /* likely */
+ } else {
+ fts_free_entry(sp, tmp);
fts_load(sp, p);
return (sp->fts_cur = p);
}
@@ -430,61 +583,70 @@ next: tmp = p;
* ignore. If followed, get a file descriptor so we can
* get back if necessary.
*/
- if (p->fts_instr == FTS_SKIP) {
- fts_free_entry(tmp);
+ if (p->fts_instr != FTS_SKIP) {
+ /* likely */
+ } else {
+ fts_free_entry(sp, tmp);
goto next;
}
- if (p->fts_instr == FTS_FOLLOW) {
+ if (p->fts_instr != FTS_FOLLOW) {
+ /* likely */
+ } else {
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;
+ /* NT: See above regarding fts_flags. */
+ if (p->fts_info == FTS_D) {
p->fts_flags |= FTS_SYMFOLLOW;
}
p->fts_instr = FTS_NOINSTR;
}
- fts_free_entry(tmp);
+ fts_free_entry(sp, tmp);
-name: t = sp->fts_path + NAPPEND(p->fts_parent);
- *t++ = '/';
- memmove(t, p->fts_name, p->fts_namelen + 1);
+name:
+ if (!(sp->fts_options & FTS_NO_ANSI)) {
+ char *t = sp->fts_path + NAPPEND(p->fts_parent);
+ *t++ = '/';
+ memmove(t, p->fts_name, p->fts_namelen + 1);
+ }
+ pwc = sp->fts_wcspath + NAPPENDW(p->fts_parent);
+ *pwc++ = '/';
+ memmove(pwc, p->fts_wcsname, (p->fts_cwcname + 1) * sizeof(wchar_t));
return (sp->fts_cur = p);
}
/* Move up to the parent node. */
p = tmp->fts_parent;
- if (p->fts_level == FTS_ROOTPARENTLEVEL) {
+ if (p->fts_level != FTS_ROOTPARENTLEVEL) {
+ /* likely */
+ } else {
/*
* 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);
+ fts_free_entry(sp, tmp);
+ fts_free_entry(sp, p);
errno = 0;
return (sp->fts_cur = NULL);
}
/* NUL terminate the pathname. */
- sp->fts_path[p->fts_pathlen] = '\0';
+ if (!(sp->fts_options & FTS_NO_ANSI))
+ sp->fts_path[p->fts_pathlen] = '\0';
+ sp->fts_wcspath[ p->fts_cwcpath] = '\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.
+ * NT: We're doing no fchdir, but we need to close the directory handle.
*/
- if (p->fts_flags & FTS_SYMFOLLOW) {
- p->fts_symfd = INVALID_HANDLE_VALUE;
- }
- if (p->fts_dirfd != INVALID_HANDLE_VALUE) {
+ if (p->fts_dirfd != INVALID_HANDLE_VALUE) {
birdCloseFile(p->fts_dirfd);
p->fts_dirfd = INVALID_HANDLE_VALUE;
- }
- fts_free_entry(tmp);
+ }
+ fts_free_entry(sp, tmp);
p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
return (sp->fts_cur = p);
}
@@ -609,15 +771,13 @@ nt_fts_set_clientptr(FTS *sp, void *clientptr)
static FTSENT *
fts_build(FTS *sp, int type)
{
- BirdDirEntry_T *dp;
- FTSENT *p, *head;
- FTSENT *cur, *tail;
+ BirdDirEntryW_T *dp;
+ FTSENT *p, *cur;
+ FTSENT * volatile head,* volatile *tailp; /* volatile is to prevent aliasing trouble */
DIR *dirp;
- void *oldaddr;
- char *cp;
- int saved_errno, doadjust;
+ int saved_errno, doadjust, doadjust_utf16;
long level;
- size_t dnamlen, len, maxlen, nitems;
+ size_t len, cwcdir, maxlen, cwcmax, nitems;
unsigned fDirOpenFlags;
/* Set current node pointer. */
@@ -633,31 +793,40 @@ fts_build(FTS *sp, int type)
* members of the directory.
*/
fDirOpenFlags = BIRDDIR_F_EXTRA_INFO | BIRDDIR_F_KEEP_HANDLE;
- if (cur->fts_dirfd == INVALID_HANDLE_VALUE) {
+ 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);
+ cur->fts_dirfd = birdOpenFileExW(cur->fts_parent->fts_dirfd,
+ cur->fts_wcsname,
+ 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);
+ cur->fts_dirfd = birdOpenFileW(cur->fts_wcsaccpath,
+ 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 {
+ if (cur->fts_dirfd != INVALID_HANDLE_VALUE) { /* likely */ }
+ else goto l_open_err;
+
+ } else {
fDirOpenFlags |= BIRDDIR_F_RESTART_SCAN;
}
+#ifdef FTS_WITH_DIRHANDLE_REUSE
+ dirp = birdDirOpenFromHandleWithReuse(&((struct _fts_private *)sp)->dirhandle, cur->fts_dirfd, NULL,
+ fDirOpenFlags | BIRDDIR_F_STATIC_ALLOC);
+#else
dirp = birdDirOpenFromHandle(cur->fts_dirfd, NULL, fDirOpenFlags);
+#endif
if (dirp == NULL) {
+l_open_err:
if (type == BREAD) {
cur->fts_info = FTS_DNR;
cur->fts_errno = errno;
@@ -671,40 +840,62 @@ fts_build(FTS *sp, int type)
* 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;
+ if (sp->fts_options & FTS_NO_ANSI) {
+ len = 0;
+ maxlen = 0x10000;
+ } else {
+ len = NAPPEND(cur);
+ len++;
+ maxlen = sp->fts_pathlen - len;
+ }
+
+ cwcdir = NAPPENDW(cur);
+ cwcdir++;
+ cwcmax = sp->fts_cwcpath - 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))
+ doadjust = doadjust_utf16 = 0;
+ nitems = 0;
+ head = NULL;
+ tailp = &head;
+ while ((dp = birdDirReadW(dirp)) != NULL) {
+ if (ISSET(FTS_SEEDOT) || !ISDOT(dp->d_name)) {
+ /* assume dirs have two or more entries */
+ } else {
continue;
+ }
- if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL)
+ if ((p = fts_alloc_utf16(sp, dp->d_name, dp->d_namlen)) != NULL) {
+ /* likely */
+ } else {
goto mem1;
- if (dnamlen >= maxlen) { /* include space for NUL */
- oldaddr = sp->fts_path;
- if (fts_palloc(sp, dnamlen + len + 1)) {
+ }
+
+ /* include space for NUL */
+ if (p->fts_namelen < maxlen && p->fts_cwcname < cwcmax) {
+ /* likely */
+ } else {
+ void *oldaddr = sp->fts_path;
+ wchar_t *oldwcspath = sp->fts_wcspath;
+ if (fts_palloc(sp,
+ p->fts_namelen >= maxlen ? len + p->fts_namelen + 1 : 0,
+ p->fts_cwcname >= cwcmax ? cwcdir + p->fts_cwcname + 1 : 0)) {
+mem1:
/*
* No more memory for path or structures. Save
* errno, free up the current structure and the
* structures already allocated.
*/
-mem1: saved_errno = errno;
+ saved_errno = errno;
if (p)
free(p);
fts_lfree(head);
+#ifndef FTS_WITH_DIRHANDLE_REUSE
birdDirClose(dirp);
+#endif
birdCloseFile(cur->fts_dirfd);
cur->fts_dirfd = INVALID_HANDLE_VALUE;
cur->fts_info = FTS_ERR;
@@ -713,33 +904,31 @@ mem1: saved_errno = 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;
- }
+ doadjust |= oldaddr != sp->fts_path;
+ doadjust_utf16 |= oldwcspath != sp->fts_wcspath;
maxlen = sp->fts_pathlen - len;
+ cwcmax = sp->fts_cwcpath - cwcdir;
}
p->fts_level = level;
p->fts_parent = sp->fts_cur;
- p->fts_pathlen = len + dnamlen;
+ p->fts_pathlen = len + p->fts_namelen;
+ p->fts_cwcpath = cwcdir + p->fts_cwcname;
p->fts_accpath = p->fts_path;
+ p->fts_wcsaccpath = p->fts_wcspath;
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;
- }
+ *tailp = p;
+ tailp = &p->fts_link;
++nitems;
}
+#ifndef FTS_WITH_DIRHANDLE_REUSE
birdDirClose(dirp);
+#endif
/*
* If realloc() changed the address of the path, adjust the
@@ -747,11 +936,8 @@ mem1: saved_errno = errno;
*/
if (doadjust)
fts_padjust(sp, head);
-
- /*
- * Reset the path back to original state.
- */
- sp->fts_path[cur->fts_pathlen] = '\0';
+ if (doadjust_utf16)
+ fts_padjustw(sp, head);
/* If didn't find anything, return NULL. */
if (!nitems) {
@@ -776,12 +962,12 @@ static int
fts_stat(FTS *sp, FTSENT *p, int follow, HANDLE dfd)
{
int saved_errno;
- const char *path;
+ const wchar_t *wcspath;
if (dfd == INVALID_HANDLE_VALUE) {
- path = p->fts_accpath;
+ wcspath = p->fts_wcsaccpath;
} else {
- path = p->fts_name;
+ wcspath = p->fts_wcsname;
}
/*
@@ -790,9 +976,9 @@ fts_stat(FTS *sp, FTSENT *p, int follow, HANDLE dfd)
* fail, set the errno from the stat call.
*/
if (ISSET(FTS_LOGICAL) || follow) {
- if (fstatat(dfd, path, &p->fts_stat, 0)) {
+ if (birdStatAtW(dfd, wcspath, &p->fts_stat, 1 /*fFollowLink*/)) {
saved_errno = errno;
- if (fstatat(dfd, path, &p->fts_stat, AT_SYMLINK_NOFOLLOW)) {
+ if (birdStatAtW(dfd, wcspath, &p->fts_stat, 0 /*fFollowLink*/)) {
p->fts_errno = saved_errno;
goto err;
}
@@ -800,7 +986,7 @@ fts_stat(FTS *sp, FTSENT *p, int follow, HANDLE dfd)
if (S_ISLNK(p->fts_stat.st_mode))
return (FTS_SLNONE);
}
- } else if (fstatat(dfd, path, &p->fts_stat, AT_SYMLINK_NOFOLLOW)) {
+ } else if (birdStatAtW(dfd, wcspath, &p->fts_stat, 0 /*fFollowLink*/)) {
p->fts_errno = errno;
err: memset(&p->fts_stat, 0, sizeof(struct stat));
return (FTS_NS);
@@ -828,7 +1014,7 @@ fts_process_stats(FTSENT *p, BirdStat_T const *sbp)
ino = p->fts_ino = sbp->st_ino;
p->fts_nlink = sbp->st_nlink;
- if (ISDOT(p->fts_name))
+ if (ISDOT(p->fts_wcsname))
return (FTS_DOT);
/*
@@ -902,43 +1088,197 @@ fts_sort(FTS *sp, FTSENT *head, size_t nitems)
}
static FTSENT *
-fts_alloc(FTS *sp, char *name, size_t namelen)
+fts_alloc(FTS *sp, char const *name, size_t namelen, wchar_t const *wcsname, size_t cwcname)
{
+ struct _fts_private *priv = (struct _fts_private *)sp;
FTSENT *p;
size_t len;
+#ifdef FTS_WITH_ALLOC_CACHE
+ size_t aligned;
+ size_t idx;
+#endif
- struct ftsent_withstat {
- FTSENT ent;
- struct stat statbuf;
- };
-
+#if defined(FTS_WITH_STATISTICS) && defined(FTS_WITH_ALLOC_CACHE)
+ priv->allocs++;
+#endif
/*
* 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);
+ len = sizeof(FTSENT) + (cwcname + 1) * sizeof(wchar_t);
+ if (!(sp->fts_options & FTS_NO_ANSI))
+ len += namelen + 1;
- p->fts_name = (char *)(p + 1);
- p->fts_statp = &p->fts_stat;
+ /*
+ * To speed things up we cache entries. This code is a little insane,
+ * but that's preferable to slow code.
+ */
+#ifdef FTS_WITH_ALLOC_CACHE
+ aligned = (len + FTS_ALIGN_FTSENT + 1) & ~(size_t)(FTS_ALIGN_FTSENT - 1);
+ idx = ((aligned - sizeof(FTSENT)) >> FTS_FREE_BUCKET_SHIFT);
+ if ( idx < FTS_NUM_FREE_BUCKETS
+ && (p = priv->freebuckets[idx].head)
+ && p->fts_alloc_size >= len) {
+ priv->freebuckets[idx].head = p->fts_link;
+ priv->numfree--;
+# ifdef FTS_WITH_STATISTICS
+ priv->hits++;
+# endif
+
+ } else {
+# ifdef FTS_WITH_STATISTICS
+ priv->misses++;
+# endif
+ p = malloc(aligned);
+ if (p) {
+ p->fts_alloc_size = (unsigned)aligned;
+ } else {
+ nt_fts_free_alloc_cache(sp);
+ p = malloc(len);
+ if (!p)
+ return NULL;
+ p->fts_alloc_size = (unsigned)len;
+ }
+ }
+#else /* !FTS_WITH_ALLOC_CACHE */
+ p = malloc(len);
+ if (p) {
+ p->fts_alloc_size = (unsigned)len;
+ } else {
+ return NULL;
+ }
+#endif /* !FTS_WITH_ALLOC_CACHE */
+
+ /* Copy the names and guarantee NUL termination. */
+ p->fts_wcsname = (wchar_t *)(p + 1);
+ memcpy(p->fts_wcsname, wcsname, cwcname * sizeof(wchar_t));
+ p->fts_wcsname[cwcname] = '\0';
+ p->fts_cwcname = cwcname;
+ if (!(sp->fts_options & FTS_NO_ANSI)) {
+ p->fts_name = (char *)(p->fts_wcsname + cwcname + 1);
+ memcpy(p->fts_name, name, namelen);
+ p->fts_name[namelen] = '\0';
+ p->fts_namelen = namelen;
+ } else {
+ p->fts_name = NULL;
+ p->fts_namelen = 0;
+ }
- /* 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_wcspath = sp->fts_wcspath;
+ p->fts_statp = &p->fts_stat;
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);
}
+
+/**
+ * Converts the ANSI name to UTF-16 and calls fts_alloc.
+ *
+ * @returns Pointer to allocated and mostly initialized FTSENT structure on
+ * success. NULL on failure, caller needs to record it.
+ * @param sp Pointer to FTS instance.
+ * @param name The ANSI name.
+ * @param namelen The ANSI name length.
+ */
+static FTSENT *
+fts_alloc_ansi(FTS *sp, char const *name, size_t namelen)
+{
+ MY_UNICODE_STRING UniStr;
+ MY_ANSI_STRING AnsiStr;
+ MY_NTSTATUS rcNt;
+ FTSENT *pRet;
+
+ UniStr.Buffer = NULL;
+ UniStr.MaximumLength = UniStr.Length = 0;
+
+ AnsiStr.Buffer = (char *)name;
+ AnsiStr.Length = AnsiStr.MaximumLength = (USHORT)namelen;
+
+ rcNt = g_pfnRtlAnsiStringToUnicodeString(&UniStr, &AnsiStr, TRUE /*fAllocate*/);
+ if (NT_SUCCESS(rcNt)) {
+ pRet = fts_alloc(sp, name, namelen, UniStr.Buffer, UniStr.Length / sizeof(wchar_t));
+ HeapFree(GetProcessHeap(), 0, UniStr.Buffer);
+ } else {
+ pRet = NULL;
+ }
+ return pRet;
+}
+
+
+/**
+ * Converts the UTF-16 name to ANSI (if necessary) and calls fts_alloc.
+ *
+ * @returns Pointer to allocated and mostly initialized FTSENT structure on
+ * success. NULL on failure, caller needs to record it.
+ * @param sp Pointer to the FTS instance.
+ * @param wcsname The UTF-16 name.
+ * @param cwcname The UTF-16 name length.
+ */
+static FTSENT *
+fts_alloc_utf16(FTS *sp, wchar_t const *wcsname, size_t cwcname)
+{
+ FTSENT *pRet;
+
+ if (sp->fts_options & FTS_NO_ANSI) {
+ pRet = fts_alloc(sp, NULL, 0, wcsname, cwcname);
+ } else {
+ MY_UNICODE_STRING UniStr;
+ MY_ANSI_STRING AnsiStr;
+ MY_NTSTATUS rcNt;
+
+ UniStr.Buffer = (wchar_t *)wcsname;
+ UniStr.MaximumLength = UniStr.Length = (USHORT)(cwcname * sizeof(wchar_t));
+
+ AnsiStr.Buffer = NULL;
+ AnsiStr.Length = AnsiStr.MaximumLength = 0;
+
+ rcNt = g_pfnRtlUnicodeStringToAnsiString(&AnsiStr, &UniStr, TRUE /*fAllocate*/);
+ if (NT_SUCCESS(rcNt)) {
+ pRet = fts_alloc(sp, AnsiStr.Buffer, AnsiStr.Length, wcsname, cwcname);
+ HeapFree(GetProcessHeap(), 0, AnsiStr.Buffer);
+ } else {
+ pRet = NULL;
+ }
+ }
+ return pRet;
+}
+
+
+/**
+ * Frees up the FTSENT allocation cache.
+ *
+ * Used by nt_fts_close, but also called by fts_alloc on alloc failure.
+ *
+ * @param sp Pointer to the FTS instance.
+ */
+static void nt_fts_free_alloc_cache(FTS *sp)
+{
+#ifdef FTS_WITH_ALLOC_CACHE
+ struct _fts_private *priv = (struct _fts_private *)sp;
+ unsigned i = K_ELEMENTS(priv->freebuckets);
+ while (i-- > 0) {
+ FTSENT *cur = priv->freebuckets[i].head;
+ priv->freebuckets[i].head = NULL;
+ while (cur) {
+ FTSENT *freeit = cur;
+ cur = cur->fts_link;
+ free(freeit);
+ }
+ }
+ priv->numfree = 0;
+#else
+ (void)sp;
+#endif
+}
+
+
static void
fts_lfree(FTSENT *head)
{
@@ -959,19 +1299,42 @@ fts_lfree(FTSENT *head)
* plus 256 bytes so don't realloc the path 2 bytes at a time.
*/
static int
-fts_palloc(FTS *sp, size_t more)
+fts_palloc(FTS *sp, size_t more, size_t cwcmore)
{
void *ptr;
- sp->fts_pathlen += more + 256;
- ptr = realloc(sp->fts_path, sp->fts_pathlen);
- if (ptr) {
- /*likely */
- } else {
- free(sp->fts_path);
+ /** @todo Isn't more and cwcmore minimum buffer sizes rather than what needs
+ * to be added to the buffer?? This code makes no sense when looking at
+ * the way the caller checks things out! */
+
+ if (more) {
+ sp->fts_pathlen += more + 256;
+ ptr = realloc(sp->fts_path, sp->fts_pathlen);
+ if (ptr) {
+ sp->fts_path = ptr;
+ } else {
+ free(sp->fts_path);
+ sp->fts_path = NULL;
+ free(sp->fts_wcspath);
+ sp->fts_wcspath = NULL;
+ return 1;
+ }
}
- sp->fts_path = ptr;
- return (ptr == NULL);
+
+ if (cwcmore) {
+ sp->fts_cwcpath += cwcmore + 256;
+ ptr = realloc(sp->fts_wcspath, sp->fts_cwcpath);
+ if (ptr) {
+ sp->fts_wcspath = ptr;
+ } else {
+ free(sp->fts_path);
+ sp->fts_path = NULL;
+ free(sp->fts_wcspath);
+ sp->fts_wcspath = NULL;
+ return 1;
+ }
+ }
+ return 0;
}
/*
@@ -1002,6 +1365,34 @@ fts_padjust(FTS *sp, FTSENT *head)
}
}
+/*
+ * When the UTF-16 path is realloc'd, have to fix all of the pointers in
+ * structures already returned.
+ */
+static void
+fts_padjustw(FTS *sp, FTSENT *head)
+{
+ FTSENT *p;
+ wchar_t *addr = sp->fts_wcspath;
+
+#define ADJUSTW(p) \
+ do { \
+ if ((p)->fts_wcsaccpath != (p)->fts_wcsname) \
+ (p)->fts_wcsaccpath = addr + ((p)->fts_wcsaccpath - (p)->fts_wcspath); \
+ (p)->fts_wcspath = addr; \
+ } while (0)
+
+ /* Adjust the current set of children. */
+ for (p = sp->fts_child; p; p = p->fts_link)
+ ADJUSTW(p);
+
+ /* Adjust the rest of the tree, including the current level. */
+ for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
+ ADJUSTW(p);
+ p = p->fts_link ? p->fts_link : p->fts_parent;
+ }
+}
+
static size_t
fts_maxarglen(char * const *argv)
{
@@ -1013,3 +1404,16 @@ fts_maxarglen(char * const *argv)
return (max + 1);
}
+/** Returns the max string size (including term). */
+static size_t
+fts_maxarglenw(wchar_t * const *argv)
+{
+ size_t max = 0;
+ for (; *argv; ++argv) {
+ size_t len = wcslen(*argv);
+ if (len > max)
+ max = len;
+ }
+ return max + 1;
+}
+
diff --git a/src/lib/nt/fts-nt.h b/src/lib/nt/fts-nt.h
index dc990b0..d5fe38e 100644
--- a/src/lib/nt/fts-nt.h
+++ b/src/lib/nt/fts-nt.h
@@ -1,4 +1,4 @@
-/* $Id: fts-nt.h 2997 2016-11-01 23:28:02Z bird $ */
+/* $Id: fts-nt.h 3004 2016-11-05 23:18:51Z bird $ */
/** @file
* Header for the NT port of BSD fts.h.
*
@@ -66,6 +66,8 @@ typedef struct {
fts_dev_t fts_dev; /* starting device # */
char *fts_path; /* path for this descent */
size_t fts_pathlen; /* sizeof(path) */
+ wchar_t *fts_wcspath; /* NT: UTF-16 path for this descent. */
+ size_t fts_cwcpath; /* NT: size of fts_wcspath buffer */
size_t fts_nitems; /* elements in the sort array */
int (FTSCALL *fts_compar) /* compare function */
(const struct _ftsent * const *, const struct _ftsent * const *);
@@ -80,7 +82,8 @@ typedef struct {
#if 0 /* No whiteout on NT. */
#define FTS_WHITEOUT 0x080 /* return whiteout information */
#endif
-#define FTS_OPTIONMASK 0x0ff /* valid user option mask */
+#define FTS_NO_ANSI 0x40000000 /* NT: No ansi name or access path. */
+#define FTS_OPTIONMASK 0x400000ff /* valid user option mask */
#define FTS_NAMEONLY 0x100 /* (private) child names only */
#define FTS_STOP 0x200 /* (private) unrecoverable error */
@@ -96,12 +99,16 @@ typedef struct _ftsent {
#define fts_bignum fts_number /* XXX non-std, should go away */
void *fts_pointer; /* local address value */
char *fts_accpath; /* access path */
+ wchar_t *fts_wcsaccpath; /* NT: UTF-16 access path */
char *fts_path; /* root path */
+ wchar_t *fts_wcspath; /* NT: UTF-16 root path */
int fts_errno; /* errno for this node */
- fts_fd_t fts_symfd; /* NT: Normally -1; -2 we followed this symlinked dir */
+ size_t fts_alloc_size; /* internal - size of the allocation for this entry. */
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_cwcpath; /* NT: length of fts_wcspath. */
size_t fts_namelen; /* strlen(fts_name) */
+ size_t fts_cwcname; /* NT: length of fts_wcsname. */
fts_ino_t fts_ino; /* inode */
fts_dev_t fts_dev; /* device */
@@ -140,6 +147,7 @@ typedef struct _ftsent {
struct stat *fts_statp; /* stat(2) information */
char *fts_name; /* file name */
+ wchar_t *fts_wcsname; /* NT: UTF-16 file name. */
FTS *fts_fts; /* back pointer to main FTS */
BirdStat_T fts_stat; /* NT: We always got stat info. */
} FTSENT;
@@ -155,8 +163,8 @@ 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 *));
+FTS *FTSCALL nt_fts_open(char * const *, int, int (FTSCALL*)(const FTSENT * const *, const FTSENT * const *));
+FTS *FTSCALL nt_fts_openw(wchar_t * 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 *);
diff --git a/src/lib/nt/kFsCache.c b/src/lib/nt/kFsCache.c
index cb28198..59fa467 100644
--- a/src/lib/nt/kFsCache.c
+++ b/src/lib/nt/kFsCache.c
@@ -1,4 +1,4 @@
-/* $Id: kFsCache.c 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: kFsCache.c 3007 2016-11-06 16:46:43Z bird $ */
/** @file
* ntdircache.c - NT directory content cache.
*/
@@ -1328,7 +1328,8 @@ static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLO
/* Include the structures for better alignment. */
MY_FILE_ID_BOTH_DIR_INFORMATION WithId;
MY_FILE_ID_FULL_DIR_INFORMATION NoId;
- /* Buffer padding. We're using a 56KB buffer here to avoid size troubles with CIFS and such. */
+ /** Buffer padding. We're using a 56KB buffer here to avoid size troubles
+ * with CIFS and such that starts at 64KB. */
KU8 abBuf[56*1024];
} uBuf;
@@ -1567,14 +1568,14 @@ static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLO
#ifdef KFSCACHE_CFG_SHORT_NAMES
if (enmInfoClass == enmInfoClassWithId)
- birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
+ birdStatFillFromFileIdBothDirInfo(&pCur->Stats, uPtr.pWithId);
else
- birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
+ birdStatFillFromFileBothDirInfo(&pCur->Stats, uPtr.pNoId);
#else
if (enmInfoClass == enmInfoClassWithId)
- birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId, pCur->pszName);
+ birdStatFillFromFileIdFullDirInfo(&pCur->Stats, uPtr.pWithId);
else
- birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId, pCur->pszName);
+ birdStatFillFromFileFullDirInfo(&pCur->Stats, uPtr.pNoId);
#endif
pCur->Stats.st_dev = pDir->uDevNo;
pCur->fHaveStats = K_TRUE;
@@ -1600,14 +1601,14 @@ static KBOOL kFsCachePopuplateOrRefreshDir(PKFSCACHE pCache, PKFSDIR pDir, KFSLO
pDir->iLastWrite = uPtr.pNoId->LastWriteTime.QuadPart;
#ifdef KFSCACHE_CFG_SHORT_NAMES
if (enmInfoClass == enmInfoClassWithId)
- birdStatFillFromFileIdBothDirInfo(&pDir->Obj.Stats, uPtr.pWithId, pDir->Obj.pszName);
+ birdStatFillFromFileIdBothDirInfo(&pDir->Obj.Stats, uPtr.pWithId);
else
- birdStatFillFromFileBothDirInfo(&pDir->Obj.Stats, uPtr.pNoId, pDir->Obj.pszName);
+ birdStatFillFromFileBothDirInfo(&pDir->Obj.Stats, uPtr.pNoId);
#else
if (enmInfoClass == enmInfoClassWithId)
- birdStatFillFromFileIdFullDirInfo(&pDir->Obj.Stats, uPtr.pWithId, pDir->Obj.pszName);
+ birdStatFillFromFileIdFullDirInfo(&pDir->Obj.Stats, uPtr.pWithId);
else
- birdStatFillFromFileFullDirInfo(&pDir->Obj.Stats, uPtr.pNoId, pDir->Obj.pszName);
+ birdStatFillFromFileFullDirInfo(&pDir->Obj.Stats, uPtr.pNoId);
#endif
}
diff --git a/src/lib/nt/ntdir.c b/src/lib/nt/ntdir.c
index 3ea166e..61a58e3 100644
--- a/src/lib/nt/ntdir.c
+++ b/src/lib/nt/ntdir.c
@@ -1,4 +1,4 @@
-/* $Id: ntdir.c 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: ntdir.c 3007 2016-11-06 16:46:43Z bird $ */
/** @file
* MSC + NT opendir, readdir, telldir, seekdir, and closedir.
*/
@@ -114,7 +114,7 @@ BirdDir_T *birdDirOpenExW(void *hRoot, const wchar_t *pwszPath, const wchar_t *p
*/
BirdDir_T *birdDirOpenFromHandle(void *pvHandle, const void *pvReserved, unsigned fFlags)
{
- if (!pvReserved)
+ if (!pvReserved && !(fFlags & BIRDDIR_F_STATIC_ALLOC))
{
/*
* Allocate and initialize the directory enum handle.
@@ -137,8 +137,56 @@ BirdDir_T *birdDirOpenFromHandle(void *pvHandle, const void *pvReserved, unsigne
}
}
else
+ {
+ assert(!(fFlags & BIRDDIR_F_STATIC_ALLOC));
+ assert(pvReserved == NULL);
+ }
+ birdSetErrnoToInvalidArg();
+ return NULL;
+}
+
+
+/**
+ * Special API that takes a preallocated BirdDir_T and can be called again
+ * without involving birdDirClose.
+ *
+ *
+ */
+BirdDir_T *birdDirOpenFromHandleWithReuse(BirdDir_T *pDir, void *pvHandle, const void *pvReserved, unsigned fFlags)
+{
+ if (!pvReserved)
+ {
+ /*
+ * Allocate and initialize the directory enum handle.
+ */
+ if (pDir)
+ {
+ if (pDir->uMagic == BIRD_DIR_MAGIC)
+ {
+ if ( (pDir->fFlags & BIRDDIR_F_CLOSE_HANDLE)
+ && pDir->pvHandle != INVALID_HANDLE_VALUE)
+ birdCloseFile((HANDLE)pDir->pvHandle);
+ }
+ else
+ {
+ pDir->cbBuf = 0;
+ pDir->pabBuf = NULL;
+ pDir->uMagic = BIRD_DIR_MAGIC;
+ }
+ pDir->pvHandle = pvHandle;
+ pDir->fFlags = fFlags;
+ pDir->uDev = 0;
+ pDir->offPos = 0;
+ pDir->fHaveData = 0;
+ pDir->fFirst = 1;
+ pDir->iInfoClass = fFlags & BIRDDIR_F_EXTRA_INFO ? MyFileIdFullDirectoryInformation : MyFileNamesInformation;
+ pDir->offBuf = 0;
+ return pDir;
+ }
+ }
+ else
assert(pvReserved == NULL);
- birdSetErrnoToNoMem();
+ birdSetErrnoToInvalidArg();
return NULL;
}
@@ -173,13 +221,19 @@ static int birdDirReadMore(BirdDir_T *pDir)
else
pDir->uDev = 0;
- /*
- * Allocate a buffer.
- */
- pDir->cbBuf = 0x20000;
- pDir->pabBuf = birdMemAlloc(pDir->cbBuf);
if (!pDir->pabBuf)
- return birdSetErrnoToNoMem();
+ {
+ /*
+ * Allocate a buffer.
+ *
+ * Better not exceed 64KB or CIFS may throw a fit. Also, on win10/64
+ * here there is a noticable speedup when going one byte below 64KB.
+ */
+ pDir->cbBuf = 0xffe0;
+ pDir->pabBuf = birdMemAlloc(pDir->cbBuf);
+ if (!pDir->pabBuf)
+ return birdSetErrnoToNoMem();
+ }
pDir->fFirst = 0;
}
@@ -238,9 +292,55 @@ static int birdDirCopyNameToEntry(WCHAR const *pwcName, ULONG cbName, BirdDirEnt
}
+/**
+ * Deals with mount points.
+ *
+ * @param pDir The directory handle.
+ * @param pInfo The NT entry information.
+ * @param pEntryStat The stats for the mount point directory that needs
+ * updating (a d_stat member).
+ */
+static void birdDirUpdateMountPointInfo(BirdDir_T *pDir, MY_FILE_ID_FULL_DIR_INFORMATION *pInfo,
+ BirdStat_T *pEntryStat)
+{
+ /*
+ * Try open the root directory of the mount.
+ * (Can't use birdStatAtW here because the name isn't terminated.)
+ */
+ HANDLE hRoot = INVALID_HANDLE_VALUE;
+ MY_NTSTATUS rcNt;
+ MY_UNICODE_STRING Name;
+ Name.Buffer = pInfo->FileName;
+ Name.Length = Name.MaximumLength = (USHORT)pInfo->FileNameLength;
+
+ rcNt = birdOpenFileUniStr((HANDLE)pDir->pvHandle, &Name,
+ FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT,
+ OBJ_CASE_INSENSITIVE,
+ &hRoot);
+ if (MY_NT_SUCCESS(rcNt))
+ {
+ int iSavedErrno = errno;
+ BirdStat_T RootStat;
+ if (birdStatHandle(hRoot, &RootStat, NULL) == 0)
+ {
+ RootStat.st_ismountpoint = 2;
+ *pEntryStat = RootStat;
+ }
+ birdCloseFile(hRoot);
+ errno = iSavedErrno;
+ }
+ /* else: don't mind failures, we've got some info. */
+}
+
/**
* Implements readdir_r().
+ *
+ * @remarks birdDirReadReentrantW is a copy of this. Keep them in sync!
*/
int birdDirReadReentrant(BirdDir_T *pDir, BirdDirEntry_T *pEntry, BirdDirEntry_T **ppResult)
{
@@ -315,7 +415,7 @@ int birdDirReadReentrant(BirdDir_T *pDir, BirdDirEntry_T *pEntry, BirdDirEntry_T
pEntry->d_namlen = 0;
if (birdDirCopyNameToEntry(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0)
fSkipEntry = 1;
- birdStatFillFromFileIdFullDirInfo(&pEntry->d_stat, pInfo, pEntry->d_name);
+ birdStatFillFromFileIdFullDirInfo(&pEntry->d_stat, pInfo);
pEntry->d_stat.st_dev = pDir->uDev;
switch (pEntry->d_stat.st_mode & S_IFMT)
{
@@ -332,6 +432,11 @@ int birdDirReadReentrant(BirdDir_T *pDir, BirdDirEntry_T *pEntry, BirdDirEntry_T
break;
}
+ if (pEntry->d_stat.st_ismountpoint != 1)
+ { /* likely. */ }
+ else
+ birdDirUpdateMountPointInfo(pDir, pInfo, &pEntry->d_stat);
+
cbMinCur = MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION + pInfo->FileNameLength;
offNext = pInfo->NextEntryOffset;
break;
@@ -370,7 +475,165 @@ int birdDirReadReentrant(BirdDir_T *pDir, BirdDirEntry_T *pEntry, BirdDirEntry_T
BirdDirEntry_T *birdDirRead(BirdDir_T *pDir)
{
BirdDirEntry_T *pRet = NULL;
- birdDirReadReentrant(pDir, &pDir->DirEntry, &pRet);
+ birdDirReadReentrant(pDir, &pDir->u.DirEntry, &pRet);
+ return pRet;
+}
+
+
+static int birdDirCopyNameToEntryW(WCHAR const *pwcName, ULONG cbName, BirdDirEntryW_T *pEntry)
+{
+ ULONG cwcName = cbName / sizeof(wchar_t);
+ if (cwcName < sizeof(pEntry->d_name))
+ {
+ memcpy(pEntry->d_name, pwcName, cbName);
+ pEntry->d_name[cwcName] = '\0';
+ pEntry->d_namlen = (unsigned __int16)cwcName;
+ pEntry->d_reclen = (unsigned __int16)((size_t)&pEntry->d_name[cwcName + 1] - (size_t)pEntry);
+ return 0;
+ }
+ return -1;
+}
+
+
+/**
+ * Implements readdir_r(), UTF-16 version.
+ *
+ * @remarks This is a copy of birdDirReadReentrant where only the name handling
+ * and entry type differs. Remember to keep them in sync!
+ */
+int birdDirReadReentrantW(BirdDir_T *pDir, BirdDirEntryW_T *pEntry, BirdDirEntryW_T **ppResult)
+{
+ int fSkipEntry;
+
+ *ppResult = NULL;
+
+ if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC)
+ return birdSetErrnoToBadFileNo();
+
+ do
+ {
+ ULONG offNext;
+ ULONG cbMinCur;
+
+ /*
+ * Read more?
+ */
+ if (!pDir->fHaveData)
+ {
+ if (birdDirReadMore(pDir) != 0)
+ return -1;
+ if (!pDir->fHaveData)
+ return 0;
+ }
+
+ /*
+ * Convert the NT data to the unixy output structure.
+ */
+ fSkipEntry = 0;
+ switch (pDir->iInfoClass)
+ {
+ case MyFileNamesInformation:
+ {
+ MY_FILE_NAMES_INFORMATION *pInfo = (MY_FILE_NAMES_INFORMATION *)&pDir->pabBuf[pDir->offBuf];
+ if ( pDir->offBuf >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_NAMES_INFORMATION
+ || pInfo->FileNameLength >= pDir->cbBuf
+ || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_NAMES_INFORMATION > pDir->cbBuf)
+ {
+ fSkipEntry = 1;
+ pDir->fHaveData = 0;
+ continue;
+ }
+
+ memset(&pEntry->d_stat, 0, sizeof(pEntry->d_stat));
+ pEntry->d_stat.st_mode = S_IFMT;
+ pEntry->d_type = DT_UNKNOWN;
+ pEntry->d_reclen = 0;
+ pEntry->d_namlen = 0;
+ if (birdDirCopyNameToEntryW(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0)
+ fSkipEntry = 1;
+
+ cbMinCur = MIN_SIZEOF_MY_FILE_NAMES_INFORMATION + pInfo->FileNameLength;
+ offNext = pInfo->NextEntryOffset;
+ break;
+ }
+
+ case MyFileIdFullDirectoryInformation:
+ {
+ MY_FILE_ID_FULL_DIR_INFORMATION *pInfo = (MY_FILE_ID_FULL_DIR_INFORMATION *)&pDir->pabBuf[pDir->offBuf];
+ if ( pDir->offBuf >= pDir->cbBuf - MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION
+ || pInfo->FileNameLength >= pDir->cbBuf
+ || pDir->offBuf + pInfo->FileNameLength + MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION > pDir->cbBuf)
+ {
+ fSkipEntry = 1;
+ pDir->fHaveData = 0;
+ continue;
+ }
+
+ pEntry->d_type = DT_UNKNOWN;
+ pEntry->d_reclen = 0;
+ pEntry->d_namlen = 0;
+ if (birdDirCopyNameToEntryW(pInfo->FileName, pInfo->FileNameLength, pEntry) != 0)
+ fSkipEntry = 1;
+ birdStatFillFromFileIdFullDirInfo(&pEntry->d_stat, pInfo);
+ pEntry->d_stat.st_dev = pDir->uDev;
+ switch (pEntry->d_stat.st_mode & S_IFMT)
+ {
+ case S_IFREG: pEntry->d_type = DT_REG; break;
+ case S_IFDIR: pEntry->d_type = DT_DIR; break;
+ case S_IFLNK: pEntry->d_type = DT_LNK; break;
+ case S_IFIFO: pEntry->d_type = DT_FIFO; break;
+ case S_IFCHR: pEntry->d_type = DT_CHR; break;
+ default:
+#ifndef NDEBUG
+ __debugbreak();
+#endif
+ pEntry->d_type = DT_UNKNOWN;
+ break;
+ }
+
+ if (pEntry->d_stat.st_ismountpoint != 1)
+ { /* likely. */ }
+ else
+ birdDirUpdateMountPointInfo(pDir, pInfo, &pEntry->d_stat);
+
+ cbMinCur = MIN_SIZEOF_MY_FILE_ID_FULL_DIR_INFORMATION + pInfo->FileNameLength;
+ offNext = pInfo->NextEntryOffset;
+ break;
+ }
+
+ default:
+ return birdSetErrnoToBadFileNo();
+ }
+
+ /*
+ * Advance.
+ */
+ if ( offNext >= cbMinCur
+ && offNext < pDir->cbBuf)
+ pDir->offBuf += offNext;
+ else
+ {
+ pDir->fHaveData = 0;
+ pDir->offBuf = pDir->cbBuf;
+ }
+ pDir->offPos++;
+ } while (fSkipEntry);
+
+
+ /*
+ * Successful return.
+ */
+ *ppResult = pEntry;
+ return 0;
+}
+
+/**
+ * Implements readdir().
+ */
+BirdDirEntryW_T *birdDirReadW(BirdDir_T *pDir)
+{
+ BirdDirEntryW_T *pRet = NULL;
+ birdDirReadReentrantW(pDir, &pDir->u.DirEntryW, &pRet);
return pRet;
}
@@ -392,7 +655,7 @@ void birdDirSeek(BirdDir_T *pDir, long offDir);
/**
* Implements closedir().
*/
-int birdDirClose(BirdDir_T *pDir)
+int birdDirClose(BirdDir_T *pDir)
{
if (!pDir || pDir->uMagic != BIRD_DIR_MAGIC)
return birdSetErrnoToBadFileNo();
@@ -403,7 +666,8 @@ int birdDirClose(BirdDir_T *pDir)
pDir->pvHandle = (void *)INVALID_HANDLE_VALUE;
birdMemFree(pDir->pabBuf);
pDir->pabBuf = NULL;
- birdMemFree(pDir);
+ if (!(pDir->fFlags & BIRDDIR_F_STATIC_ALLOC))
+ birdMemFree(pDir);
return 0;
}
diff --git a/src/lib/nt/ntdir.h b/src/lib/nt/ntdir.h
index dd8eee2..c6d6c3c 100644
--- a/src/lib/nt/ntdir.h
+++ b/src/lib/nt/ntdir.h
@@ -1,4 +1,4 @@
-/* $Id: ntdir.h 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: ntdir.h 3005 2016-11-06 00:07:37Z bird $ */
/** @file
* MSC + NT opendir, readdir, closedir and friends.
*/
@@ -49,6 +49,21 @@ typedef struct dirent
char d_name[512 - sizeof(BirdStat_T) - 2 - 2 - 1];
} BirdDirEntry_T;
+typedef struct direntw
+{
+ /** Optional stat information.
+ * Only provided if using birdDirOpenExtraInfo(). */
+ BirdStat_T d_stat;
+ /** The record length. */
+ unsigned __int16 d_reclen;
+ /** The name length (in wchar_t). */
+ unsigned __int16 d_namlen;
+ /** The name type. */
+ unsigned char d_type;
+ /** The name. */
+ wchar_t d_name[512 - sizeof(BirdStat_T) - 2 - 2 - 1];
+} BirdDirEntryW_T;
+
#define d_ino d_stat.st_ino;
/** @name d_type values.
@@ -74,6 +89,8 @@ typedef struct dirent
#define BIRDDIR_F_EXTRA_INFO 2U
/** Whether to restart the scan. */
#define BIRDDIR_F_RESTART_SCAN 4U
+/** Set if the BirdDir_T structure is statically allocated. */
+#define BIRDDIR_F_STATIC_ALLOC 8U
/** @} */
typedef struct BirdDir
@@ -103,7 +120,11 @@ typedef struct BirdDir
unsigned char *pabBuf;
/** Static directory entry. */
- BirdDirEntry_T DirEntry;
+ union
+ {
+ BirdDirEntry_T DirEntry;
+ BirdDirEntryW_T DirEntryW;
+ } u;
} BirdDir_T;
/** Magic value for BirdDir. */
#define BIRD_DIR_MAGIC 0x19731120
@@ -113,7 +134,9 @@ 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);
+BirdDir_T *birdDirOpenFromHandleWithReuse(BirdDir_T *pDir, void *pvHandle, const void *pvReserved, unsigned fFlags);
BirdDirEntry_T *birdDirRead(BirdDir_T *pDir);
+BirdDirEntryW_T *birdDirReadW(BirdDir_T *pDir);
long birdDirTell(BirdDir_T *pDir);
void birdDirSeek(BirdDir_T *pDir, long offDir);
int birdDirClose(BirdDir_T *pDir);
diff --git a/src/lib/nt/nthlp.h b/src/lib/nt/nthlp.h
index 4199cc2..18125e4 100644
--- a/src/lib/nt/nthlp.h
+++ b/src/lib/nt/nthlp.h
@@ -1,4 +1,4 @@
-/* $Id: nthlp.h 2997 2016-11-01 23:28:02Z bird $ */
+/* $Id: nthlp.h 3009 2016-11-07 02:21:59Z bird $ */
/** @file
* MSC + NT helper functions.
*/
@@ -74,7 +74,9 @@ MY_NTSTATUS birdOpenFileUniStr(HANDLE hRoot, MY_UNICODE_STRING *pNtPath, ACCESS_
HANDLE birdOpenCurrentDirectory(void);
void birdCloseFile(HANDLE hFile);
int birdDosToNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath);
+int birdDosToNtPathW(const wchar_t *pwszPath, MY_UNICODE_STRING *pNtPath);
int birdDosToRelativeNtPath(const char *pszPath, MY_UNICODE_STRING *pNtPath);
+int birdDosToRelativeNtPathW(const wchar_t *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 b4d77eb..82793fe 100644
--- a/src/lib/nt/nthlpcore.c
+++ b/src/lib/nt/nthlpcore.c
@@ -1,4 +1,4 @@
-/* $Id: nthlpcore.c 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: nthlpcore.c 2998 2016-11-05 19:37:35Z bird $ */
/** @file
* MSC + NT core helpers functions and globals.
*/
@@ -60,6 +60,7 @@ MY_NTSTATUS (WINAPI *g_pfnNtQueryFullAttributesFile)(MY_OBJECT_ATTRIBUTES *, MY_
MY_NTSTATUS (WINAPI *g_pfnNtSetInformationFile)(HANDLE, MY_IO_STATUS_BLOCK *, PVOID, LONG, MY_FILE_INFORMATION_CLASS);
BOOLEAN (WINAPI *g_pfnRtlDosPathNameToNtPathName_U)(PCWSTR, MY_UNICODE_STRING *, PCWSTR *, MY_RTL_RELATIVE_NAME_U *);
MY_NTSTATUS (WINAPI *g_pfnRtlAnsiStringToUnicodeString)(MY_UNICODE_STRING *, MY_ANSI_STRING const *, BOOLEAN);
+MY_NTSTATUS (WINAPI *g_pfnRtlUnicodeStringToAnsiString)(MY_ANSI_STRING *, MY_UNICODE_STRING *, BOOLEAN);
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);
@@ -67,7 +68,6 @@ ULONG (WINAPI *g_pfnRtlNtStatusToDosError)(MY_NTSTATUS rcNt);
VOID (WINAPI *g_pfnRtlAcquirePebLock)(VOID);
VOID (WINAPI *g_pfnRtlReleasePebLock)(VOID);
-
static struct
{
FARPROC *ppfn;
@@ -87,6 +87,7 @@ static struct
{ (FARPROC *)&g_pfnNtSetInformationFile, "NtSetInformationFile" },
{ (FARPROC *)&g_pfnRtlDosPathNameToNtPathName_U, "RtlDosPathNameToNtPathName_U" },
{ (FARPROC *)&g_pfnRtlAnsiStringToUnicodeString, "RtlAnsiStringToUnicodeString" },
+ { (FARPROC *)&g_pfnRtlUnicodeStringToAnsiString, "RtlUnicodeStringToAnsiString" },
{ (FARPROC *)&g_pfnRtlEqualUnicodeString, "RtlEqualUnicodeString" },
{ (FARPROC *)&g_pfnRtlEqualString, "RtlEqualString" },
{ (FARPROC *)&g_pfnRtlUpperChar, "RtlUpperChar" },
diff --git a/src/lib/nt/ntstat.c b/src/lib/nt/ntstat.c
index 8778cda..dff8e5d 100644
--- a/src/lib/nt/ntstat.c
+++ b/src/lib/nt/ntstat.c
@@ -1,4 +1,4 @@
-/* $Id: ntstat.c 2993 2016-11-01 22:41:26Z bird $ */
+/* $Id: ntstat.c 3019 2017-01-07 00:07:08Z bird $ */
/** @file
* MSC + NT stat, lstat and fstat.
*/
@@ -57,7 +57,7 @@ static int birdIsExecutableExtension(const char *pszExt)
return pszExt[1] == 'a' && pszExt[2] == 't' && pszExt[3] == '\0';
case 'v': /* vbs */
- return pszExt[1] == 'v' && pszExt[2] == 's' && pszExt[3] == '\0';
+ return pszExt[1] == 'b' && pszExt[2] == 's' && pszExt[3] == '\0';
case 'c': /* com and cmd */
return (pszExt[1] == 'o' && pszExt[2] == 'm' && pszExt[3] == '\0')
@@ -68,43 +68,48 @@ static int birdIsExecutableExtension(const char *pszExt)
static int birdIsFileExecutable(const char *pszName)
{
- const char *pszExt = NULL;
- char szExt[8];
- size_t cchExt;
- unsigned i;
- char ch;
+ if (pszName)
+ {
+ const char *pszExt = NULL;
+ char szExt[8];
+ size_t cchExt;
+ unsigned i;
+ char ch;
+
+ /* Look for a 3 char extension. */
+ ch = *pszName++;
+ if (!ch)
+ return 0;
- /* Look for a 3 char extension. */
- ch = *pszName++;
- if (!ch)
- return 0;
+ while ((ch = *pszName++) != '\0')
+ if (ch == '.')
+ pszExt = pszName;
- while ((ch = *pszName++) != '\0')
- if (ch == '.')
- pszExt = pszName;
+ if (!pszExt)
+ return 0;
+ pszExt++;
+ cchExt = pszName - pszExt;
+ if (cchExt != 3)
+ return 0;
- if (!pszExt)
- return 0;
- pszExt++;
- cchExt = pszName - pszExt;
- if (cchExt != 3)
- return 0;
+ /* 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')
+ { /* likely */ }
+ else if (ch >= 'A' && ch <= 'Z')
+ ch += 'a' - 'A';
+ else
+ return 0;
+ szExt[i] = ch;
+ }
+ szExt[i] = '\0';
- /* 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')
- { /* likely */ }
- else if (ch >= 'A' && ch <= 'Z')
- ch += 'a' - 'A';
- else
- return 0;
- szExt[i] = ch;
+ return birdIsExecutableExtension(szExt);
}
- szExt[i] = '\0';
- return birdIsExecutableExtension(szExt);
+ return 0;
}
@@ -147,40 +152,41 @@ static int birdIsFileExecutableW(WCHAR const *pwcName, size_t cwcName)
}
-static unsigned short birdFileInfoToMode(HANDLE hFile, ULONG fAttribs, const char *pszName,
- const wchar_t *pwszName, size_t cbNameW, __int16 *pfIsDirSymlink)
+static unsigned short birdFileInfoToMode(ULONG fAttribs, ULONG uReparseTag,
+ const char *pszName, const wchar_t *pwszName, size_t cbNameW,
+ unsigned __int8 *pfIsDirSymlink, unsigned __int8 *pfIsMountPoint)
{
unsigned short fMode;
/* File type. */
- if ( (fAttribs & FILE_ATTRIBUTE_REPARSE_POINT)
- && hFile != INVALID_HANDLE_VALUE)
+ *pfIsDirSymlink = 0;
+ *pfIsMountPoint = 0;
+ if (!(fAttribs & FILE_ATTRIBUTE_REPARSE_POINT))
{
- MY_FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
- MY_IO_STATUS_BLOCK Ios;
- MY_NTSTATUS rcNt;
- Ios.Information = 0;
- Ios.u.Status = -1;
- rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), MyFileAttributeTagInformation);
- if ( !MY_NT_SUCCESS(rcNt)
- || !MY_NT_SUCCESS(Ios.u.Status)
- || TagInfo.ReparseTag != IO_REPARSE_TAG_SYMLINK)
- fAttribs &= ~FILE_ATTRIBUTE_REPARSE_POINT;
- }
-
- if (fAttribs & FILE_ATTRIBUTE_REPARSE_POINT)
- {
- *pfIsDirSymlink = !!(fAttribs & FILE_ATTRIBUTE_DIRECTORY);
- fMode = S_IFLNK;
- }
- else
- {
- *pfIsDirSymlink = 0;
if (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
fMode = S_IFDIR;
else
fMode = S_IFREG;
}
+ else
+ {
+ switch (uReparseTag)
+ {
+ case IO_REPARSE_TAG_SYMLINK:
+ *pfIsDirSymlink = !!(fAttribs & FILE_ATTRIBUTE_DIRECTORY);
+ fMode = S_IFLNK;
+ break;
+
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ *pfIsMountPoint = 1;
+ default:
+ if (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
+ fMode = S_IFDIR;
+ else
+ fMode = S_IFREG;
+ break;
+ }
+ }
/* Access mask. */
fMode |= S_IROTH | S_IRGRP | S_IRUSR;
@@ -188,7 +194,7 @@ static unsigned short birdFileInfoToMode(HANDLE hFile, ULONG fAttribs, const cha
fMode |= S_IWOTH | S_IWGRP | S_IWUSR;
if ( (fAttribs & FILE_ATTRIBUTE_DIRECTORY)
|| (pwszName
- ? birdIsFileExecutableW(pwszName, cbNameW)
+ ? birdIsFileExecutableW(pwszName, cbNameW / sizeof(wchar_t))
: birdIsFileExecutable(pszName)) )
fMode |= S_IXOTH | S_IXGRP | S_IXUSR;
@@ -201,13 +207,12 @@ static unsigned short birdFileInfoToMode(HANDLE hFile, ULONG fAttribs, const cha
*
* @param pStat The stat structure.
* @param pBuf The MY_FILE_ID_FULL_DIR_INFORMATION entry.
- * @param pszPath Optionally, the path for X bit checks.
* @remarks Caller sets st_dev.
*/
-void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf, const char *pszPath)
+void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf)
{
- pStat->st_mode = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes,
- pszPath, pBuf->FileName, pBuf->FileNameLength, &pStat->st_dirsymlink);
+ pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
+ pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
pStat->st_padding0[0] = 0;
pStat->st_padding0[1] = 0;
pStat->st_size = pBuf->EndOfFile.QuadPart;
@@ -233,13 +238,12 @@ void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_IN
*
* @param pStat The stat structure.
* @param pBuf The MY_FILE_ID_BOTH_DIR_INFORMATION entry.
- * @param pszPath Optionally, the path for X bit checks.
* @remarks Caller sets st_dev.
*/
-void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf, const char *pszPath)
+void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf)
{
- pStat->st_mode = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes,
- pszPath, pBuf->FileName, pBuf->FileNameLength, &pStat->st_dirsymlink);
+ pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
+ pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
pStat->st_padding0[0] = 0;
pStat->st_padding0[1] = 0;
pStat->st_size = pBuf->EndOfFile.QuadPart;
@@ -265,13 +269,12 @@ void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_IN
*
* @param pStat The stat structure.
* @param pBuf The MY_FILE_BOTH_DIR_INFORMATION entry.
- * @param pszPath Optionally, the path for X bit checks.
* @remarks Caller sets st_dev.
*/
-void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf, const char *pszPath)
+void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf)
{
- pStat->st_mode = birdFileInfoToMode(INVALID_HANDLE_VALUE, pBuf->FileAttributes,
- pszPath, pBuf->FileName, pBuf->FileNameLength, &pStat->st_dirsymlink);
+ pStat->st_mode = birdFileInfoToMode(pBuf->FileAttributes, pBuf->EaSize, NULL /*pszPath*/, pBuf->FileName,
+ pBuf->FileNameLength, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
pStat->st_padding0[0] = 0;
pStat->st_padding0[1] = 0;
pStat->st_size = pBuf->EndOfFile.QuadPart;
@@ -309,9 +312,9 @@ int birdStatHandle2(HANDLE hFile, BirdStat_T *pStat, const char *pszPath, const
rcNt = Ios.u.Status;
if (MY_NT_SUCCESS(rcNt))
{
- pStat->st_mode = birdFileInfoToMode(hFile, pAll->BasicInformation.FileAttributes, pszPath,
+ pStat->st_mode = birdFileInfoToMode(pAll->BasicInformation.FileAttributes, pszPath,
pAll->NameInformation.FileNamepAll->NameInformation.FileNameLength,
- &pStat->st_dirsymlink);
+ hFile, &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
pStat->st_padding0[0] = 0;
pStat->st_padding0[1] = 0;
pStat->st_size = pAll->StandardInformation.EndOfFile.QuadPart;
@@ -353,27 +356,47 @@ int birdStatHandle2(HANDLE hFile, BirdStat_T *pStat, const char *pszPath, const
else
rc = birdSetErrnoToNoMem();
#else
- ULONG cbNameInfo = 0;
- MY_FILE_NAME_INFORMATION *pNameInfo = NULL;
- MY_FILE_STANDARD_INFORMATION StdInfo;
- MY_FILE_BASIC_INFORMATION BasicInfo;
- MY_FILE_INTERNAL_INFORMATION InternalInfo;
- MY_IO_STATUS_BLOCK Ios;
+ ULONG cbNameInfo = 0;
+ MY_FILE_NAME_INFORMATION *pNameInfo = NULL;
+ MY_FILE_STANDARD_INFORMATION StdInfo;
+ MY_FILE_BASIC_INFORMATION BasicInfo;
+ MY_FILE_INTERNAL_INFORMATION InternalInfo;
+ MY_FILE_ATTRIBUTE_TAG_INFORMATION TagInfo;
+ MY_IO_STATUS_BLOCK Ios;
Ios.Information = 0;
Ios.u.Status = -1;
rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), MyFileStandardInformation);
if (MY_NT_SUCCESS(rcNt))
rcNt = Ios.u.Status;
+
if (MY_NT_SUCCESS(rcNt))
rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &BasicInfo, sizeof(BasicInfo), MyFileBasicInformation);
if (MY_NT_SUCCESS(rcNt))
rcNt = Ios.u.Status;
+
if (MY_NT_SUCCESS(rcNt))
rcNt = g_pfnNtQueryInformationFile(hFile, &Ios, &InternalInfo, sizeof(InternalInfo), MyFileInternalInformation);
if (MY_NT_SUCCESS(rcNt))
rcNt = Ios.u.Status;
- if (MY_NT_SUCCESS(rcNt) && !pszPath && !pwszPath)
+
+ if (MY_NT_SUCCESS(rcNt))
+ {
+ if (!(BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ TagInfo.ReparseTag = 0;
+ else
+ {
+ MY_NTSTATUS rcNt2 = g_pfnNtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), MyFileAttributeTagInformation);
+ if ( !MY_NT_SUCCESS(rcNt2)
+ || !MY_NT_SUCCESS(Ios.u.Status))
+ TagInfo.ReparseTag = 0;
+ }
+ }
+
+ if ( MY_NT_SUCCESS(rcNt)
+ && !pszPath
+ && !pwszPath
+ && !(BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
cbNameInfo = 0x10020;
pNameInfo = (MY_FILE_NAME_INFORMATION *)alloca(cbNameInfo);
@@ -384,11 +407,11 @@ int birdStatHandle2(HANDLE hFile, BirdStat_T *pStat, const char *pszPath, const
if (MY_NT_SUCCESS(rcNt))
{
- pStat->st_mode = birdFileInfoToMode(hFile, BasicInfo.FileAttributes, pszPath,
+ pStat->st_mode = birdFileInfoToMode(BasicInfo.FileAttributes, TagInfo.ReparseTag, pszPath,
pNameInfo ? pNameInfo->FileName : pwszPath,
pNameInfo ? pNameInfo->FileNameLength
: pwszPath ? wcslen(pwszPath) * sizeof(wchar_t) : 0,
- &pStat->st_dirsymlink);
+ &pStat->st_isdirsymlink, &pStat->st_ismountpoint);
pStat->st_padding0[0] = 0;
pStat->st_padding0[1] = 0;
pStat->st_size = StdInfo.EndOfFile.QuadPart;
@@ -505,6 +528,30 @@ static int birdStatInternal(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat
rc = birdStatHandle2(hFile, pStat, pszPath, NULL);
birdCloseFile(hFile);
+ if (rc || !pStat->st_ismountpoint)
+ { /* very likely */ }
+ else
+ {
+ /*
+ * If we hit a mount point (NTFS volume mounted under an empty NTFS directory),
+ * we should return information about what's mounted there rather than the
+ * directory it is mounted at as this is what UNIX does.
+ */
+ 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,
+ OBJ_CASE_INSENSITIVE);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ rc = birdStatHandle2(hFile, pStat, pszPath, NULL);
+ pStat->st_ismountpoint = 2;
+ birdCloseFile(hFile);
+ }
+ }
+
#if 0
{
static char s_szPrev[256];
@@ -557,7 +604,7 @@ static int birdStatInternal(HANDLE hRoot, const char *pszPath, BirdStat_T *pStat
/*
* Convert the data.
*/
- birdStatFillFromFileIdFullDirInfo(pStat, pBuf, pszPath);
+ birdStatFillFromFileIdFullDirInfo(pStat, pBuf);
/* Get the serial number, reusing the buffer from above. */
rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev);
@@ -596,6 +643,30 @@ static int birdStatInternalW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *
{
rc = birdStatHandle2(hFile, pStat, NULL, pwszPath);
birdCloseFile(hFile);
+
+ if (rc || !pStat->st_ismountpoint)
+ { /* very likely */ }
+ else
+ {
+ /*
+ * If we hit a mount point (NTFS volume mounted under an empty NTFS directory),
+ * we should return information about what's mounted there rather than the
+ * directory it is mounted at as this is what UNIX does.
+ */
+ 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,
+ OBJ_CASE_INSENSITIVE);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ rc = birdStatHandle2(hFile, pStat, NULL, pwszPath);
+ pStat->st_ismountpoint = 2;
+ birdCloseFile(hFile);
+ }
+ }
}
else
{
@@ -635,7 +706,7 @@ static int birdStatInternalW(HANDLE hRoot, const wchar_t *pwszPath, BirdStat_T *
/*
* Convert the data.
*/
- birdStatFillFromFileIdFullDirInfo(pStat, pBuf, NULL);
+ birdStatFillFromFileIdFullDirInfo(pStat, pBuf);
/* Get the serial number, reusing the buffer from above. */
rcNt = birdQueryVolumeDeviceNumber(hFile, (MY_FILE_FS_VOLUME_INFORMATION *)pBuf, cbBuf, &pStat->st_dev);
diff --git a/src/lib/nt/ntstat.h b/src/lib/nt/ntstat.h
index ca18c3b..e400797 100644
--- a/src/lib/nt/ntstat.h
+++ b/src/lib/nt/ntstat.h
@@ -1,4 +1,4 @@
-/* $Id: ntstat.h 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: ntstat.h 3007 2016-11-06 16:46:43Z bird $ */
/** @file
* MSC + NT stat, lstat and fstat implementation and wrappers.
*/
@@ -49,7 +49,8 @@
typedef struct BirdStat
{
unsigned __int16 st_mode;
- unsigned __int16 st_dirsymlink;
+ unsigned __int8 st_isdirsymlink; /**< Set if directory symlink. */
+ unsigned __int8 st_ismountpoint; /**< Set if mount point; 1 if not followed, 2 if followed (lstat & readdir only). */
unsigned __int16 st_padding0[2];
__int64 st_size;
BirdTimeSpec_T st_atim;
@@ -86,9 +87,9 @@ int birdStatOnFdJustSize(int fd, __int64 *pcbFile);
int birdStatModTimeOnly(const char *pszPath, BirdTimeSpec_T *pTimeSpec, int fFollowLink);
#ifdef ___nt_ntstuff_h
int birdStatHandle(HANDLE hFile, BirdStat_T *pStat, const char *pszPath);
-void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf, const char *pszPath);
-void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf, const char *pszPath);
-void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf, const char *pszPath);
+void birdStatFillFromFileIdFullDirInfo(BirdStat_T *pStat, MY_FILE_ID_FULL_DIR_INFORMATION const *pBuf);
+void birdStatFillFromFileIdBothDirInfo(BirdStat_T *pStat, MY_FILE_ID_BOTH_DIR_INFORMATION const *pBuf);
+void birdStatFillFromFileBothDirInfo(BirdStat_T *pStat, MY_FILE_BOTH_DIR_INFORMATION const *pBuf);
MY_NTSTATUS birdQueryVolumeDeviceNumber(HANDLE hFile, MY_FILE_FS_VOLUME_INFORMATION *pVolInfo, size_t cbVolInfo,
unsigned __int64 *puDevNo);
unsigned __int64 birdVolumeInfoToDeviceNumber(MY_FILE_FS_VOLUME_INFORMATION const *pVolInfo);
diff --git a/src/lib/nt/ntstuff.h b/src/lib/nt/ntstuff.h
index c1a9be3..a70d349 100644
--- a/src/lib/nt/ntstuff.h
+++ b/src/lib/nt/ntstuff.h
@@ -1,4 +1,4 @@
-/* $Id: ntstuff.h 2985 2016-11-01 18:26:35Z bird $ */
+/* $Id: ntstuff.h 3021 2017-01-07 16:52:16Z bird $ */
/** @file
* Definitions, types, prototypes and globals for NT.
*/
@@ -518,10 +518,10 @@ typedef struct 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
+#elif K_ARCH == K_ARCH_X86_32
# define MY_NT_READ_TEB_WORKER(a_offTebMember) ( __readfsdword(a_offTebMember) )
#else
-# else "Port me!"
+# error "Port me!"
#endif
/** Get the PEB pointer.
* @remark Needs stddef.h. */
@@ -555,6 +555,7 @@ extern MY_NTSTATUS (WINAPI * g_pfnNtQueryFullAttributesFile)(MY_OBJECT_ATTRIBUTE
extern MY_NTSTATUS (WINAPI * g_pfnNtSetInformationFile)(HANDLE, MY_IO_STATUS_BLOCK *, PVOID, LONG, MY_FILE_INFORMATION_CLASS);
extern BOOLEAN (WINAPI * g_pfnRtlDosPathNameToNtPathName_U)(PCWSTR, MY_UNICODE_STRING *, PCWSTR *, MY_RTL_RELATIVE_NAME_U *);
extern MY_NTSTATUS (WINAPI * g_pfnRtlAnsiStringToUnicodeString)(MY_UNICODE_STRING *, MY_ANSI_STRING const *, BOOLEAN);
+extern MY_NTSTATUS (WINAPI * g_pfnRtlUnicodeStringToAnsiString)(MY_ANSI_STRING *, MY_UNICODE_STRING *, BOOLEAN);
extern BOOLEAN (WINAPI * g_pfnRtlEqualUnicodeString)(MY_UNICODE_STRING const *pUniStr1, MY_UNICODE_STRING const *pUniStr2,
BOOLEAN fCaseInsensitive);
extern BOOLEAN (WINAPI * g_pfnRtlEqualString)(MY_ANSI_STRING const *pAnsiStr1, MY_ANSI_STRING const *pAnsiStr2,
diff --git a/src/lib/nt/ntunlink.c b/src/lib/nt/ntunlink.c
index 622f47e..50921bd 100644
--- a/src/lib/nt/ntunlink.c
+++ b/src/lib/nt/ntunlink.c
@@ -1,4 +1,4 @@
-/* $Id: ntunlink.c 2997 2016-11-01 23:28:02Z bird $ */
+/* $Id: ntunlink.c 3009 2016-11-07 02:21:59Z bird $ */
/** @file
* MSC + NT unlink and variations.
*/
@@ -82,7 +82,7 @@ static MY_NTSTATUS birdMakeWritable(MY_UNICODE_STRING *pNtPath)
}
-static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, int fReadOnlyToo, int fFast)
+static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, const wchar_t *pwszFile, int fReadOnlyToo, int fFast)
{
MY_UNICODE_STRING NtPath;
int rc;
@@ -90,9 +90,19 @@ static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, int fReadOnlyTo
if (hRoot == INVALID_HANDLE_VALUE)
hRoot = NULL;
if (hRoot == NULL)
- rc = birdDosToNtPath(pszFile, &NtPath);
+ {
+ if (pwszFile)
+ rc = birdDosToNtPathW(pwszFile, &NtPath);
+ else
+ rc = birdDosToNtPath(pszFile, &NtPath);
+ }
else
- rc = birdDosToRelativeNtPath(pszFile, &NtPath);
+ {
+ if (pwszFile)
+ rc = birdDosToRelativeNtPathW(pwszFile, &NtPath);
+ else
+ rc = birdDosToRelativeNtPath(pszFile, &NtPath);
+ }
if (rc == 0)
{
MY_NTSTATUS rcNt;
@@ -161,36 +171,72 @@ static int birdUnlinkInternal(HANDLE hRoot, const char *pszFile, int fReadOnlyTo
int birdUnlink(const char *pszFile)
{
- return birdUnlinkInternal(NULL /*hRoot*/, pszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
+ return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
+}
+
+
+int birdUnlinkW(const wchar_t *pwszFile)
+{
+ return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pwszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
}
int birdUnlinkEx(void *hRoot, const char *pszFile)
{
- return birdUnlinkInternal((HANDLE)hRoot, pszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
+ return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
+}
+
+
+int birdUnlinkExW(void *hRoot, const wchar_t *pwszFile)
+{
+ return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 0 /*fReadOnlyToo*/, 0 /*fFast*/);
}
int birdUnlinkForced(const char *pszFile)
{
- return birdUnlinkInternal(NULL /*hRoot*/, pszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
+ return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
+}
+
+
+int birdUnlinkForcedW(const wchar_t *pwszFile)
+{
+ return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
}
int birdUnlinkForcedEx(void *hRoot, const char *pszFile)
{
- return birdUnlinkInternal((HANDLE)hRoot, pszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
+ return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
+}
+
+
+int birdUnlinkForcedExW(void *hRoot, const wchar_t *pwszFile)
+{
+ return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 0 /*fFast*/);
}
int birdUnlinkForcedFast(const char *pszFile)
{
- return birdUnlinkInternal(NULL /*hRoot*/, pszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
+ return birdUnlinkInternal(NULL /*hRoot*/, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
+}
+
+
+int birdUnlinkForcedFastW(const wchar_t *pwszFile)
+{
+ return birdUnlinkInternal(NULL /*hRoot*/, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
}
int birdUnlinkForcedFastEx(void *hRoot, const char *pszFile)
{
- return birdUnlinkInternal((HANDLE)hRoot, pszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
+ return birdUnlinkInternal((HANDLE)hRoot, pszFile, NULL /*pwszFile*/, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
+}
+
+
+int birdUnlinkForcedFastExW(void *hRoot, const wchar_t *pwszFile)
+{
+ return birdUnlinkInternal((HANDLE)hRoot, NULL /*pszFile*/, pwszFile, 1 /*fReadOnlyToo*/, 1 /*fFast*/);
}
diff --git a/src/lib/nt/ntunlink.h b/src/lib/nt/ntunlink.h
index 195ef7f..035a028 100644
--- a/src/lib/nt/ntunlink.h
+++ b/src/lib/nt/ntunlink.h
@@ -1,4 +1,4 @@
-/* $Id: ntunlink.h 2997 2016-11-01 23:28:02Z bird $ */
+/* $Id: ntunlink.h 3009 2016-11-07 02:21:59Z bird $ */
/** @file
* MSC + NT unlink and variations.
*/
@@ -34,11 +34,17 @@
#include "nttypes.h"
int birdUnlink(const char *pszFile);
+int birdUnlinkW(const wchar_t *pwszFile);
int birdUnlinkEx(void *hRoot, const char *pszFile);
+int birdUnlinkExW(void *hRoot, const wchar_t *pwszFile);
int birdUnlinkForced(const char *pszFile);
+int birdUnlinkForcedW(const wchar_t *pwszFile);
int birdUnlinkForcedEx(void *hRoot, const char *pszFile);
+int birdUnlinkForcedExW(void *hRoot, const wchar_t *pszFile);
int birdUnlinkForcedFast(const char *pszFile);
+int birdUnlinkForcedFastW(const wchar_t *pwszFile);
int birdUnlinkForcedFastEx(void *hRoot, const char *pszFile);
+int birdUnlinkForcedFastExW(void *hRoot, const wchar_t *pwszFile);
#undef unlink
#define unlink(a_pszPath) birdUnlinkForced(a_pszPath)
diff --git a/src/lib/nt/tstNtFts.c b/src/lib/nt/tstNtFts.c
index d2fed4c..8d8136c 100644
--- a/src/lib/nt/tstNtFts.c
+++ b/src/lib/nt/tstNtFts.c
@@ -70,7 +70,6 @@ int main(int argc, char **argv)
pszArg++;
if (chOpt == '-')
{
- chOpt = *pszArg++;
if (!chOpt)
{
fDoneOptions = 1;
@@ -98,9 +97,11 @@ int main(int argc, char **argv)
chOpt = 'q';
else if (strcmp(pszArg, "verbose") == 0)
chOpt = 'v';
+ else if (strcmp(pszArg, "no-ansi") == 0)
+ chOpt = 'w';
else
{
- fprintf(stderr, "syntax error: Unknown option: --%s\n", pszArg);
+ fprintf(stderr, "syntax error: Unknown option: %s (%s)\n", argv[i], pszArg);
return 2;
}
pszArg = "";
@@ -137,6 +138,11 @@ int main(int argc, char **argv)
case 'x':
fFtsFlags |= FTS_XDEV;
break;
+#ifdef FTS_NO_ANSI
+ case 'w':
+ fFtsFlags |= FTS_NO_ANSI;
+ break;
+#endif
case 'L':
fFollowLinks = 1;
break;
@@ -205,7 +211,14 @@ int main(int argc, char **argv)
}
if (cVerbosity > 0)
- printf("%8s %s\n", pszState, pFtsEnt->fts_accpath);
+ {
+#ifdef FTS_NO_ANSI
+ if (fFtsFlags & FTS_NO_ANSI)
+ printf("%8s %ls\n", pszState, pFtsEnt->fts_wcsaccpath);
+ else
+#endif
+ printf("%8s %s\n", pszState, pFtsEnt->fts_accpath);
+ }
if ( pFtsEnt->fts_info == FTS_SL
&& pFtsEnt->fts_number == 0
&& fFollowLinks
diff --git a/src/lib/nt/tstNtStat.c b/src/lib/nt/tstNtStat.c
index 2a830f1..3823ab7 100644
--- a/src/lib/nt/tstNtStat.c
+++ b/src/lib/nt/tstNtStat.c
@@ -1,4 +1,4 @@
-/* $Id: tstNtStat.c 2702 2013-11-21 00:11:08Z bird $ */
+/* $Id: tstNtStat.c 3007 2016-11-06 16:46:43Z bird $ */
/** @file
* Manual lstat/stat testcase.
*/
@@ -130,7 +130,8 @@ int main(int argc, char **argv)
char szBuf[256];
printf("%s:\n", argv[i]);
printf(" st_mode: %o\n", st.st_mode);
- printf(" st_dirsymlink: %d\n", st.st_dirsymlink);
+ printf(" st_isdirsymlink: %d\n", st.st_isdirsymlink);
+ printf(" st_ismountpoint: %d\n", st.st_ismountpoint);
printf(" st_size: %I64u (%#I64x)\n", st.st_size, st.st_size);
printf(" st_atim: %s\n", FormatTimeSpec(szBuf, &st.st_atim));
printf(" st_mtim: %s\n", FormatTimeSpec(szBuf, &st.st_mtim));
--
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