diff options
Diffstat (limited to 'contrib')
40 files changed, 9197 insertions, 809 deletions
diff --git a/contrib/Makefile.am b/contrib/Makefile.am deleted file mode 100644 index a3d5cfbf855..00000000000 --- a/contrib/Makefile.am +++ /dev/null @@ -1,3 +0,0 @@ -SUBDIRS = fuse-util - -CLEANFILES = diff --git a/contrib/aclocal/mkdirp.m4 b/contrib/aclocal/mkdirp.m4 new file mode 100644 index 00000000000..d2f7edd5ccd --- /dev/null +++ b/contrib/aclocal/mkdirp.m4 @@ -0,0 +1,146 @@ +# Excerpt from autoconf/autoconf/programs.m4 +# This file is part of Autoconf. -*- Autoconf -*- +# Checking for programs. + +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software +# Foundation, Inc. + +# This file is part of Autoconf. This program is free +# software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# Under Section 7 of GPL version 3, you are granted additional +# permissions described in the Autoconf Configure Script Exception, +# version 3.0, as published by the Free Software Foundation. +# +# You should have received a copy of the GNU General Public License +# and a copy of the Autoconf Configure Script Exception along with +# this program; see the files COPYINGv3 and COPYING.EXCEPTION +# respectively. If not, see <http://www.gnu.org/licenses/>. + +# Written by David MacKenzie, with help from +# Franc,ois Pinard, Karl Berry, Richard Pixley, Ian Lance Taylor, +# Roland McGrath, Noah Friedman, david d zuhn, and many others. + +# AC_PROG_MKDIR_P +# --------------- +# Check whether `mkdir -p' is known to be thread-safe, and fall back to +# install-sh -d otherwise. +# +# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories +# created by `make install' are always world readable, even if the +# installer happens to have an overly restrictive umask (e.g. 077). +# This was a mistake. There are at least two reasons why we must not +# use `-m 0755': +# - it causes special bits like SGID to be ignored, +# - it may be too restrictive (some setups expect 775 directories). +# +# Do not use -m 0755 and let people choose whatever they expect by +# setting umask. +# +# We cannot accept any implementation of `mkdir' that recognizes `-p'. +# Some implementations (such as Solaris 8's) are vulnerable to race conditions: +# if a parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c' +# concurrently, both version can detect that a/ is missing, but only +# one can create it and the other will error out. Consequently we +# restrict ourselves to known race-free implementations. +# +# Automake used to define mkdir_p as `mkdir -p .', in order to +# allow $(mkdir_p) to be used without argument. As in +# $(mkdir_p) $(somedir) +# where $(somedir) is conditionally defined. However we don't do +# that for MKDIR_P. +# 1. before we restricted the check to GNU mkdir, `mkdir -p .' was +# reported to fail in read-only directories. The system where this +# happened has been forgotten. +# 2. in practice we call $(MKDIR_P) on directories such as +# $(MKDIR_P) "$(DESTDIR)$(somedir)" +# and we don't want to create $(DESTDIR) if $(somedir) is empty. +# To support the latter case, we have to write +# test -z "$(somedir)" || $(MKDIR_P) "$(DESTDIR)$(somedir)" +# so $(MKDIR_P) always has an argument. +# We will have better chances of detecting a missing test if +# $(MKDIR_P) complains about missing arguments. +# 3. $(MKDIR_P) is named after `mkdir -p' and we don't expect this +# to accept no argument. +# 4. having something like `mkdir .' in the output is unsightly. +# +# On NextStep and OpenStep, the `mkdir' command does not +# recognize any option. It will interpret all options as +# directories to create. +AN_MAKEVAR([MKDIR_P], [AC_PROG_MKDIR_P]) +AC_DEFUN_ONCE([AC_PROG_MKDIR_P], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl + +AC_MSG_CHECKING([for a thread-safe mkdir -p]) +if test -z "$MKDIR_P"; then + AC_CACHE_VAL([ac_cv_path_mkdir], + [_AS_PATH_WALK([$PATH$PATH_SEPARATOR/opt/sfw/bin], + [for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + AS_EXECUTABLE_P(["$as_dir/$ac_prog$ac_exec_ext"]) || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done])]) + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +dnl status.m4 does special magic for MKDIR_P instead of AC_SUBST, +dnl to get relative names right. However, also AC_SUBST here so +dnl that Automake versions before 1.10 will pick it up (they do not +dnl trace AC_SUBST_TRACE). +dnl FIXME: Remove this once we drop support for Automake < 1.10. +AC_SUBST([MKDIR_P])dnl +AC_MSG_RESULT([$MKDIR_P]) +])# AC_PROG_MKDIR_P + + +# From automake/m4/mkdirp.m4 +## -*- Autoconf -*- +# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_MKDIR_P +# --------------- +# Check for `mkdir -p'. +AC_DEFUN([AM_PROG_MKDIR_P], +[ +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, +dnl while keeping a definition of mkdir_p for backward compatibility. +dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. +dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of +dnl Makefile.ins that do not define MKDIR_P, so we do our own +dnl adjustment using top_builddir (which is defined more often than +dnl MKDIR_P). +AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl +case $mkdir_p in + [[\\/$]]* | ?:[[\\/]]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac +]) diff --git a/contrib/aclocal/python.m4 b/contrib/aclocal/python.m4 new file mode 100644 index 00000000000..a39a9009046 --- /dev/null +++ b/contrib/aclocal/python.m4 @@ -0,0 +1,209 @@ +## ------------------------ -*- Autoconf -*- +## Python file handling +## From Andrew Dalke +## Updated by James Henstridge +## ------------------------ +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# --------------------------------------------------------------------------- +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. +# +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. +# +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). +# +# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. +AC_DEFUN([AM_PATH_PYTHON], + [ + dnl Find a Python interpreter. Python versions prior to 2.0 are not + dnl supported. (2.0 was released on October 16, 2000). + m4_define_default([_AM_PYTHON_INTERPRETER_LIST], + [python python2 python3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 dnl +python2.1 python2.0]) + + m4_if([$1],[],[ + dnl No version check is needed. + # Find any Python interpreter. + if test -z "$PYTHON"; then + AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) + fi + am_display_PYTHON=python + ], [ + dnl A version check is needed. + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + AC_MSG_CHECKING([whether $PYTHON version >= $1]) + AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], + [AC_MSG_RESULT(yes)], + [AC_MSG_ERROR(too old)]) + am_display_PYTHON=$PYTHON + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + AC_CACHE_CHECK([for a Python interpreter with version >= $1], + [am_cv_pathless_PYTHON],[ + for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do + test "$am_cv_pathless_PYTHON" = none && break + AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) + done]) + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + if test "$am_cv_pathless_PYTHON" = none; then + PYTHON=: + else + AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) + fi + am_display_PYTHON=$am_cv_pathless_PYTHON + fi + ]) + + if test "$PYTHON" = :; then + dnl Run any user-specified action, or abort. + m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) + else + + dnl Query Python for its version number. Getting [:3] seems to be + dnl the best way to do this; it's what "site.py" does in the standard + dnl library. + + AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], + [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`]) + AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST([PYTHON_PREFIX], ['${prefix}']) + AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], + [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`]) + AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) + + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behavior + dnl is more consistent with lispdir.m4 for example. + dnl Query distutils for this directory. distutils does not exist in + dnl Python 1.5, so we fall back to the hardcoded directory if it + dnl doesn't work. + AC_CACHE_CHECK([for $am_display_PYTHON script directory], + [am_cv_python_pythondir], + [if test "x$prefix" = xNONE + then + am_py_prefix=$ac_default_prefix + else + am_py_prefix=$prefix + fi + am_cv_python_pythondir=`$PYTHON -c "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.get_python_lib(0,0,prefix='$am_py_prefix'))" 2>/dev/null || + echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"` + case $am_cv_python_pythondir in + $am_py_prefix*) + am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` + am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` + ;; + *) + case $am_py_prefix in + /usr|/System*) ;; + *) + am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + ]) + AC_SUBST([pythondir], [$am_cv_python_pythondir]) + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + + AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) + dnl Query distutils for this directory. distutils does not exist in + dnl Python 1.5, so we fall back to the hardcoded directory if it + dnl doesn't work. + AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], + [am_cv_python_pyexecdir], + [if test "x$exec_prefix" = xNONE + then + am_py_exec_prefix=$am_py_prefix + else + am_py_exec_prefix=$exec_prefix + fi + am_cv_python_pyexecdir=`$PYTHON -c "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.get_python_lib(1,0,prefix='$am_py_exec_prefix'))" 2>/dev/null || + echo "$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages"` + case $am_cv_python_pyexecdir in + $am_py_exec_prefix*) + am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` + am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` + ;; + *) + case $am_py_exec_prefix in + /usr|/System*) ;; + *) + am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + ]) + AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + + AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) + + dnl Run any user-specified action. + $2 + fi + +]) + + +# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# --------------------------------------------------------------------------- +# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. +# Run ACTION-IF-FALSE otherwise. +# This test uses sys.hexversion instead of the string equivalent (first +# word of sys.version), in order to cope with versions such as 2.2c1. +# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000). +AC_DEFUN([AM_PYTHON_CHECK_VERSION], + [prog="import sys +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +# map returns an iterator in Python 3.0 and a list in 2.x +minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]] +minverhex = 0 +# xrange is not present in Python 3.0 and range returns an iterator +for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]] +sys.exit(sys.hexversion < minverhex)" + AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) diff --git a/contrib/fuse-include/fuse-misc.h b/contrib/fuse-include/fuse-misc.h index 16b47347262..f905215b0a1 100644 --- a/contrib/fuse-include/fuse-misc.h +++ b/contrib/fuse-include/fuse-misc.h @@ -10,4 +10,5 @@ unsigned long calc_timeout_sec (double t); unsigned int calc_timeout_nsec (double t); -void convert_fuse_file_lock (struct fuse_file_lock *fl, struct flock *flock); +void convert_fuse_file_lock (struct fuse_file_lock *fl, struct gf_flock *flock, + uint64_t lk_owner); diff --git a/contrib/fuse-include/fuse-mount.h b/contrib/fuse-include/fuse-mount.h index 6aaab01aac6..7d28462de47 100644 --- a/contrib/fuse-include/fuse-mount.h +++ b/contrib/fuse-include/fuse-mount.h @@ -1,11 +1,13 @@ /* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> - Copyright (c) 2009 Gluster, Inc. <http://www.gluster.com> + Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB. */ void gf_fuse_unmount (const char *mountpoint, int fd); -int gf_fuse_mount (const char *mountpoint, char *fsname, char *mnt_param); +int gf_fuse_unmount_daemon (const char *mountpoint, int fd); +int gf_fuse_mount (const char *mountpoint, char *fsname, char *mnt_param, + pid_t *mtab_pid, int status_fd); diff --git a/contrib/fuse-include/fuse_kernel.h b/contrib/fuse-include/fuse_kernel.h index 9ae25d6f9c0..1e41e237e6f 100644 --- a/contrib/fuse-include/fuse_kernel.h +++ b/contrib/fuse-include/fuse_kernel.h @@ -60,23 +60,87 @@ * 7.13 * - make max number of background requests and congestion threshold * tunables + * + * 7.14 + * - add splice support to fuse device + * + * 7.15 + * - add store notify + * - add retrieve notify + * + * 7.16 + * - add BATCH_FORGET request + * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct + * fuse_ioctl_iovec' instead of ambiguous 'struct iovec' + * - add FUSE_IOCTL_32BIT flag + * + * 7.17 + * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK + * + * 7.18 + * - add FUSE_IOCTL_DIR flag + * - add FUSE_NOTIFY_DELETE + * + * 7.19 + * - add FUSE_FALLOCATE + * + * 7.20 + * - add FUSE_AUTO_INVAL_DATA + * + * 7.21 + * - add FUSE_READDIRPLUS + * - send the requested events in POLL request + * + * 7.22 + * - add FUSE_ASYNC_DIO + * + * 7.23 + * - add FUSE_WRITEBACK_CACHE + * - add time_gran to fuse_init_out + * - add reserved space to fuse_init_out + * - add FATTR_CTIME + * - add ctime and ctimensec to fuse_setattr_in + * - add FUSE_RENAME2 request + * - add FUSE_NO_OPEN_SUPPORT flag + * + * 7.24 + * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support */ #ifndef _LINUX_FUSE_H #define _LINUX_FUSE_H -#include <sys/types.h> -#define __u64 uint64_t -#define __s64 int64_t -#define __u32 uint32_t -#define __s32 int32_t -#define __u16 uint16_t +#ifdef __KERNEL__ +#include <linux/types.h> +#else +#include <stdint.h> +#endif + +/* + * Version negotiation: + * + * Both the kernel and userspace send the version they support in the + * INIT request and reply respectively. + * + * If the major versions match then both shall use the smallest + * of the two minor versions for communication. + * + * If the kernel supports a larger major version, then userspace shall + * reply with the major version it supports, ignore the rest of the + * INIT message and expect a new INIT message from the kernel with a + * matching major version. + * + * If the library supports a larger major version, then it shall fall + * back to the major protocol version sent by the kernel for + * communication and reply with that major version (and an arbitrary + * supported minor version). + */ /** Version number of this interface */ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 13 +#define FUSE_KERNEL_MINOR_VERSION 24 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -85,42 +149,42 @@ userspace works under 64bit kernels */ struct fuse_attr { - __u64 ino; - __u64 size; - __u64 blocks; - __u64 atime; - __u64 mtime; - __u64 ctime; - __u32 atimensec; - __u32 mtimensec; - __u32 ctimensec; - __u32 mode; - __u32 nlink; - __u32 uid; - __u32 gid; - __u32 rdev; - __u32 blksize; - __u32 padding; + uint64_t ino; + uint64_t size; + uint64_t blocks; + uint64_t atime; + uint64_t mtime; + uint64_t ctime; + uint32_t atimensec; + uint32_t mtimensec; + uint32_t ctimensec; + uint32_t mode; + uint32_t nlink; + uint32_t uid; + uint32_t gid; + uint32_t rdev; + uint32_t blksize; + uint32_t padding; }; struct fuse_kstatfs { - __u64 blocks; - __u64 bfree; - __u64 bavail; - __u64 files; - __u64 ffree; - __u32 bsize; - __u32 namelen; - __u32 frsize; - __u32 padding; - __u32 spare[6]; + uint64_t blocks; + uint64_t bfree; + uint64_t bavail; + uint64_t files; + uint64_t ffree; + uint32_t bsize; + uint32_t namelen; + uint32_t frsize; + uint32_t padding; + uint32_t spare[6]; }; struct fuse_file_lock { - __u64 start; - __u64 end; - __u32 type; - __u32 pid; /* tgid */ + uint64_t start; + uint64_t end; + uint32_t type; + uint32_t pid; /* tgid */ }; /** @@ -136,6 +200,7 @@ struct fuse_file_lock { #define FATTR_ATIME_NOW (1 << 7) #define FATTR_MTIME_NOW (1 << 8) #define FATTR_LOCKOWNER (1 << 9) +#define FATTR_CTIME (1 << 10) /** * Flags returned by the OPEN request @@ -151,8 +216,24 @@ struct fuse_file_lock { /** * INIT request/reply flags * + * FUSE_ASYNC_READ: asynchronous read requests + * FUSE_POSIX_LOCKS: remote locking for POSIX file locks + * FUSE_FILE_OPS: kernel sends file handle for fstat, etc... (not yet supported) + * FUSE_ATOMIC_O_TRUNC: handles the O_TRUNC open flag in the filesystem * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." + * FUSE_BIG_WRITES: filesystem can handle write size larger than 4kB * FUSE_DONT_MASK: don't apply umask to file mode on create operations + * FUSE_SPLICE_WRITE: kernel supports splice write on the device + * FUSE_SPLICE_MOVE: kernel supports splice move on the device + * FUSE_SPLICE_READ: kernel supports splice read on the device + * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks + * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories + * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages + * FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one) + * FUSE_READDIRPLUS_AUTO: adaptive readdirplus + * FUSE_ASYNC_DIO: asynchronous direct I/O submission + * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes + * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -161,6 +242,17 @@ struct fuse_file_lock { #define FUSE_EXPORT_SUPPORT (1 << 4) #define FUSE_BIG_WRITES (1 << 5) #define FUSE_DONT_MASK (1 << 6) +#define FUSE_SPLICE_WRITE (1 << 7) +#define FUSE_SPLICE_MOVE (1 << 8) +#define FUSE_SPLICE_READ (1 << 9) +#define FUSE_FLOCK_LOCKS (1 << 10) +#define FUSE_HAS_IOCTL_DIR (1 << 11) +#define FUSE_AUTO_INVAL_DATA (1 << 12) +#define FUSE_DO_READDIRPLUS (1 << 13) +#define FUSE_READDIRPLUS_AUTO (1 << 14) +#define FUSE_ASYNC_DIO (1 << 15) +#define FUSE_WRITEBACK_CACHE (1 << 16) +#define FUSE_NO_OPEN_SUPPORT (1 << 17) /** * CUSE INIT request/reply flags @@ -173,6 +265,7 @@ struct fuse_file_lock { * Release flags */ #define FUSE_RELEASE_FLUSH (1 << 0) +#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1) /** * Getattr flags @@ -204,12 +297,16 @@ struct fuse_file_lock { * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed * FUSE_IOCTL_RETRY: retry with new iovecs + * FUSE_IOCTL_32BIT: 32bit ioctl + * FUSE_IOCTL_DIR: is a directory * * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs */ #define FUSE_IOCTL_COMPAT (1 << 0) #define FUSE_IOCTL_UNRESTRICTED (1 << 1) #define FUSE_IOCTL_RETRY (1 << 2) +#define FUSE_IOCTL_32BIT (1 << 3) +#define FUSE_IOCTL_DIR (1 << 4) #define FUSE_IOCTL_MAX_IOV 256 @@ -259,6 +356,12 @@ enum fuse_opcode { FUSE_DESTROY = 38, FUSE_IOCTL = 39, FUSE_POLL = 40, + FUSE_NOTIFY_REPLY = 41, + FUSE_BATCH_FORGET = 42, + FUSE_FALLOCATE = 43, + FUSE_READDIRPLUS = 44, + FUSE_RENAME2 = 45, + FUSE_LSEEK = 46, /* CUSE specific operations */ CUSE_INIT = 4096, @@ -268,6 +371,9 @@ enum fuse_notify_code { FUSE_NOTIFY_POLL = 1, FUSE_NOTIFY_INVAL_INODE = 2, FUSE_NOTIFY_INVAL_ENTRY = 3, + FUSE_NOTIFY_STORE = 4, + FUSE_NOTIFY_RETRIEVE = 5, + FUSE_NOTIFY_DELETE = 6, FUSE_NOTIFY_CODE_MAX, }; @@ -277,133 +383,149 @@ enum fuse_notify_code { #define FUSE_COMPAT_ENTRY_OUT_SIZE 120 struct fuse_entry_out { - __u64 nodeid; /* Inode ID */ - __u64 generation; /* Inode generation: nodeid:gen must - be unique for the fs's lifetime */ - __u64 entry_valid; /* Cache timeout for the name */ - __u64 attr_valid; /* Cache timeout for the attributes */ - __u32 entry_valid_nsec; - __u32 attr_valid_nsec; + uint64_t nodeid; /* Inode ID */ + uint64_t generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + uint64_t entry_valid; /* Cache timeout for the name */ + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t entry_valid_nsec; + uint32_t attr_valid_nsec; struct fuse_attr attr; }; struct fuse_forget_in { - __u64 nlookup; + uint64_t nlookup; +}; + +struct fuse_forget_one { + uint64_t nodeid; + uint64_t nlookup; +}; + +struct fuse_batch_forget_in { + uint32_t count; + uint32_t dummy; }; struct fuse_getattr_in { - __u32 getattr_flags; - __u32 dummy; - __u64 fh; + uint32_t getattr_flags; + uint32_t dummy; + uint64_t fh; }; #define FUSE_COMPAT_ATTR_OUT_SIZE 96 struct fuse_attr_out { - __u64 attr_valid; /* Cache timeout for the attributes */ - __u32 attr_valid_nsec; - __u32 dummy; + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t attr_valid_nsec; + uint32_t dummy; struct fuse_attr attr; }; #define FUSE_COMPAT_MKNOD_IN_SIZE 8 struct fuse_mknod_in { - __u32 mode; - __u32 rdev; - __u32 umask; - __u32 padding; + uint32_t mode; + uint32_t rdev; + uint32_t umask; + uint32_t padding; }; struct fuse_mkdir_in { - __u32 mode; - __u32 umask; + uint32_t mode; + uint32_t umask; }; struct fuse_rename_in { - __u64 newdir; + uint64_t newdir; +}; + +struct fuse_rename2_in { + uint64_t newdir; + uint32_t flags; + uint32_t padding; }; struct fuse_link_in { - __u64 oldnodeid; + uint64_t oldnodeid; }; struct fuse_setattr_in { - __u32 valid; - __u32 padding; - __u64 fh; - __u64 size; - __u64 lock_owner; - __u64 atime; - __u64 mtime; - __u64 unused2; - __u32 atimensec; - __u32 mtimensec; - __u32 unused3; - __u32 mode; - __u32 unused4; - __u32 uid; - __u32 gid; - __u32 unused5; + uint32_t valid; + uint32_t padding; + uint64_t fh; + uint64_t size; + uint64_t lock_owner; + uint64_t atime; + uint64_t mtime; + uint64_t ctime; + uint32_t atimensec; + uint32_t mtimensec; + uint32_t ctimensec; + uint32_t mode; + uint32_t unused4; + uint32_t uid; + uint32_t gid; + uint32_t unused5; }; struct fuse_open_in { - __u32 flags; - __u32 unused; + uint32_t flags; + uint32_t unused; }; struct fuse_create_in { - __u32 flags; - __u32 mode; - __u32 umask; - __u32 padding; + uint32_t flags; + uint32_t mode; + uint32_t umask; + uint32_t padding; }; struct fuse_open_out { - __u64 fh; - __u32 open_flags; - __u32 padding; + uint64_t fh; + uint32_t open_flags; + uint32_t padding; }; struct fuse_release_in { - __u64 fh; - __u32 flags; - __u32 release_flags; - __u64 lock_owner; + uint64_t fh; + uint32_t flags; + uint32_t release_flags; + uint64_t lock_owner; }; struct fuse_flush_in { - __u64 fh; - __u32 unused; - __u32 padding; - __u64 lock_owner; + uint64_t fh; + uint32_t unused; + uint32_t padding; + uint64_t lock_owner; }; struct fuse_read_in { - __u64 fh; - __u64 offset; - __u32 size; - __u32 read_flags; - __u64 lock_owner; - __u32 flags; - __u32 padding; + uint64_t fh; + uint64_t offset; + uint32_t size; + uint32_t read_flags; + uint64_t lock_owner; + uint32_t flags; + uint32_t padding; }; #define FUSE_COMPAT_WRITE_IN_SIZE 24 struct fuse_write_in { - __u64 fh; - __u64 offset; - __u32 size; - __u32 write_flags; - __u64 lock_owner; - __u32 flags; - __u32 padding; + uint64_t fh; + uint64_t offset; + uint32_t size; + uint32_t write_flags; + uint64_t lock_owner; + uint32_t flags; + uint32_t padding; }; struct fuse_write_out { - __u32 size; - __u32 padding; + uint32_t size; + uint32_t padding; }; #define FUSE_COMPAT_STATFS_SIZE 48 @@ -413,32 +535,32 @@ struct fuse_statfs_out { }; struct fuse_fsync_in { - __u64 fh; - __u32 fsync_flags; - __u32 padding; + uint64_t fh; + uint32_t fsync_flags; + uint32_t padding; }; struct fuse_setxattr_in { - __u32 size; - __u32 flags; + uint32_t size; + uint32_t flags; }; struct fuse_getxattr_in { - __u32 size; - __u32 padding; + uint32_t size; + uint32_t padding; }; struct fuse_getxattr_out { - __u32 size; - __u32 padding; + uint32_t size; + uint32_t padding; }; struct fuse_lk_in { - __u64 fh; - __u64 owner; + uint64_t fh; + uint64_t owner; struct fuse_file_lock lk; - __u32 lk_flags; - __u32 padding; + uint32_t lk_flags; + uint32_t padding; }; struct fuse_lk_out { @@ -446,134 +568,206 @@ struct fuse_lk_out { }; struct fuse_access_in { - __u32 mask; - __u32 padding; + uint32_t mask; + uint32_t padding; }; struct fuse_init_in { - __u32 major; - __u32 minor; - __u32 max_readahead; - __u32 flags; + uint32_t major; + uint32_t minor; + uint32_t max_readahead; + uint32_t flags; }; +#define FUSE_COMPAT_INIT_OUT_SIZE 8 +#define FUSE_COMPAT_22_INIT_OUT_SIZE 24 + struct fuse_init_out { - __u32 major; - __u32 minor; - __u32 max_readahead; - __u32 flags; - __u16 max_background; - __u16 congestion_threshold; - __u32 max_write; + uint32_t major; + uint32_t minor; + uint32_t max_readahead; + uint32_t flags; + uint16_t max_background; + uint16_t congestion_threshold; + uint32_t max_write; + uint32_t time_gran; + uint32_t unused[9]; }; #define CUSE_INIT_INFO_MAX 4096 struct cuse_init_in { - __u32 major; - __u32 minor; - __u32 unused; - __u32 flags; + uint32_t major; + uint32_t minor; + uint32_t unused; + uint32_t flags; }; struct cuse_init_out { - __u32 major; - __u32 minor; - __u32 unused; - __u32 flags; - __u32 max_read; - __u32 max_write; - __u32 dev_major; /* chardev major */ - __u32 dev_minor; /* chardev minor */ - __u32 spare[10]; + uint32_t major; + uint32_t minor; + uint32_t unused; + uint32_t flags; + uint32_t max_read; + uint32_t max_write; + uint32_t dev_major; /* chardev major */ + uint32_t dev_minor; /* chardev minor */ + uint32_t spare[10]; }; struct fuse_interrupt_in { - __u64 unique; + uint64_t unique; }; struct fuse_bmap_in { - __u64 block; - __u32 blocksize; - __u32 padding; + uint64_t block; + uint32_t blocksize; + uint32_t padding; }; struct fuse_bmap_out { - __u64 block; + uint64_t block; }; struct fuse_ioctl_in { - __u64 fh; - __u32 flags; - __u32 cmd; - __u64 arg; - __u32 in_size; - __u32 out_size; + uint64_t fh; + uint32_t flags; + uint32_t cmd; + uint64_t arg; + uint32_t in_size; + uint32_t out_size; +}; + +struct fuse_ioctl_iovec { + uint64_t base; + uint64_t len; }; struct fuse_ioctl_out { - __s32 result; - __u32 flags; - __u32 in_iovs; - __u32 out_iovs; + int32_t result; + uint32_t flags; + uint32_t in_iovs; + uint32_t out_iovs; }; struct fuse_poll_in { - __u64 fh; - __u64 kh; - __u32 flags; - __u32 padding; + uint64_t fh; + uint64_t kh; + uint32_t flags; + uint32_t events; }; struct fuse_poll_out { - __u32 revents; - __u32 padding; + uint32_t revents; + uint32_t padding; }; struct fuse_notify_poll_wakeup_out { - __u64 kh; + uint64_t kh; +}; + +struct fuse_fallocate_in { + uint64_t fh; + uint64_t offset; + uint64_t length; + uint32_t mode; + uint32_t padding; }; struct fuse_in_header { - __u32 len; - __u32 opcode; - __u64 unique; - __u64 nodeid; - __u32 uid; - __u32 gid; - __u32 pid; - __u32 padding; + uint32_t len; + uint32_t opcode; + uint64_t unique; + uint64_t nodeid; + uint32_t uid; + uint32_t gid; + uint32_t pid; + uint32_t padding; }; struct fuse_out_header { - __u32 len; - __s32 error; - __u64 unique; + uint32_t len; + int32_t error; + uint64_t unique; }; struct fuse_dirent { - __u64 ino; - __u64 off; - __u32 namelen; - __u32 type; - char name[0]; + uint64_t ino; + uint64_t off; + uint32_t namelen; + uint32_t type; + char name[]; }; #define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) -#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) +#define FUSE_DIRENT_ALIGN(x) \ + (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)) #define FUSE_DIRENT_SIZE(d) \ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) +struct fuse_direntplus { + struct fuse_entry_out entry_out; + struct fuse_dirent dirent; +}; + +#define FUSE_NAME_OFFSET_DIRENTPLUS \ + offsetof(struct fuse_direntplus, dirent.name) +#define FUSE_DIRENTPLUS_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen) + struct fuse_notify_inval_inode_out { - __u64 ino; - __s64 off; - __s64 len; + uint64_t ino; + int64_t off; + int64_t len; }; struct fuse_notify_inval_entry_out { - __u64 parent; - __u32 namelen; - __u32 padding; + uint64_t parent; + uint32_t namelen; + uint32_t padding; +}; + +struct fuse_notify_delete_out { + uint64_t parent; + uint64_t child; + uint32_t namelen; + uint32_t padding; +}; + +struct fuse_notify_store_out { + uint64_t nodeid; + uint64_t offset; + uint32_t size; + uint32_t padding; +}; + +struct fuse_notify_retrieve_out { + uint64_t notify_unique; + uint64_t nodeid; + uint64_t offset; + uint32_t size; + uint32_t padding; +}; + +/* Matches the size of fuse_write_in */ +struct fuse_notify_retrieve_in { + uint64_t dummy1; + uint64_t offset; + uint32_t size; + uint32_t dummy2; + uint64_t dummy3; + uint64_t dummy4; +}; + +struct fuse_lseek_in { + uint64_t fh; + uint64_t offset; + int32_t whence; + uint32_t padding; +}; + +struct fuse_lseek_out { + uint64_t offset; }; #endif /* _LINUX_FUSE_H */ diff --git a/contrib/fuse-include/fuse_kernel_macfuse.h b/contrib/fuse-include/fuse_kernel_macfuse.h new file mode 100644 index 00000000000..31bc495a552 --- /dev/null +++ b/contrib/fuse-include/fuse_kernel_macfuse.h @@ -0,0 +1,424 @@ +/* + This file defines the kernel interface of FUSE + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + + This -- and only this -- header file may also be distributed under + the terms of the BSD Licence as follows: + + Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#ifndef linux +#include <sys/types.h> +#define __u64 uint64_t +#define __u32 uint32_t +#define __s32 int32_t +#else +#include <asm/types.h> +#include <linux/major.h> +#endif + +/** Version number of this interface */ +#define FUSE_KERNEL_VERSION 7 + +/** Minor version number of this interface */ +#define FUSE_KERNEL_MINOR_VERSION 8 + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/** The major number of the fuse character device */ +#define FUSE_MAJOR MISC_MAJOR + +/** The minor number of the fuse character device */ +#define FUSE_MINOR 229 + +/* Make sure all structures are padded to 64bit boundary, so 32bit + userspace works under 64bit kernels */ + +struct fuse_attr { + __u64 ino; + __u64 size; + __u64 blocks; + __u64 atime; + __u64 mtime; + __u64 ctime; + __u64 crtime; + __u32 atimensec; + __u32 mtimensec; + __u32 ctimensec; + __u32 crtimensec; + __u32 mode; + __u32 nlink; + __u32 uid; + __u32 gid; + __u32 rdev; + __u32 flags; /* file flags; see chflags(2) */ +}; + +struct fuse_kstatfs { + __u64 blocks; + __u64 bfree; + __u64 bavail; + __u64 files; + __u64 ffree; + __u32 bsize; + __u32 namelen; + __u32 frsize; + __u32 padding; + __u32 spare[6]; +}; + +struct fuse_file_lock { + __u64 start; + __u64 end; + __u32 type; + __u32 pid; /* tgid */ +}; + +/** + * Bitmasks for fuse_setattr_in.valid + */ +#define FATTR_MODE (1 << 0) +#define FATTR_UID (1 << 1) +#define FATTR_GID (1 << 2) +#define FATTR_SIZE (1 << 3) +#define FATTR_ATIME (1 << 4) +#define FATTR_MTIME (1 << 5) +#define FATTR_FH (1 << 6) +#define FATTR_CRTIME (1 << 28) +#define FATTR_CHGTIME (1 << 29) +#define FATTR_BKUPTIME (1 << 30) +#define FATTR_FLAGS (1 << 31) + +/** + * Flags returned by the OPEN request + * + * FOPEN_DIRECT_IO: bypass page cache for this open file + * FOPEN_KEEP_CACHE: don't invalidate the data cache on open + */ +#define FOPEN_DIRECT_IO (1 << 0) +#define FOPEN_KEEP_CACHE (1 << 1) +#define FOPEN_PURGE_ATTR (1 << 30) +#define FOPEN_PURGE_UBC (1 << 31) + +/** + * INIT request/reply flags + */ +#define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) +#define FUSE_CASE_INSENSITIVE (1 << 29) +#define FUSE_VOL_RENAME (1 << 30) +#define FUSE_XTIMES (1 << 31) + +/** + * Release flags + */ +#define FUSE_RELEASE_FLUSH (1 << 0) + +enum fuse_opcode { + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ + FUSE_GETATTR = 3, + FUSE_SETATTR = 4, + FUSE_READLINK = 5, + FUSE_SYMLINK = 6, + FUSE_MKNOD = 8, + FUSE_MKDIR = 9, + FUSE_UNLINK = 10, + FUSE_RMDIR = 11, + FUSE_RENAME = 12, + FUSE_LINK = 13, + FUSE_OPEN = 14, + FUSE_READ = 15, + FUSE_WRITE = 16, + FUSE_STATFS = 17, + FUSE_RELEASE = 18, + FUSE_FSYNC = 20, + FUSE_SETXATTR = 21, + FUSE_GETXATTR = 22, + FUSE_LISTXATTR = 23, + FUSE_REMOVEXATTR = 24, + FUSE_FLUSH = 25, + FUSE_INIT = 26, + FUSE_OPENDIR = 27, + FUSE_READDIR = 28, + FUSE_RELEASEDIR = 29, + FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, + FUSE_ACCESS = 34, + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, + FUSE_BMAP = 37, + FUSE_DESTROY = 38, + /* + FUSE_IOCTL = 39, + FUSE_POLL = 40, + FUSE_NOTIFY_REPLY = 41, + FUSE_BATCH_FORGET = 42, + FUSE_FALLOCATE = 43, + FUSE_READDIRPLUS = 44, + */ + + FUSE_SETVOLNAME = 61, + FUSE_GETXTIMES = 62, + FUSE_EXCHANGE = 63, +}; + +/* The read buffer is required to be at least 8k, but may be much larger */ +#define FUSE_MIN_READ_BUFFER 8192 + +struct fuse_entry_out { + __u64 nodeid; /* Inode ID */ + __u64 generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + __u64 entry_valid; /* Cache timeout for the name */ + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 entry_valid_nsec; + __u32 attr_valid_nsec; + struct fuse_attr attr; +}; + +struct fuse_forget_in { + __u64 nlookup; +}; + +struct fuse_attr_out { + __u64 attr_valid; /* Cache timeout for the attributes */ + __u32 attr_valid_nsec; + __u32 dummy; + struct fuse_attr attr; +}; + +struct fuse_getxtimes_out { + __u64 bkuptime; + __u64 crtime; + __u32 bkuptimensec; + __u32 crtimensec; +}; + +struct fuse_mknod_in { + __u32 mode; + __u32 rdev; +}; + +struct fuse_mkdir_in { + __u32 mode; + __u32 padding; +}; + +struct fuse_rename_in { + __u64 newdir; +}; + +struct fuse_exchange_in { + __u64 olddir; + __u64 newdir; + __u64 options; +}; + +struct fuse_link_in { + __u64 oldnodeid; +}; + +struct fuse_setattr_in { + __u32 valid; + __u32 padding; + __u64 fh; + __u64 size; + __u64 unused1; + __u64 atime; + __u64 mtime; + __u64 unused2; + __u32 atimensec; + __u32 mtimensec; + __u32 unused3; + __u32 mode; + __u32 unused4; + __u32 uid; + __u32 gid; + __u32 unused5; + __u64 bkuptime; + __u64 chgtime; + __u64 crtime; + __u32 bkuptimensec; + __u32 chgtimensec; + __u32 crtimensec; + __u32 flags; /* file flags; see chflags(2) */ +}; + +struct fuse_open_in { + __u32 flags; + __u32 mode; +}; + +struct fuse_open_out { + __u64 fh; + __u32 open_flags; + __u32 padding; +}; + +struct fuse_release_in { + __u64 fh; + __u32 flags; + __u32 release_flags; + __u64 lock_owner; +}; + +struct fuse_flush_in { + __u64 fh; + __u32 unused; + __u32 padding; + __u64 lock_owner; +}; + +struct fuse_read_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 padding; +}; + +struct fuse_write_in { + __u64 fh; + __u64 offset; + __u32 size; + __u32 write_flags; +}; + +struct fuse_write_out { + __u32 size; + __u32 padding; +}; + +#define FUSE_COMPAT_STATFS_SIZE 48 + +struct fuse_statfs_out { + struct fuse_kstatfs st; +}; + +struct fuse_fsync_in { + __u64 fh; + __u32 fsync_flags; + __u32 padding; +}; + +struct fuse_setxattr_in { + __u32 size; + __u32 flags; + __u32 position; + __u32 padding; +}; + +struct fuse_getxattr_in { + __u32 size; + __u32 padding; + __u32 position; + __u32 padding2; +}; + +struct fuse_getxattr_out { + __u32 size; + __u32 padding; +}; + +struct fuse_lk_in { + __u64 fh; + __u64 owner; + struct fuse_file_lock lk; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + +struct fuse_access_in { + __u32 mask; + __u32 padding; +}; + +struct fuse_init_in { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; +}; + +struct fuse_init_out { + __u32 major; + __u32 minor; + __u32 max_readahead; + __u32 flags; + __u32 unused; + __u32 max_write; +}; + +struct fuse_interrupt_in { + __u64 unique; +}; + +struct fuse_bmap_in { + __u64 block; + __u32 blocksize; + __u32 padding; +}; + +struct fuse_bmap_out { + __u64 block; +}; + +struct fuse_in_header { + __u32 len; + __u32 opcode; + __u64 unique; + __u64 nodeid; + __u32 uid; + __u32 gid; + __u32 pid; + __u32 padding; +}; + +struct fuse_out_header { + __u32 len; + __s32 error; + __u64 unique; +}; + +struct fuse_dirent { + __u64 ino; + __u64 off; + __u32 namelen; + __u32 type; + char name[0]; +}; + +#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) +#define FUSE_DIRENT_ALIGN(x) (((x) + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1)) +#define FUSE_DIRENT_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) diff --git a/contrib/fuse-util/mount_util.h b/contrib/fuse-include/mount_util.h index cf54d9d0d02..f392f99f17a 100644 --- a/contrib/fuse-util/mount_util.h +++ b/contrib/fuse-include/mount_util.h @@ -10,7 +10,8 @@ int fuse_mnt_add_mount(const char *progname, const char *fsname, const char *mnt, const char *type, const char *opts); -int fuse_mnt_umount(const char *progname, const char *mnt, int lazy); +int fuse_mnt_umount(const char *progname, const char *abs_mnt, + const char *rel_mnt, int lazy); char *fuse_mnt_resolve_path(const char *progname, const char *orig); int fuse_mnt_check_empty(const char *progname, const char *mnt, mode_t rootmode, off_t rootsize); diff --git a/contrib/fuse-lib/misc.c b/contrib/fuse-lib/misc.c index 877c3880de0..1a9b418e511 100644 --- a/contrib/fuse-lib/misc.c +++ b/contrib/fuse-lib/misc.c @@ -10,6 +10,7 @@ #include <string.h> #include <limits.h> #include <fcntl.h> +#include "glusterfs/glusterfs.h" #include "fuse_kernel.h" #include "fuse-misc.h" @@ -37,9 +38,9 @@ calc_timeout_nsec (double t) } void -convert_fuse_file_lock (struct fuse_file_lock *fl, struct flock *flock) +convert_fuse_file_lock (struct fuse_file_lock *fl, struct gf_flock *flock, + uint64_t lk_owner) { - memset (flock, 0, sizeof (struct flock)); flock->l_type = fl->type; flock->l_whence = SEEK_SET; flock->l_start = fl->start; @@ -48,4 +49,5 @@ convert_fuse_file_lock (struct fuse_file_lock *fl, struct flock *flock) else flock->l_len = fl->end - fl->start + 1; flock->l_pid = fl->pid; + set_lk_owner_from_uint64 (&flock->l_owner, lk_owner); } diff --git a/contrib/fuse-lib/mount-common.c b/contrib/fuse-lib/mount-common.c new file mode 100644 index 00000000000..cffd4c01ed5 --- /dev/null +++ b/contrib/fuse-lib/mount-common.c @@ -0,0 +1,282 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include "mount-gluster-compat.h" + +/* + * These functions (and gf_fuse_umount() in mount.c) + * were originally taken from libfuse as of commit 7960e99e + * (http://fuse.git.sourceforge.net/git/gitweb.cgi?p=fuse/fuse;a=commit;h=7960e99e) + * almost verbatim. What has been changed upon adoption: + * - style adopted to that of glusterfs + * - s/fprintf/gf_log/ + * - s/free/FREE/, s/malloc/MALLOC/ + * - there are some other minor things + * + * For changes that were made later and syncs with upstream, + * see the commit log and per-function comments. + */ + +#ifdef GF_LINUX_HOST_OS +/* FUSE: cherry-picked bd99f9cf */ +static int +mtab_needs_update (const char *mnt) +{ + int res; + struct stat stbuf; + + /* If mtab is within new mount, don't touch it */ + if (strncmp (mnt, _PATH_MOUNTED, sizeof (_PATH_MOUNTED) - 1) == 0 && + _PATH_MOUNTED[strlen (mnt)] == '/') + return 0; + + /* + * Skip mtab update if /etc/mtab: + * + * - doesn't exist, + * - is a symlink, + * - is on a read-only filesystem. + */ + res = lstat (_PATH_MOUNTED, &stbuf); + if (res == -1) { + if (errno == ENOENT) + return 0; + } else { + uid_t ruid; + int err; + + if (S_ISLNK (stbuf.st_mode)) + return 0; + + ruid = getuid (); + if (ruid != 0) + setreuid (0, -1); + + res = access (_PATH_MOUNTED, W_OK); + err = (res == -1) ? errno : 0; + if (ruid != 0) + setreuid (ruid, -1); + + if (err == EROFS) + return 0; + } + + return 1; +} +#else /* GF_LINUX_HOST_OS */ +#define mtab_needs_update(x) 1 +#endif /* GF_LINUX_HOST_OS */ + +/* FUSE: called add_mount_legacy(); R.I.P. as of cbd3a2a8 */ +int +fuse_mnt_add_mount (const char *progname, const char *fsname, + const char *mnt, const char *type, const char *opts) +{ + int res; + int status; + sigset_t blockmask; + sigset_t oldmask; + + if (!mtab_needs_update (mnt)) + return 0; + + sigemptyset (&blockmask); + sigaddset (&blockmask, SIGCHLD); + res = sigprocmask (SIG_BLOCK, &blockmask, &oldmask); + if (res == -1) { + GFFUSE_LOGERR ("%s: sigprocmask: %s", + progname, strerror (errno)); + return -1; + } + + res = fork (); + if (res == -1) { + GFFUSE_LOGERR ("%s: fork: %s", progname, strerror (errno)); + goto out_restore; + } + if (res == 0) { + char templ[] = "/tmp/fusermountXXXXXX"; + char *tmp; + + sigprocmask (SIG_SETMASK, &oldmask, NULL); + res = setuid (geteuid ()); + if (res != 0) { + GFFUSE_LOGERR ("%s: setuid: %s", progname, strerror (errno)); + exit (1); + } + + /* + * hide in a directory, where mount isn't able to resolve + * fsname as a valid path + */ + tmp = mkdtemp (templ); + if (!tmp) { + GFFUSE_LOGERR ("%s: failed to create temporary directory", + progname); + exit (1); + } + if (chdir (tmp)) { + GFFUSE_LOGERR ("%s: failed to chdir to %s: %s", + progname, tmp, strerror (errno)); + exit (1); + } + rmdir (tmp); + execl (_PATH_MOUNT, _PATH_MOUNT, "-i", "-f", "-t", type, + "-o", opts, fsname, mnt, NULL); + GFFUSE_LOGERR ("%s: failed to execute %s: %s", + progname, _PATH_MOUNT, strerror (errno)); + exit (1); + } + + res = waitpid (res, &status, 0); + if (res == -1) + GFFUSE_LOGERR ("%s: waitpid: %s", progname, strerror (errno)); + res = (res != -1 && status == 0) ? 0 : -1; + + out_restore: + sigprocmask (SIG_SETMASK, &oldmask, NULL); + return res; +} + +char * +fuse_mnt_resolve_path (const char *progname, const char *orig) +{ + char buf[PATH_MAX]; + char *copy; + char *dst; + char *end; + char *lastcomp; + const char *toresolv; + + if (!orig[0]) { + GFFUSE_LOGERR ("%s: invalid mountpoint '%s'", progname, orig); + return NULL; + } + + copy = strdup (orig); + if (copy == NULL) { + GFFUSE_LOGERR ("%s: failed to allocate memory", progname); + return NULL; + } + + toresolv = copy; + lastcomp = NULL; + for (end = copy + strlen (copy) - 1; end > copy && *end == '/'; end --); + if (end[0] != '/') { + char *tmp; + end[1] = '\0'; + tmp = strrchr (copy, '/'); + if (tmp == NULL) { + lastcomp = copy; + toresolv = "."; + } else { + lastcomp = tmp + 1; + if (tmp == copy) + toresolv = "/"; + } + if (strcmp (lastcomp, ".") == 0 || strcmp (lastcomp, "..") == 0) { + lastcomp = NULL; + toresolv = copy; + } + else if (tmp) + tmp[0] = '\0'; + } + if (realpath (toresolv, buf) == NULL) { + GFFUSE_LOGERR ("%s: bad mount point %s: %s", progname, orig, + strerror (errno)); + FREE (copy); + return NULL; + } + if (lastcomp == NULL) + dst = strdup (buf); + else { + dst = (char *) MALLOC (strlen (buf) + 1 + strlen (lastcomp) + 1); + if (dst) { + unsigned buflen = strlen (buf); + if (buflen && buf[buflen-1] == '/') + sprintf (dst, "%s%s", buf, lastcomp); + else + sprintf (dst, "%s/%s", buf, lastcomp); + } + } + FREE (copy); + if (dst == NULL) + GFFUSE_LOGERR ("%s: failed to allocate memory", progname); + return dst; +} + +/* FUSE: to support some changes that were reverted since + * then, it was split in two (fuse_mnt_umount() and + * exec_umount()); however the actual code is same as here + * since 0197ce40 + */ +int +fuse_mnt_umount (const char *progname, const char *abs_mnt, + const char *rel_mnt, int lazy) +{ + int res; + int status; + sigset_t blockmask; + sigset_t oldmask; + + if (!mtab_needs_update (abs_mnt)) { + res = umount2 (rel_mnt, lazy ? 2 : 0); + if (res == -1) + GFFUSE_LOGERR ("%s: failed to unmount %s: %s", + progname, abs_mnt, strerror (errno)); + return res; + } + + sigemptyset (&blockmask); + sigaddset (&blockmask, SIGCHLD); + res = sigprocmask (SIG_BLOCK, &blockmask, &oldmask); + if (res == -1) { + GFFUSE_LOGERR ("%s: sigprocmask: %s", progname, + strerror (errno)); + return -1; + } + + res = fork (); + if (res == -1) { + GFFUSE_LOGERR ("%s: fork: %s", progname, strerror (errno)); + goto out_restore; + } + if (res == 0) { + sigprocmask (SIG_SETMASK, &oldmask, NULL); + res = setuid (geteuid ()); + if (res != 0) { + GFFUSE_LOGERR ("%s: setuid: %s", progname, strerror (errno)); + exit (1); + } +#ifdef GF_LINUX_HOST_OS + execl ("umount", "umount", "-i", rel_mnt, + lazy ? "-l" : NULL, NULL); + GFFUSE_LOGERR ("%s: failed to execute umount: %s", + progname, strerror (errno)); +#elif __NetBSD__ + /* exitting the filesystem causes the umount */ + exit (0); +#else + execl ("umount", "umount", "-f", rel_mnt, NULL); + GFFUSE_LOGERR ("%s: failed to execute umount: %s", + progname, strerror (errno)); +#endif /* GF_LINUX_HOST_OS */ + exit (1); + } + res = waitpid (res, &status, 0); + if (res == -1) + GFFUSE_LOGERR ("%s: waitpid: %s", progname, strerror (errno)); + + if (status != 0) + res = -1; + + out_restore: + sigprocmask (SIG_SETMASK, &oldmask, NULL); + return res; +} diff --git a/contrib/fuse-lib/mount-gluster-compat.h b/contrib/fuse-lib/mount-gluster-compat.h new file mode 100644 index 00000000000..d3646d08d8e --- /dev/null +++ b/contrib/fuse-lib/mount-gluster-compat.h @@ -0,0 +1,105 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <limits.h> +#include <fcntl.h> +#include <errno.h> +#include <dirent.h> +#include <signal.h> +#if defined(GF_LINUX_HOST_OS) +#include <mntent.h> +#endif /* GF_LINUX_HOST_OS */ +#include <sys/stat.h> +#include <sys/poll.h> +#include <sys/un.h> +#include <sys/wait.h> +#include <sys/mount.h> + +#ifdef GF_LINUX_HOST_OS +typedef unsigned long mount_flag_t; +#endif + +#if defined(__NetBSD__) +#include <perfuse.h> +#define umount2(dir, flags) unmount(dir, ((flags) != 0) ? MNT_FORCE : 0) +#define MS_RDONLY MNT_RDONLY +#define MS_NOSUID MNT_NOSUID +#define MS_NODEV MNT_NODEV +#define MS_NOATIME MNT_NOATIME +#define MS_NOEXEC MNT_NOEXEC +typedef int mount_flag_t; +#endif + +#if defined(GF_DARWIN_HOST_OS) || defined(__FreeBSD__) +#include <sys/param.h> +#include <sys/mount.h> +#define umount2(dir, flags) unmount(dir, ((flags) != 0) ? MNT_FORCE : 0) +#endif + +#if defined(__FreeBSD__) +#define MS_RDONLY MNT_RDONLY +#define MS_NOSUID MNT_NOSUID +/* "nodev"/MNT_NODEV was removed from FreBSD, as it became unneeded because "As + * of FreeBSD 6.0 device nodes may be created in regular file systems but such + * nodes cannot be used to access devices." (See + * https://freebsd.org/cgi/man.cgi?query=mknod&sektion=8 . + * Also see: + * - https://github.com/freebsd/freebsd/commit/266790a + * - https://github.com/freebsd/freebsd/commit/a5e716d + * - 700008 in + * https://www.freebsd.org/doc/en/books/porters-handbook/versions-7.html .) + */ +#if __FreeBSD_version < 700008 +#define MS_NODEV MNT_NODEV +#else +#define MS_NODEV 0 +#endif +#define MS_NOATIME MNT_NOATIME +#define MS_NOEXEC MNT_NOEXEC +#if __FreeBSD_version < 1000715 +typedef int mount_flag_t; +#else +/* __FreeBSD_version was not bumped for this type change. Anyway, see + * https://github.com/freebsd/freebsd/commit/e8d76f8 + * and respective __FreeBSD_version: + * https://github.com/freebsd/freebsd/blob/e8d76f8/sys/sys/param.h#L61 . + * We use the subsequent value, 1000715, to switch. (Also see: + * https://www.freebsd.org/doc/en/books/porters-handbook/versions-10.html .) + */ +typedef long long mount_flag_t; +#endif +#endif + +#ifdef GF_LINUX_HOST_OS +#define _PATH_MOUNT "/bin/mount" +#else /* FreeBSD, NetBSD, MacOS X */ +#define _PATH_MOUNT "/sbin/mount" +#endif + +#ifdef FUSE_UTIL +#define MALLOC(size) malloc (size) +#define FREE(ptr) free (ptr) +#define GFFUSE_LOGERR(...) fprintf (stderr, ## __VA_ARGS__) +#else /* FUSE_UTIL */ +#include "glusterfs/glusterfs.h" +#include "glusterfs/logging.h" +#include "glusterfs/common-utils.h" + +#define GFFUSE_LOGERR(...) \ + gf_log ("glusterfs-fuse", GF_LOG_ERROR, ## __VA_ARGS__) +#endif /* !FUSE_UTIL */ diff --git a/contrib/fuse-lib/mount.c b/contrib/fuse-lib/mount.c index cf8dc5b4afb..06ff191f542 100644 --- a/contrib/fuse-lib/mount.c +++ b/contrib/fuse-lib/mount.c @@ -1,317 +1,203 @@ /* FUSE: Filesystem in Userspace Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> - Copyright (c) 2009 Gluster, Inc. <http://www.gluster.com> + Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> This program can be distributed under the terms of the GNU LGPLv2. See the file COPYING.LIB. */ -#ifndef _CONFIG_H -#define _CONFIG_H -#include "config.h" -#endif - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <stddef.h> -#include <limits.h> -#include <fcntl.h> -#include <errno.h> -#include <dirent.h> -#include <mntent.h> -#include <sys/stat.h> -#include <sys/poll.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/wait.h> -#include <sys/mount.h> - -#ifdef FUSE_UTIL -#define MALLOC(size) malloc (size) -#define FREE(ptr) free (ptr) -#define GFFUSE_LOGERR(...) fprintf (stderr, ## __VA_ARGS__) -#else /* FUSE_UTIL */ -#include "glusterfs.h" -#include "logging.h" -#include "common-utils.h" +#include "mount_util.h" +#include "mount-gluster-compat.h" #ifdef GF_FUSERMOUNT #define FUSERMOUNT_PROG FUSERMOUNT_DIR "/fusermount-glusterfs" #else #define FUSERMOUNT_PROG "fusermount" #endif -#define FUSE_COMMFD_ENV "_FUSE_COMMFD" - -#define GFFUSE_LOGERR(...) \ - gf_log ("glusterfs-fuse", GF_LOG_ERROR, ## __VA_ARGS__) -#endif /* !FUSE_UTIL */ +#define FUSE_DEVFD_ENV "_FUSE_DEVFD" -/* - * Functions below, until following note, were taken from libfuse - * (http://git.gluster.com/?p=users/csaba/fuse.git;a=commit;h=b988bbf9) - * almost verbatim. What has been changed: - * - style adopted to that of glusterfs - * - s/fprintf/gf_log/ - * - s/free/FREE/, s/malloc/MALLOC/ - * - there are some other minor things - */ +#ifdef __FreeBSD__ +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> +#endif /* __FreeBSD__ */ -static int -mtab_needs_update (const char *mnt) +/* FUSE: function is called fuse_kern_unmount() */ +void +gf_fuse_unmount (const char *mountpoint, int fd) { int res; - struct stat stbuf; - - /* If mtab is within new mount, don't touch it */ - if (strncmp (mnt, _PATH_MOUNTED, strlen (mnt)) == 0 && - _PATH_MOUNTED[strlen (mnt)] == '/') - return 0; - - /* - * Skip mtab update if /etc/mtab: - * - * - doesn't exist, - * - is a symlink, - * - is on a read-only filesystem. - */ - res = lstat (_PATH_MOUNTED, &stbuf); - if (res == -1) { - if (errno == ENOENT) - return 0; - } else { - if (S_ISLNK (stbuf.st_mode)) - return 0; + int pid; + + if (!mountpoint) + return; + + if (fd != -1) { + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = 0; + res = poll (&pfd, 1, 0); + /* If file poll returns POLLERR on the device file descriptor, + then the filesystem is already unmounted */ + if (res == 1 && (pfd.revents & POLLERR)) + return; - res = access (_PATH_MOUNTED, W_OK); - if (res == -1 && errno == EROFS) - return 0; + /* Need to close file descriptor, otherwise synchronous umount + would recurse into filesystem, and deadlock */ + close (fd); + } + + if (geteuid () == 0) { + fuse_mnt_umount ("fuse", mountpoint, mountpoint, 1); + return; + } else { + GFFUSE_LOGERR ("fuse: Effective-uid: %d", geteuid()); } - return 1; + res = umount2 (mountpoint, 2); + if (res == 0) + return; + + GFFUSE_LOGERR ("fuse: failed to unmount %s: %s", + mountpoint, strerror (errno)); + pid = fork (); + if (pid == -1) + return; + + if (pid == 0) { + const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z", + "--", mountpoint, NULL }; + + execvp (FUSERMOUNT_PROG, (char **)argv); + GFFUSE_LOGERR ("fuse: failed to execute fuserumount: %s", + strerror (errno)); + _exit (1); + } + waitpid (pid, NULL, 0); } -#ifndef FUSE_UTIL -static -#endif + +/* gluster-specific routines */ + +/* Unmounting in a daemon that lurks 'till main process exits */ int -fuse_mnt_add_mount (const char *progname, const char *fsname, - const char *mnt, const char *type, const char *opts) +gf_fuse_unmount_daemon (const char *mountpoint, int fd) { - int res; - int status; - sigset_t blockmask; - sigset_t oldmask; - - if (!mtab_needs_update (mnt)) - return 0; - - sigemptyset (&blockmask); - sigaddset (&blockmask, SIGCHLD); - res = sigprocmask (SIG_BLOCK, &blockmask, &oldmask); - if (res == -1) { - GFFUSE_LOGERR ("%s: sigprocmask: %s", - progname, strerror (errno)); + int ret = -1; + pid_t pid = -1; + + if (fd == -1) return -1; - } - res = fork (); - if (res == -1) { - GFFUSE_LOGERR ("%s: fork: %s", progname, strerror (errno)); - goto out_restore; - } - if (res == 0) { - char templ[] = "/tmp/fusermountXXXXXX"; - char *tmp; - - /* mtab update done async, just log if fails */ - res = fork (); - if (res) - exit (res == -1 ? 1 : 0); - res = fork (); - if (res) { - if (res != -1) - res = waitpid (res, &status, 0); - if (res == -1) - GFFUSE_LOGERR ("%s: /etc/mtab update failed", - progname); - - exit (0); - } + int ump[2] = {0,}; - sigprocmask (SIG_SETMASK, &oldmask, NULL); - setuid (geteuid ()); - - /* - * hide in a directory, where mount isn't able to resolve - * fsname as a valid path - */ - tmp = mkdtemp (templ); - if (!tmp) { - GFFUSE_LOGERR ("%s: failed to create temporary directory", - progname); - exit (1); - } - if (chdir (tmp)) { - GFFUSE_LOGERR ("%s: failed to chdir to %s: %s", - progname, tmp, strerror (errno)); - exit (1); - } - rmdir (tmp); - execl ("/bin/mount", "/bin/mount", "-i", "-f", "-t", type, - "-o", opts, fsname, mnt, NULL); - GFFUSE_LOGERR ("%s: failed to execute /bin/mount: %s", - progname, strerror (errno)); - exit (1); + ret = pipe(ump); + if (ret == -1) { + close (fd); + return -1; } - res = waitpid (res, &status, 0); - if (res == -1) - GFFUSE_LOGERR ("%s: waitpid: %s", progname, strerror (errno)); - if (status != 0) - res = -1; + pid = fork (); + switch (pid) { + case 0: + { + char c = 0; + sigset_t sigset; - out_restore: - sigprocmask (SIG_SETMASK, &oldmask, NULL); - return res; -} + close_fds_except (ump, 1); -#ifndef FUSE_UTIL -static -#endif -char -*fuse_mnt_resolve_path (const char *progname, const char *orig) -{ - char buf[PATH_MAX]; - char *copy; - char *dst; - char *end; - char *lastcomp; - const char *toresolv; - - if (!orig[0]) { - GFFUSE_LOGERR ("%s: invalid mountpoint '%s'", progname, orig); - return NULL; - } + setsid(); + (void)chdir("/"); + sigfillset(&sigset); + sigprocmask(SIG_BLOCK, &sigset, NULL); - copy = strdup (orig); - if (copy == NULL) { - GFFUSE_LOGERR ("%s: failed to allocate memory", progname); - return NULL; - } + read (ump[0], &c, 1); - toresolv = copy; - lastcomp = NULL; - for (end = copy + strlen (copy) - 1; end > copy && *end == '/'; end --); - if (end[0] != '/') { - char *tmp; - end[1] = '\0'; - tmp = strrchr (copy, '/'); - if (tmp == NULL) { - lastcomp = copy; - toresolv = "."; - } else { - lastcomp = tmp + 1; - if (tmp == copy) - toresolv = "/"; - } - if (strcmp (lastcomp, ".") == 0 || strcmp (lastcomp, "..") == 0) { - lastcomp = NULL; - toresolv = copy; - } - else if (tmp) - tmp[0] = '\0'; + gf_fuse_unmount (mountpoint, fd); + exit (0); } - if (realpath (toresolv, buf) == NULL) { - GFFUSE_LOGERR ("%s: bad mount point %s: %s", progname, orig, - strerror (errno)); - FREE (copy); - return NULL; - } - if (lastcomp == NULL) - dst = strdup (buf); - else { - dst = (char *) MALLOC (strlen (buf) + 1 + strlen (lastcomp) + 1); - if (dst) { - unsigned buflen = strlen (buf); - if (buflen && buf[buflen-1] == '/') - sprintf (dst, "%s%s", buf, lastcomp); - else - sprintf (dst, "%s/%s", buf, lastcomp); - } + case -1: + close (fd); + fd = -1; + ret = -1; + close (ump[1]); } - FREE (copy); - if (dst == NULL) - GFFUSE_LOGERR ("%s: failed to allocate memory", progname); - return dst; + close (ump[0]); + + return ret; } -#ifndef FUSE_UTIL -/* return value: - * >= 0 => fd - * -1 => error - */ -static int -receive_fd (int fd) +static char * +escape (char *s) { - struct msghdr msg; - struct iovec iov; - char buf[1]; - int rv; - size_t ccmsg[CMSG_SPACE (sizeof (int)) / sizeof (size_t)]; - struct cmsghdr *cmsg; - - iov.iov_base = buf; - iov.iov_len = 1; - - msg.msg_name = 0; - msg.msg_namelen = 0; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - /* old BSD implementations should use msg_accrights instead of - * msg_control; the interface is different. */ - msg.msg_control = ccmsg; - msg.msg_controllen = sizeof (ccmsg); - - while (((rv = recvmsg (fd, &msg, 0)) == -1) && errno == EINTR); - if (rv == -1) { - GFFUSE_LOGERR ("recvmsg failed: %s", strerror (errno)); - return -1; - } - if (!rv) { - /* EOF */ - return -1; + size_t len = 0; + char *p = NULL; + char *q = NULL; + char *e = NULL; + + for (p = s; *p; p++) { + if (*p == ',') + len++; + len++; } - cmsg = CMSG_FIRSTHDR (&msg); - if (!cmsg->cmsg_type == SCM_RIGHTS) { - GFFUSE_LOGERR ("got control message of unknown type %d", - cmsg->cmsg_type); - return -1; + e = CALLOC (1, len + 1); + if (!e) + return NULL; + + for (p = s, q = e; *p; p++, q++) { + if (*p == ',') { + *q = '\\'; + q++; + } + *q = *p; } - return *(int*)CMSG_DATA (cmsg); + + return e; } static int -fuse_mount_fusermount (const char *mountpoint, const char *opts) +fuse_mount_fusermount (const char *mountpoint, char *fsname, + char *mnt_param, int fd) { - int fds[2], pid; - int res; - int rv; + int pid = -1; + int res = 0; + int ret = -1; + char *fm_mnt_params = NULL; + char *efsname = NULL; + +#ifndef GF_FUSERMOUNT + GFFUSE_LOGERR ("Mounting via helper utility " + "(unprivileged mounting) is supported " + "only if glusterfs is compiled with " + "--enable-fusermount"); + return -1; +#endif + + efsname = escape (fsname); + if (!efsname) { + GFFUSE_LOGERR ("Out of memory"); - res = socketpair (PF_UNIX, SOCK_STREAM, 0, fds); - if (res == -1) { - GFFUSE_LOGERR ("socketpair() failed: %s", strerror (errno)); return -1; } + ret = asprintf (&fm_mnt_params, + "%s,fsname=%s,nonempty,subtype=glusterfs", + mnt_param, efsname); + FREE (efsname); + if (ret == -1) { + GFFUSE_LOGERR ("Out of memory"); + goto out; + } + + /* fork to exec fusermount */ pid = fork (); if (pid == -1) { GFFUSE_LOGERR ("fork() failed: %s", strerror (errno)); - close (fds[0]); - close (fds[1]); - return -1; + ret = -1; + goto out; } if (pid == 0) { @@ -320,223 +206,197 @@ fuse_mount_fusermount (const char *mountpoint, const char *opts) int a = 0; argv[a++] = FUSERMOUNT_PROG; - if (opts) { - argv[a++] = "-o"; - argv[a++] = opts; - } + argv[a++] = "-o"; + argv[a++] = fm_mnt_params; argv[a++] = "--"; argv[a++] = mountpoint; argv[a++] = NULL; - close (fds[1]); - fcntl (fds[0], F_SETFD, 0); - snprintf (env, sizeof (env), "%i", fds[0]); - setenv (FUSE_COMMFD_ENV, env, 1); + snprintf (env, sizeof (env), "%i", fd); + setenv (FUSE_DEVFD_ENV, env, 1); execvp (FUSERMOUNT_PROG, (char **)argv); GFFUSE_LOGERR ("failed to exec fusermount: %s", strerror (errno)); _exit (1); } - close (fds[0]); - rv = receive_fd (fds[1]); - close (fds[1]); - waitpid (pid, NULL, 0); /* bury zombie */ - - return rv; + ret = waitpid (pid, &res, 0); + ret = (ret == pid && res == 0) ? 0 : -1; + out: + FREE (fm_mnt_params); + return ret; } -#endif -#ifndef FUSE_UTIL -static -#endif -int -fuse_mnt_umount (const char *progname, const char *mnt, int lazy) +#if defined(__FreeBSD__) +void +build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val, + size_t len) { - int res; - int status; - sigset_t blockmask; - sigset_t oldmask; - - if (!mtab_needs_update (mnt)) { - res = umount2 (mnt, lazy ? 2 : 0); - if (res == -1) - GFFUSE_LOGERR ("%s: failed to unmount %s: %s", - progname, mnt, strerror (errno)); - return res; - } - - sigemptyset (&blockmask); - sigaddset (&blockmask, SIGCHLD); - res = sigprocmask (SIG_BLOCK, &blockmask, &oldmask); - if (res == -1) { - GFFUSE_LOGERR ("%s: sigprocmask: %s", progname, - strerror (errno)); - return -1; - } + int i; + if (*iovlen < 0) + return; + i = *iovlen; - res = fork (); - if (res == -1) { - GFFUSE_LOGERR ("%s: fork: %s", progname, strerror (errno)); - goto out_restore; - } - if (res == 0) { - sigprocmask (SIG_SETMASK, &oldmask, NULL); - setuid (geteuid ()); - execl ("/bin/umount", "/bin/umount", "-i", mnt, - lazy ? "-l" : NULL, NULL); - GFFUSE_LOGERR ("%s: failed to execute /bin/umount: %s", - progname, strerror (errno)); - exit (1); + *iov = realloc(*iov, sizeof **iov * (i + 2)); + if (*iov == NULL) { + *iovlen = -1; + return; } - res = waitpid (res, &status, 0); - if (res == -1) - GFFUSE_LOGERR ("%s: waitpid: %s", progname, strerror (errno)); - if (status != 0) - res = -1; + (*iov)[i].iov_base = strdup(name); + (*iov)[i].iov_len = strlen(name) + 1; - out_restore: - sigprocmask (SIG_SETMASK, &oldmask, NULL); - return res; -} - -#ifdef FUSE_UTIL -int -fuse_mnt_check_empty (const char *progname, const char *mnt, - mode_t rootmode, off_t rootsize) -{ - int isempty = 1; - - if (S_ISDIR (rootmode)) { - struct dirent *ent; - DIR *dp = opendir (mnt); - if (dp == NULL) { - fprintf (stderr, - "%s: failed to open mountpoint for reading: %s\n", - progname, strerror (errno)); - return -1; - } - while ((ent = readdir (dp)) != NULL) { - if (strcmp (ent->d_name, ".") != 0 && - strcmp (ent->d_name, "..") != 0) { - isempty = 0; - break; - } - } - closedir (dp); - } else if (rootsize) - isempty = 0; - - if (!isempty) { - fprintf (stderr, "%s: mountpoint is not empty\n", progname); - fprintf (stderr, "%s: if you are sure this is safe, " - "use the 'nonempty' mount option\n", progname); - return -1; + i++; + (*iov)[i].iov_base = val; + if (len == (size_t) -1) { + if (val != NULL) + len = strlen(val) + 1; + else + len = 0; } - return 0; + (*iov)[i].iov_len = (int)len; + *iovlen = ++i; } -int -fuse_mnt_check_fuseblk (void) +/* + * This function is needed for compatibility with parameters + * which used to use the mount_argf() command for the old mount() syscall. + */ +void +build_iovec_argf(struct iovec **iov, int *iovlen, const char *name, + const char *fmt, ...) { - char buf[256]; - FILE *f = fopen ("/proc/filesystems", "r"); - if (!f) - return 1; - - while (fgets (buf, sizeof (buf), f)) - if (strstr (buf, "fuseblk\n")) { - fclose (f); - return 1; - } + va_list ap; + char val[255] = { 0 }; - fclose (f); - return 0; + va_start(ap, fmt); + vsnprintf(val, sizeof(val), fmt, ap); + va_end(ap); + build_iovec(iov, iovlen, name, strdup(val), (size_t)-1); } +#endif /* __FreeBSD__ */ + +struct mount_flags { + const char *opt; + mount_flag_t flag; + int on; +} mount_flags[] = { + /* We provide best effort cross platform support for mount flags by + * defining the ones which are commonly used in Unix-like OS-es. + */ + {"ro", MS_RDONLY, 1}, + {"nosuid", MS_NOSUID, 1}, + {"nodev", MS_NODEV, 1}, + {"noatime", MS_NOATIME, 1}, + {"noexec", MS_NOEXEC, 1}, +#ifdef GF_LINUX_HOST_OS + {"rw", MS_RDONLY, 0}, + {"suid", MS_NOSUID, 0}, + {"dev", MS_NODEV, 0}, + {"exec", MS_NOEXEC, 0}, + {"async", MS_SYNCHRONOUS, 0}, + {"sync", MS_SYNCHRONOUS, 1}, + {"atime", MS_NOATIME, 0}, + {"dirsync", MS_DIRSYNC, 1}, #endif + {NULL, 0, 0} +}; -#ifndef FUSE_UTIL -void -gf_fuse_unmount (const char *mountpoint, int fd) +static int +mount_param_to_flag (char *mnt_param, mount_flag_t *mntflags, + char **mnt_param_new) { - int res; - int pid; - - if (!mountpoint) - return; - - if (fd != -1) { - struct pollfd pfd; - - pfd.fd = fd; - pfd.events = 0; - res = poll (&pfd, 1, 0); - /* If file poll returns POLLERR on the device file descriptor, - then the filesystem is already unmounted */ - if (res == 1 && (pfd.revents & POLLERR)) - return; + gf_boolean_t found = _gf_false; + struct mount_flags *flag = NULL; + char *param_tok = NULL; + token_iter_t tit = {0,}; + gf_boolean_t iter_end = _gf_false; + + /* Allocate a buffer that will hold the mount parameters remaining + * after the ones corresponding to mount flags are processed and + * removed.The length of the original params are a good upper bound + * of the size needed. + */ + *mnt_param_new = strdup (mnt_param); + if (!*mnt_param_new) + return -1; - /* Need to close file descriptor, otherwise synchronous umount - would recurse into filesystem, and deadlock */ - close (fd); - } + for (param_tok = token_iter_init (*mnt_param_new, ',', &tit) ;;) { + iter_end = next_token (¶m_tok, &tit); + + found = _gf_false; + for (flag = mount_flags; flag->opt; flag++) { + /* Compare the mount flag name to the param + * name at hand. + */ + if (strcmp (flag->opt, param_tok) == 0) { + /* If there is a match, adjust mntflags + * accordingly and break. + */ + if (flag->on) { + *mntflags |= flag->flag; + } else { + *mntflags &= ~flag->flag; + } + found = _gf_true; + break; + } + } + /* Exclude flag names from new parameter list. */ + if (found) + drop_token (param_tok, &tit); - if (geteuid () == 0) { - fuse_mnt_umount ("fuse", mountpoint, 1); - return; + if (iter_end) + break; } - res = umount2 (mountpoint, 2); - if (res == 0) - return; - - pid = fork (); - if (pid == -1) - return; - - if (pid == 0) { - const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z", - "--", mountpoint, NULL }; - - execvp (FUSERMOUNT_PROG, (char **)argv); - _exit (1); - } - waitpid (pid, NULL, 0); + return 0; } -#endif -/* - * Functions below are loosely modelled after similar functions of libfuse - */ - -#ifndef FUSE_UTIL static int -fuse_mount_sys (const char *mountpoint, char *fsname, char *mnt_param) +fuse_mount_sys (const char *mountpoint, char *fsname, + char *mnt_param, int fd) { - int fd = -1, ret = -1; + int ret = -1; unsigned mounted = 0; char *mnt_param_mnt = NULL; char *fstype = "fuse.glusterfs"; char *source = fsname; - - fd = open ("/dev/fuse", O_RDWR); - if (fd == -1) { - GFFUSE_LOGERR ("cannot open /dev/fuse (%s)", strerror (errno)); - - return -1; - } - - ret = asprintf (&mnt_param_mnt, - "%s,fd=%i,rootmode=%o,user_id=%i,group_id=%i", - mnt_param, fd, S_IFDIR, getuid (), getgid ()); + mount_flag_t mountflags = 0; + char *mnt_param_new = NULL; + + ret = mount_param_to_flag (mnt_param, &mountflags, &mnt_param_new); + if (ret == 0) + ret = asprintf (&mnt_param_mnt, + "%s,fd=%i,rootmode=%o,user_id=%i,group_id=%i", + mnt_param_new, fd, S_IFDIR, getuid (), + getgid ()); if (ret == -1) { GFFUSE_LOGERR ("Out of memory"); goto out; } - ret = mount (source, mountpoint, fstype, 0, + +#ifdef __FreeBSD__ + struct iovec *iov = NULL; + int iovlen = 0; + char fdstr[15]; + sprintf (fdstr, "%d", fd); + + build_iovec (&iov, &iovlen, "fstype", "fusefs", -1); + build_iovec (&iov, &iovlen, "subtype", "glusterfs", -1); + build_iovec (&iov, &iovlen, "fspath", __DECONST(void *, mountpoint), + -1); + build_iovec (&iov, &iovlen, "from", "/dev/fuse", -1); + build_iovec (&iov, &iovlen, "volname", source, -1); + build_iovec (&iov, &iovlen, "fd", fdstr, -1); + build_iovec (&iov, &iovlen, "allow_other", NULL, -1); + ret = nmount (iov, iovlen, mountflags); +#else + ret = mount (source, mountpoint, fstype, mountflags, mnt_param_mnt); +#endif /* __FreeBSD__ */ +#ifdef GF_LINUX_HOST_OS if (ret == -1 && errno == ENODEV) { /* fs subtype support was added by 79c0b2df aka v2.6.21-3159-g79c0b2d. Probably we have an @@ -548,16 +408,19 @@ fuse_mount_sys (const char *mountpoint, char *fsname, char *mnt_param) goto out; } - ret = mount (source, mountpoint, fstype, 0, + ret = mount (source, mountpoint, fstype, mountflags, mnt_param_mnt); } +#endif /* GF_LINUX_HOST_OS */ if (ret == -1) goto out; else mounted = 1; +#ifdef GF_LINUX_HOST_OS if (geteuid () == 0) { char *newmnt = fuse_mnt_resolve_path ("fuse", mountpoint); + char *mnt_param_mtab = NULL; if (!newmnt) { ret = -1; @@ -565,8 +428,17 @@ fuse_mount_sys (const char *mountpoint, char *fsname, char *mnt_param) goto out; } - ret = fuse_mnt_add_mount ("fuse", source, newmnt, fstype, - mnt_param); + ret = asprintf (&mnt_param_mtab, "%s%s", + mountflags & MS_RDONLY ? "ro," : "", + mnt_param_new); + if (ret == -1) + GFFUSE_LOGERR ("Out of memory"); + else { + ret = fuse_mnt_add_mount ("fuse", source, newmnt, + fstype, mnt_param_mtab); + FREE (mnt_param_mtab); + } + FREE (newmnt); if (ret == -1) { GFFUSE_LOGERR ("failed to add mtab entry"); @@ -574,58 +446,81 @@ fuse_mount_sys (const char *mountpoint, char *fsname, char *mnt_param) goto out; } } +#endif /* GF_LINUX_HOST_OS */ - out: +out: if (ret == -1) { + GFFUSE_LOGERR("ret = -1\n"); if (mounted) umount2 (mountpoint, 2); /* lazy umount */ - close (fd); - fd = -1; } FREE (mnt_param_mnt); + FREE (mnt_param_new); if (source != fsname) FREE (source); - return fd; + + return ret; } int -gf_fuse_mount (const char *mountpoint, char *fsname, char *mnt_param) +gf_fuse_mount (const char *mountpoint, char *fsname, + char *mnt_param, pid_t *mnt_pid, int status_fd) { - int fd = -1, rv = -1; - char *fm_mnt_params = NULL, *p = NULL; + int fd = -1; + pid_t pid = -1; + int ret = -1; - fd = fuse_mount_sys (mountpoint, fsname, mnt_param); + fd = open ("/dev/fuse", O_RDWR); if (fd == -1) { - gf_log ("glusterfs-fuse", GF_LOG_NORMAL, - "direct mount failed (%s), " - "retry to mount via fusermount", - strerror (errno)); - - rv = asprintf (&fm_mnt_params, - "%s,fsname=%s,nonempty,subtype=glusterfs", - mnt_param, fsname); - - if (rv == -1) { - GFFUSE_LOGERR ("Out of memory"); + GFFUSE_LOGERR ("cannot open /dev/fuse (%s)", + strerror (errno)); + return -1; + } - return -1; + /* start mount agent */ + pid = fork(); + switch (pid) { + case 0: + /* hello it's mount agent */ + if (!mnt_pid) { + /* daemonize mount agent, caller is + * not interested in waiting for it + */ + pid = fork (); + if (pid) + exit (pid == -1 ? 1 : 0); } - fd = fuse_mount_fusermount (mountpoint, fm_mnt_params); - if (fd == -1) { - p = fm_mnt_params + strlen (fm_mnt_params); - while (*--p != ','); - *p = '\0'; + ret = fuse_mount_sys (mountpoint, fsname, mnt_param, fd); + if (ret == -1) { + gf_log ("glusterfs-fuse", GF_LOG_INFO, + "direct mount failed (%s) errno %d", + strerror (errno), errno); - fd = fuse_mount_fusermount (mountpoint, fm_mnt_params); + if (errno == EPERM) { + gf_log ("glusterfs-fuse", GF_LOG_INFO, + "retry to mount via fusermount"); + + ret = fuse_mount_fusermount (mountpoint, fsname, + mnt_param, fd); + } } - FREE (fm_mnt_params); + if (ret == -1) + GFFUSE_LOGERR ("mount of %s to %s (%s) failed", + fsname, mountpoint, mnt_param); - if (fd == -1) - GFFUSE_LOGERR ("mount failed"); + if (status_fd >= 0) + (void)write (status_fd, &ret, sizeof (ret)); + exit (!!ret); + /* bye mount agent */ + case -1: + close (fd); + fd = -1; } + if (mnt_pid) + *mnt_pid = pid; + return fd; } -#endif diff --git a/contrib/fuse-util/Makefile.am b/contrib/fuse-util/Makefile.am index 42609a6883d..abbc10eb6d9 100644 --- a/contrib/fuse-util/Makefile.am +++ b/contrib/fuse-util/Makefile.am @@ -1,9 +1,11 @@ bin_PROGRAMS = fusermount-glusterfs -fusermount_glusterfs_SOURCES = fusermount.c $(CONTRIBDIR)/fuse-lib/mount.c -noinst_HEADERS = mount_util.h +fusermount_glusterfs_SOURCES = fusermount.c mount_util.c $(CONTRIBDIR)/fuse-lib/mount-common.c +noinst_HEADERS = $(CONTRIBDIR)/fuse-include/mount_util.h -AM_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -DFUSE_UTIL $(GF_CFLAGS) +AM_CPPFLAGS = $(GF_CPPFLAGS) -DFUSE_UTIL -I$(CONTRIBDIR)/fuse-include -I$(CONTRIBDIR)/fuse-lib + +AM_CFLAGS = -Wall $(GF_CFLAGS) install-exec-hook: -chown root $(DESTDIR)$(bindir)/fusermount-glusterfs diff --git a/contrib/fuse-util/fusermount.c b/contrib/fuse-util/fusermount.c index c3ecc86cc44..ff743f75a21 100644 --- a/contrib/fuse-util/fusermount.c +++ b/contrib/fuse-util/fusermount.c @@ -10,6 +10,11 @@ #include <config.h> #include "mount_util.h" + +#ifndef HAVE_UMOUNT2 +#include "mount-gluster-compat.h" +#endif + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -19,14 +24,24 @@ #include <errno.h> #include <fcntl.h> #include <pwd.h> +#include <limits.h> +#if !defined(__NetBSD__) && !defined(GF_DARWIN_HOST_OS) #include <mntent.h> +#endif /* __NetBSD__ */ #include <sys/wait.h> #include <sys/stat.h> -#include <sys/mount.h> +#ifdef HAVE_SET_FSID #include <sys/fsuid.h> +#endif +#ifdef GF_DARWIN_HOST_OS +#include <sys/param.h> +#endif +#include <sys/mount.h> #include <sys/socket.h> #include <sys/utsname.h> +#include <sched.h> +#define FUSE_DEVFD_ENV "_FUSE_DEVFD" #define FUSE_COMMFD_ENV "_FUSE_COMMFD" #define FUSE_DEV_OLD "/proc/fs/fuse/dev" @@ -37,6 +52,12 @@ #ifndef MS_DIRSYNC #define MS_DIRSYNC 128 #endif +#ifndef MS_REC +#define MS_REC 16384 +#endif +#ifndef MS_PRIVATE +#define MS_PRIVATE (1<<18) +#endif static const char *progname; @@ -54,97 +75,378 @@ static const char *get_user_name(void) } } +#ifdef HAVE_SET_FSID static uid_t oldfsuid; static gid_t oldfsgid; +#endif static void drop_privs(void) { if (getuid() != 0) { +#ifdef HAVE_SET_FSID oldfsuid = setfsuid(getuid()); oldfsgid = setfsgid(getgid()); +#else + fprintf(stderr, "%s: Implement alternative setfsuid/gid \n", progname); +#endif } } static void restore_privs(void) { if (getuid() != 0) { +#ifdef HAVE_SET_FSID setfsuid(oldfsuid); setfsgid(oldfsgid); +#else + fprintf(stderr, "%s: Implement alternative setfsuid/gid \n", progname); +#endif } } #ifndef IGNORE_MTAB +/* + * Make sure that /etc/mtab is checked and updated atomically + */ +static int lock_umount(void) +{ + const char *mtab_lock = _PATH_MOUNTED ".fuselock"; + int mtablock; + int res; + struct stat mtab_stat; + + /* /etc/mtab could be a symlink to /proc/mounts */ + if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode)) + return -1; + + mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600); + if (mtablock == -1) { + fprintf(stderr, "%s: unable to open fuse lock file: %s\n", + progname, strerror(errno)); + return -1; + } + res = lockf(mtablock, F_LOCK, 0); + if (res < 0) { + fprintf(stderr, "%s: error getting lock: %s\n", progname, + strerror(errno)); + close(mtablock); + return -1; + } + + return mtablock; +} + +static void unlock_umount(int mtablock) +{ + if (mtablock >= 0) { + int res; + + res = lockf(mtablock, F_ULOCK, 0); + if (res < 0) { + fprintf(stderr, "%s: error releasing lock: %s\n", + progname, strerror(errno)); + } + close(mtablock); + } +} + static int add_mount(const char *source, const char *mnt, const char *type, const char *opts) { return fuse_mnt_add_mount(progname, source, mnt, type, opts); } -static int unmount_fuse(const char *mnt, int quiet, int lazy) +static int may_unmount(const char *mnt, int quiet) { - if (getuid() != 0) { - struct mntent *entp; - FILE *fp; - const char *user = NULL; - char uidstr[32]; - unsigned uidlen = 0; - int found; - const char *mtab = _PATH_MOUNTED; - - user = get_user_name(); - if (user == NULL) - return -1; + struct mntent *entp; + FILE *fp; + const char *user = NULL; + char uidstr[32]; + unsigned uidlen = 0; + int found; + const char *mtab = _PATH_MOUNTED; - fp = setmntent(mtab, "r"); - if (fp == NULL) { - fprintf(stderr, - "%s: failed to open %s: %s\n", progname, mtab, - strerror(errno)); - return -1; - } + user = get_user_name(); + if (user == NULL) + return -1; - uidlen = sprintf(uidstr, "%u", getuid()); - - found = 0; - while ((entp = getmntent(fp)) != NULL) { - if (!found && strcmp(entp->mnt_dir, mnt) == 0 && - (strcmp(entp->mnt_type, "fuse") == 0 || - strcmp(entp->mnt_type, "fuseblk") == 0 || - strncmp(entp->mnt_type, "fuse.", 5) == 0 || - strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) { - char *p = strstr(entp->mnt_opts, "user="); - if (p && - (p == entp->mnt_opts || *(p-1) == ',') && - strcmp(p + 5, user) == 0) { - found = 1; - break; - } - /* /etc/mtab is a link pointing to - /proc/mounts: */ - else if ((p = - strstr(entp->mnt_opts, "user_id=")) && - (p == entp->mnt_opts || - *(p-1) == ',') && - strncmp(p + 8, uidstr, uidlen) == 0 && - (*(p+8+uidlen) == ',' || - *(p+8+uidlen) == '\0')) { - found = 1; - break; - } + fp = setmntent(mtab, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab, + strerror(errno)); + return -1; + } + + uidlen = sprintf(uidstr, "%u", getuid()); + + found = 0; + while ((entp = getmntent(fp)) != NULL) { + if (!found && strcmp(entp->mnt_dir, mnt) == 0 && + (strcmp(entp->mnt_type, "fuse") == 0 || + strcmp(entp->mnt_type, "fuseblk") == 0 || + strncmp(entp->mnt_type, "fuse.", 5) == 0 || + strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) { + char *p = strstr(entp->mnt_opts, "user="); + if (p && + (p == entp->mnt_opts || *(p-1) == ',') && + strcmp(p + 5, user) == 0) { + found = 1; + break; + } + /* /etc/mtab is a link pointing to + /proc/mounts: */ + else if ((p = + strstr(entp->mnt_opts, "user_id=")) && + (p == entp->mnt_opts || + *(p-1) == ',') && + strncmp(p + 8, uidstr, uidlen) == 0 && + (*(p+8+uidlen) == ',' || + *(p+8+uidlen) == '\0')) { + found = 1; + break; } } - endmntent(fp); + } + endmntent(fp); - if (!found) { - if (!quiet) - fprintf(stderr, - "%s: entry for %s not found in %s\n", - progname, mnt, mtab); - return -1; + if (!found) { + if (!quiet) + fprintf(stderr, + "%s: entry for %s not found in %s\n", + progname, mnt, mtab); + return -1; + } + + return 0; +} + +/* + * Check whether the file specified in "fusermount -u" is really a + * mountpoint and not a symlink. This is necessary otherwise the user + * could move the mountpoint away and replace it with a symlink + * pointing to an arbitrary mount, thereby tricking fusermount into + * unmounting that (umount(2) will follow symlinks). + * + * This is the child process running in a separate mount namespace, so + * we don't mess with the global namespace and if the process is + * killed for any reason, mounts are automatically cleaned up. + * + * First make sure nothing is propagated back into the parent + * namespace by marking all mounts "private". + * + * Then bind mount parent onto a stable base where the user can't move + * it around. + * + * Finally check /proc/mounts for an entry matching the requested + * mountpoint. If it's found then we are OK, and the user can't move + * it around within the parent directory as rename() will return + * EBUSY. Be careful to ignore any mounts that existed before the + * bind. + */ +static int check_is_mount_child(void *p) +{ + const char **a = p; + const char *last = a[0]; + const char *mnt = a[1]; + int res; + const char *procmounts = "/proc/mounts"; + int found; + FILE *fp; + struct mntent *entp; + int count; + + res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL); + if (res == -1) { + fprintf(stderr, "%s: failed to mark mounts private: %s\n", + progname, strerror(errno)); + return 1; + } + + fp = setmntent(procmounts, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, + procmounts, strerror(errno)); + return 1; + } + + count = 0; + while (getmntent(fp) != NULL) + count++; + endmntent(fp); + + fp = setmntent(procmounts, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, + procmounts, strerror(errno)); + return 1; + } + + res = mount(".", "/", "", MS_BIND | MS_REC, NULL); + if (res == -1) { + fprintf(stderr, "%s: failed to bind parent to /: %s\n", + progname, strerror(errno)); + return 1; + } + + found = 0; + while ((entp = getmntent(fp)) != NULL) { + if (count > 0) { + count--; + continue; + } + if (entp->mnt_dir[0] == '/' && + strcmp(entp->mnt_dir + 1, last) == 0) { + found = 1; + break; } } + endmntent(fp); + + if (!found) { + fprintf(stderr, "%s: %s not mounted\n", progname, mnt); + return 1; + } - return fuse_mnt_umount(progname, mnt, lazy); + return 0; +} + +static pid_t clone_newns(void *a) +{ + char buf[131072]; + char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15)); + +#ifdef __ia64__ + extern int __clone2(int (*fn)(void *), + void *child_stack_base, size_t stack_size, + int flags, void *arg, pid_t *ptid, + void *tls, pid_t *ctid); + + return __clone2(check_is_mount_child, stack, sizeof(buf) / 2, + CLONE_NEWNS, a, NULL, NULL, NULL); +#else + return clone(check_is_mount_child, stack, CLONE_NEWNS, a); +#endif +} + +static int check_is_mount(const char *last, const char *mnt) +{ + pid_t pid, p; + int status; + const char *a[2] = { last, mnt }; + + pid = clone_newns((void *) a); + if (pid == (pid_t) -1) { + fprintf(stderr, "%s: failed to clone namespace: %s\n", + progname, strerror(errno)); + return -1; + } + p = waitpid(pid, &status, __WCLONE); + if (p == (pid_t) -1) { + fprintf(stderr, "%s: waitpid failed: %s\n", + progname, strerror(errno)); + return -1; + } + if (!WIFEXITED(status)) { + fprintf(stderr, "%s: child terminated abnormally (status %i)\n", + progname, status); + return -1; + } + if (WEXITSTATUS(status) != 0) + return -1; + + return 0; +} + +static int chdir_to_parent(char *copy, const char **lastp) +{ + char *tmp; + const char *parent; + char buf[65536]; + int res; + + tmp = strrchr(copy, '/'); + if (tmp == NULL || tmp[1] == '\0') { + fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n", + progname, copy); + return -1; + } + if (tmp != copy) { + *tmp = '\0'; + parent = copy; + *lastp = tmp + 1; + } else if (tmp[1] != '\0') { + *lastp = tmp + 1; + parent = "/"; + } else { + *lastp = "."; + parent = "/"; + } + + res = chdir(parent); + if (res == -1) { + fprintf(stderr, "%s: failed to chdir to %s: %s\n", + progname, parent, strerror(errno)); + return -1; + } + + if (getcwd(buf, sizeof(buf)) == NULL) { + fprintf(stderr, "%s: failed to obtain current directory: %s\n", + progname, strerror(errno)); + return -1; + } + if (strcmp(buf, parent) != 0) { + fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname, + parent, buf); + return -1; + + } + + return 0; +} + +static int unmount_fuse_locked(const char *mnt, int quiet, int lazy) +{ + char *copy; + const char *last; + int res; + + if (getuid() != 0) { + res = may_unmount(mnt, quiet); + if (res == -1) + return -1; + } + + copy = strdup(mnt); + if (copy == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return -1; + } + + res = chdir_to_parent(copy, &last); + if (res == -1) + goto out; + + res = check_is_mount(last, mnt); + if (res == -1) + goto out; + + res = fuse_mnt_umount(progname, mnt, last, lazy); + +out: + free(copy); + + return res; +} + +static int unmount_fuse(const char *mnt, int quiet, int lazy) +{ + int res; + int mtablock = lock_umount(); + + res = unmount_fuse_locked(mnt, quiet, lazy); + unlock_umount(mtablock); + + return res; } static int count_fuse_fs(void) @@ -186,7 +488,7 @@ static int add_mount(const char *source, const char *mnt, const char *type, static int unmount_fuse(const char *mnt, int quiet, int lazy) { - return fuse_mnt_umount(progname, mnt, lazy); + return fuse_mnt_umount(progname, mnt, mnt, lazy); } #endif /* IGNORE_MTAB */ @@ -218,20 +520,22 @@ static void parse_line(char *line, int linenum) static void read_conf(void) { + int len; FILE *fp = fopen(FUSE_CONF, "r"); if (fp != NULL) { int linenum = 1; char line[256]; int isnewline = 1; while (fgets(line, sizeof(line), fp) != NULL) { + len = strlen (line); if (isnewline) { - if (line[strlen(line)-1] == '\n') { + if (len && line[len-1] == '\n') { strip_line(line); parse_line(line, linenum); } else { isnewline = 0; } - } else if(line[strlen(line)-1] == '\n') { + } else if (len && line[len-1] == '\n') { fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum); isnewline = 1; @@ -326,7 +630,7 @@ static int add_option(char **optsp, const char *opt, unsigned expand) static int get_mnt_opts(int flags, char *opts, char **mnt_optsp) { int i; - int l; + size_t l; if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1) return -1; @@ -341,7 +645,7 @@ static int get_mnt_opts(int flags, char *opts, char **mnt_optsp) return -1; /* remove comma from end of opts*/ l = strlen(*mnt_optsp); - if ((*mnt_optsp)[l-1] == ',') + if (l && (*mnt_optsp)[l-1] == ',') (*mnt_optsp)[l-1] = '\0'; if (getuid() != 0) { const char *user = get_user_name(); @@ -366,18 +670,26 @@ static int opt_eq(const char *s, unsigned len, const char *opt) static int get_string_opt(const char *s, unsigned len, const char *opt, char **val) { + int i; unsigned opt_len = strlen(opt); + char *d; - if (*val) - free(*val); + free(*val); *val = (char *) malloc(len - opt_len + 1); if (!*val) { fprintf(stderr, "%s: failed to allocate memory\n", progname); return 0; } - memcpy(*val, s + opt_len, len - opt_len); - (*val)[len - opt_len] = '\0'; + d = *val; + s += opt_len; + len -= opt_len; + for (i = 0; i < len; i++) { + if (s[i] == '\\' && i + 1 < len) + i++; + *d++ = s[i]; + } + *d = '\0'; return 1; } @@ -408,7 +720,12 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, unsigned len; const char *fsname_str = "fsname="; const char *subtype_str = "subtype="; - for (len = 0; s[len] && s[len] != ','; len++); + for (len = 0; s[len]; len++) { + if (s[len] == '\\' && s[len + 1]) + len++; + else if (s[len] == ',') + break; + } if (begins_with(s, fsname_str)) { if (!get_string_opt(s, len, fsname_str, &fsname)) goto err; @@ -526,15 +843,14 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode, fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save)); goto err; - } else { - *sourcep = source; - *typep = type; - *mnt_optsp = mnt_opts; } + *sourcep = source; + *typep = type; + *mnt_optsp = mnt_opts; free(fsname); free(optbuf); - return res; + return 0; err: free(fsname); @@ -577,8 +893,7 @@ static int check_version(const char *dev) return 0; } -static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd, - int *mountpoint_fd) +static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd) { int res; const char *mnt = *mntp; @@ -596,13 +911,6 @@ static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd, return 0; if (S_ISDIR(stbuf->st_mode)) { - *currdir_fd = open(".", O_RDONLY); - if (*currdir_fd == -1) { - fprintf(stderr, - "%s: failed to open current directory: %s\n", - progname, strerror(errno)); - return -1; - } res = chdir(mnt); if (res == -1) { fprintf(stderr, @@ -719,8 +1027,36 @@ static int open_fuse_device(char **devp) return -1; } +static int check_fuse_device(char *devfd, char **devp) +{ + int res; + char *devlink; -static int mount_fuse(const char *mnt, const char *opts) + res = asprintf(&devlink, "/proc/self/fd/%s", devfd); + if (res == -1) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return -1; + } + + *devp = (char *) calloc(1, PATH_MAX + 1); + if (!*devp) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + free(devlink); + return -1; + } + + res = readlink (devlink, *devp, PATH_MAX); + free (devlink); + if (res == -1) { + fprintf(stderr, "%s: specified fuse fd is invalid\n", + progname); + return -1; + } + + return atoi(devfd); +} + +static int mount_fuse(const char *mnt, const char *opts, char *devfd) { int res; int fd; @@ -730,10 +1066,9 @@ static int mount_fuse(const char *mnt, const char *opts) char *source = NULL; char *mnt_opts = NULL; const char *real_mnt = mnt; - int currdir_fd = -1; int mountpoint_fd = -1; - fd = open_fuse_device(&dev); + fd = devfd ? check_fuse_device(devfd, &dev) : open_fuse_device(&dev); if (fd == -1) return -1; @@ -744,15 +1079,13 @@ static int mount_fuse(const char *mnt, const char *opts) int mount_count = count_fuse_fs(); if (mount_count >= mount_max) { fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname); - close(fd); - return -1; + goto fail_close_fd; } } res = check_version(dev); if (res != -1) { - res = check_perm(&real_mnt, &stbuf, &currdir_fd, - &mountpoint_fd); + res = check_perm(&real_mnt, &stbuf, &mountpoint_fd); restore_privs(); if (res != -1) res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, @@ -761,33 +1094,38 @@ static int mount_fuse(const char *mnt, const char *opts) } else restore_privs(); - if (currdir_fd != -1) { - fchdir(currdir_fd); - close(currdir_fd); - } if (mountpoint_fd != -1) close(mountpoint_fd); + if (res == -1) + goto fail_close_fd; + + res = chdir("/"); if (res == -1) { - close(fd); - return -1; + fprintf(stderr, "%s: failed to chdir to '/'\n", progname); + goto fail_close_fd; } if (geteuid() == 0) { res = add_mount(source, mnt, type, mnt_opts); if (res == -1) { - umount2(mnt, 2); /* lazy umount */ - close(fd); - return -1; + /* Can't clean up mount in a non-racy way */ + goto fail_close_fd; } } +out_free: free(source); free(type); free(mnt_opts); free(dev); return fd; + +fail_close_fd: + close(fd); + fd = -1; + goto out_free; } static int send_fd(int sock_fd, int fd) @@ -857,6 +1195,7 @@ int main(int argc, char *argv[]) static int unmount = 0; static int lazy = 0; static int quiet = 0; + char *devfd; char *commfd; int cfd; const char *opts = ""; @@ -925,6 +1264,13 @@ int main(int argc, char *argv[]) drop_privs(); mnt = fuse_mnt_resolve_path(progname, origmnt); + if (mnt != NULL) { + res = chdir("/"); + if (res == -1) { + fprintf(stderr, "%s: failed to chdir to '/'\n", progname); + exit(1); + } + } restore_privs(); if (mnt == NULL) exit(1); @@ -945,21 +1291,26 @@ int main(int argc, char *argv[]) return 0; } - commfd = getenv(FUSE_COMMFD_ENV); - if (commfd == NULL) { - fprintf(stderr, "%s: old style mounting not supported\n", - progname); - exit(1); + devfd = getenv(FUSE_DEVFD_ENV); + if (devfd == NULL) { + commfd = getenv(FUSE_COMMFD_ENV); + if (commfd == NULL) { + fprintf(stderr, "%s: old style mounting not supported\n", + progname); + exit(1); + } } - fd = mount_fuse(mnt, opts); + fd = mount_fuse(mnt, opts, devfd); if (fd == -1) exit(1); - cfd = atoi(commfd); - res = send_fd(cfd, fd); - if (res == -1) - exit(1); + if (devfd == NULL) { + cfd = atoi(commfd); + res = send_fd(cfd, fd); + if (res == -1) + exit(1); + } return 0; } diff --git a/contrib/fuse-util/mount_util.c b/contrib/fuse-util/mount_util.c new file mode 100644 index 00000000000..911b84445e9 --- /dev/null +++ b/contrib/fuse-util/mount_util.c @@ -0,0 +1,64 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU LGPLv2. + See the file COPYING.LIB. +*/ + +#include <dirent.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +int fuse_mnt_check_empty(const char *progname, const char *mnt, + mode_t rootmode, off_t rootsize) +{ + int isempty = 1; + + if (S_ISDIR(rootmode)) { + struct dirent *ent; + DIR *dp = opendir(mnt); + if (dp == NULL) { + fprintf(stderr, + "%s: failed to open mountpoint for reading: %s\n", + progname, strerror(errno)); + return -1; + } + while ((ent = readdir(dp)) != NULL) { + if (strcmp(ent->d_name, ".") != 0 && + strcmp(ent->d_name, "..") != 0) { + isempty = 0; + break; + } + } + closedir(dp); + } else if (rootsize) + isempty = 0; + + if (!isempty) { + fprintf(stderr, "%s: mountpoint is not empty\n", progname); + fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname); + return -1; + } + return 0; +} + +int fuse_mnt_check_fuseblk(void) +{ + char buf[256]; + FILE *f = fopen("/proc/filesystems", "r"); + if (!f) + return 1; + + while (fgets(buf, sizeof(buf), f)) + if (strstr(buf, "fuseblk\n")) { + fclose(f); + return 1; + } + + fclose(f); + return 0; +} diff --git a/contrib/libexecinfo/execinfo.c b/contrib/libexecinfo/execinfo.c new file mode 100644 index 00000000000..03500257788 --- /dev/null +++ b/contrib/libexecinfo/execinfo.c @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2003 Maxim Sobolev <sobomax@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: execinfo.c,v 1.3 2004/07/19 05:21:09 sobomax Exp $ + */ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#ifndef HAVE_BACKTRACE +#include <sys/types.h> +#include <sys/uio.h> +#include <dlfcn.h> +#include <math.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stddef.h> + +#include "execinfo_compat.h" + +#define D10(x) ceil(log10(((x) == 0) ? 2 : ((x) + 1))) + +static void * +getreturnaddr(int level) +{ + switch (level) { + case 0: return __builtin_return_address(1); + case 1: return __builtin_return_address(2); + case 2: return __builtin_return_address(3); + case 3: return __builtin_return_address(4); + case 4: return __builtin_return_address(5); + case 5: return __builtin_return_address(6); + case 6: return __builtin_return_address(7); + case 7: return __builtin_return_address(8); + case 8: return __builtin_return_address(9); + case 9: return __builtin_return_address(10); + case 10: return __builtin_return_address(11); + case 11: return __builtin_return_address(12); + case 12: return __builtin_return_address(13); + case 13: return __builtin_return_address(14); + case 14: return __builtin_return_address(15); + case 15: return __builtin_return_address(16); + case 16: return __builtin_return_address(17); + case 17: return __builtin_return_address(18); + case 18: return __builtin_return_address(19); + case 19: return __builtin_return_address(20); + case 20: return __builtin_return_address(21); + case 21: return __builtin_return_address(22); + case 22: return __builtin_return_address(23); + case 23: return __builtin_return_address(24); + case 24: return __builtin_return_address(25); + case 25: return __builtin_return_address(26); + case 26: return __builtin_return_address(27); + case 27: return __builtin_return_address(28); + case 28: return __builtin_return_address(29); + case 29: return __builtin_return_address(30); + case 30: return __builtin_return_address(31); + case 31: return __builtin_return_address(32); + case 32: return __builtin_return_address(33); + case 33: return __builtin_return_address(34); + case 34: return __builtin_return_address(35); + case 35: return __builtin_return_address(36); + case 36: return __builtin_return_address(37); + case 37: return __builtin_return_address(38); + case 38: return __builtin_return_address(39); + case 39: return __builtin_return_address(40); + case 40: return __builtin_return_address(41); + case 41: return __builtin_return_address(42); + case 42: return __builtin_return_address(43); + case 43: return __builtin_return_address(44); + case 44: return __builtin_return_address(45); + case 45: return __builtin_return_address(46); + case 46: return __builtin_return_address(47); + case 47: return __builtin_return_address(48); + case 48: return __builtin_return_address(49); + case 49: return __builtin_return_address(50); + case 50: return __builtin_return_address(51); + case 51: return __builtin_return_address(52); + case 52: return __builtin_return_address(53); + case 53: return __builtin_return_address(54); + case 54: return __builtin_return_address(55); + case 55: return __builtin_return_address(56); + case 56: return __builtin_return_address(57); + case 57: return __builtin_return_address(58); + case 58: return __builtin_return_address(59); + case 59: return __builtin_return_address(60); + case 60: return __builtin_return_address(61); + case 61: return __builtin_return_address(62); + case 62: return __builtin_return_address(63); + case 63: return __builtin_return_address(64); + case 64: return __builtin_return_address(65); + case 65: return __builtin_return_address(66); + case 66: return __builtin_return_address(67); + case 67: return __builtin_return_address(68); + case 68: return __builtin_return_address(69); + case 69: return __builtin_return_address(70); + case 70: return __builtin_return_address(71); + case 71: return __builtin_return_address(72); + case 72: return __builtin_return_address(73); + case 73: return __builtin_return_address(74); + case 74: return __builtin_return_address(75); + case 75: return __builtin_return_address(76); + case 76: return __builtin_return_address(77); + case 77: return __builtin_return_address(78); + case 78: return __builtin_return_address(79); + case 79: return __builtin_return_address(80); + case 80: return __builtin_return_address(81); + case 81: return __builtin_return_address(82); + case 82: return __builtin_return_address(83); + case 83: return __builtin_return_address(84); + case 84: return __builtin_return_address(85); + case 85: return __builtin_return_address(86); + case 86: return __builtin_return_address(87); + case 87: return __builtin_return_address(88); + case 88: return __builtin_return_address(89); + case 89: return __builtin_return_address(90); + case 90: return __builtin_return_address(91); + case 91: return __builtin_return_address(92); + case 92: return __builtin_return_address(93); + case 93: return __builtin_return_address(94); + case 94: return __builtin_return_address(95); + case 95: return __builtin_return_address(96); + case 96: return __builtin_return_address(97); + case 97: return __builtin_return_address(98); + case 98: return __builtin_return_address(99); + case 99: return __builtin_return_address(100); + case 100: return __builtin_return_address(101); + case 101: return __builtin_return_address(102); + case 102: return __builtin_return_address(103); + case 103: return __builtin_return_address(104); + case 104: return __builtin_return_address(105); + case 105: return __builtin_return_address(106); + case 106: return __builtin_return_address(107); + case 107: return __builtin_return_address(108); + case 108: return __builtin_return_address(109); + case 109: return __builtin_return_address(110); + case 110: return __builtin_return_address(111); + case 111: return __builtin_return_address(112); + case 112: return __builtin_return_address(113); + case 113: return __builtin_return_address(114); + case 114: return __builtin_return_address(115); + case 115: return __builtin_return_address(116); + case 116: return __builtin_return_address(117); + case 117: return __builtin_return_address(118); + case 118: return __builtin_return_address(119); + case 119: return __builtin_return_address(120); + case 120: return __builtin_return_address(121); + case 121: return __builtin_return_address(122); + case 122: return __builtin_return_address(123); + case 123: return __builtin_return_address(124); + case 124: return __builtin_return_address(125); + case 125: return __builtin_return_address(126); + case 126: return __builtin_return_address(127); + case 127: return __builtin_return_address(128); + default: return NULL; + } +} + +static void * +getframeaddr(int level) +{ + + switch (level) { + case 0: return __builtin_frame_address(1); + case 1: return __builtin_frame_address(2); + case 2: return __builtin_frame_address(3); + case 3: return __builtin_frame_address(4); + case 4: return __builtin_frame_address(5); + case 5: return __builtin_frame_address(6); + case 6: return __builtin_frame_address(7); + case 7: return __builtin_frame_address(8); + case 8: return __builtin_frame_address(9); + case 9: return __builtin_frame_address(10); + case 10: return __builtin_frame_address(11); + case 11: return __builtin_frame_address(12); + case 12: return __builtin_frame_address(13); + case 13: return __builtin_frame_address(14); + case 14: return __builtin_frame_address(15); + case 15: return __builtin_frame_address(16); + case 16: return __builtin_frame_address(17); + case 17: return __builtin_frame_address(18); + case 18: return __builtin_frame_address(19); + case 19: return __builtin_frame_address(20); + case 20: return __builtin_frame_address(21); + case 21: return __builtin_frame_address(22); + case 22: return __builtin_frame_address(23); + case 23: return __builtin_frame_address(24); + case 24: return __builtin_frame_address(25); + case 25: return __builtin_frame_address(26); + case 26: return __builtin_frame_address(27); + case 27: return __builtin_frame_address(28); + case 28: return __builtin_frame_address(29); + case 29: return __builtin_frame_address(30); + case 30: return __builtin_frame_address(31); + case 31: return __builtin_frame_address(32); + case 32: return __builtin_frame_address(33); + case 33: return __builtin_frame_address(34); + case 34: return __builtin_frame_address(35); + case 35: return __builtin_frame_address(36); + case 36: return __builtin_frame_address(37); + case 37: return __builtin_frame_address(38); + case 38: return __builtin_frame_address(39); + case 39: return __builtin_frame_address(40); + case 40: return __builtin_frame_address(41); + case 41: return __builtin_frame_address(42); + case 42: return __builtin_frame_address(43); + case 43: return __builtin_frame_address(44); + case 44: return __builtin_frame_address(45); + case 45: return __builtin_frame_address(46); + case 46: return __builtin_frame_address(47); + case 47: return __builtin_frame_address(48); + case 48: return __builtin_frame_address(49); + case 49: return __builtin_frame_address(50); + case 50: return __builtin_frame_address(51); + case 51: return __builtin_frame_address(52); + case 52: return __builtin_frame_address(53); + case 53: return __builtin_frame_address(54); + case 54: return __builtin_frame_address(55); + case 55: return __builtin_frame_address(56); + case 56: return __builtin_frame_address(57); + case 57: return __builtin_frame_address(58); + case 58: return __builtin_frame_address(59); + case 59: return __builtin_frame_address(60); + case 60: return __builtin_frame_address(61); + case 61: return __builtin_frame_address(62); + case 62: return __builtin_frame_address(63); + case 63: return __builtin_frame_address(64); + case 64: return __builtin_frame_address(65); + case 65: return __builtin_frame_address(66); + case 66: return __builtin_frame_address(67); + case 67: return __builtin_frame_address(68); + case 68: return __builtin_frame_address(69); + case 69: return __builtin_frame_address(70); + case 70: return __builtin_frame_address(71); + case 71: return __builtin_frame_address(72); + case 72: return __builtin_frame_address(73); + case 73: return __builtin_frame_address(74); + case 74: return __builtin_frame_address(75); + case 75: return __builtin_frame_address(76); + case 76: return __builtin_frame_address(77); + case 77: return __builtin_frame_address(78); + case 78: return __builtin_frame_address(79); + case 79: return __builtin_frame_address(80); + case 80: return __builtin_frame_address(81); + case 81: return __builtin_frame_address(82); + case 82: return __builtin_frame_address(83); + case 83: return __builtin_frame_address(84); + case 84: return __builtin_frame_address(85); + case 85: return __builtin_frame_address(86); + case 86: return __builtin_frame_address(87); + case 87: return __builtin_frame_address(88); + case 88: return __builtin_frame_address(89); + case 89: return __builtin_frame_address(90); + case 90: return __builtin_frame_address(91); + case 91: return __builtin_frame_address(92); + case 92: return __builtin_frame_address(93); + case 93: return __builtin_frame_address(94); + case 94: return __builtin_frame_address(95); + case 95: return __builtin_frame_address(96); + case 96: return __builtin_frame_address(97); + case 97: return __builtin_frame_address(98); + case 98: return __builtin_frame_address(99); + case 99: return __builtin_frame_address(100); + case 100: return __builtin_frame_address(101); + case 101: return __builtin_frame_address(102); + case 102: return __builtin_frame_address(103); + case 103: return __builtin_frame_address(104); + case 104: return __builtin_frame_address(105); + case 105: return __builtin_frame_address(106); + case 106: return __builtin_frame_address(107); + case 107: return __builtin_frame_address(108); + case 108: return __builtin_frame_address(109); + case 109: return __builtin_frame_address(110); + case 110: return __builtin_frame_address(111); + case 111: return __builtin_frame_address(112); + case 112: return __builtin_frame_address(113); + case 113: return __builtin_frame_address(114); + case 114: return __builtin_frame_address(115); + case 115: return __builtin_frame_address(116); + case 116: return __builtin_frame_address(117); + case 117: return __builtin_frame_address(118); + case 118: return __builtin_frame_address(119); + case 119: return __builtin_frame_address(120); + case 120: return __builtin_frame_address(121); + case 121: return __builtin_frame_address(122); + case 122: return __builtin_frame_address(123); + case 123: return __builtin_frame_address(124); + case 124: return __builtin_frame_address(125); + case 125: return __builtin_frame_address(126); + case 126: return __builtin_frame_address(127); + case 127: return __builtin_frame_address(128); + default: return NULL; + } +} + +static inline void * +realloc_safe(void *ptr, size_t size) +{ + void *nptr; + + nptr = realloc (ptr, size); + if (nptr == NULL) + free (ptr); + return nptr; +} + +int +backtrace(void **buffer, int size) +{ + int i; + + for (i = 1; getframeaddr(i + 1) != NULL && i != size + 1; i++) { + buffer[i - 1] = getreturnaddr(i); + if (buffer[i - 1] == NULL) + break; + } + return i - 1; +} + +char ** +backtrace_symbols(void *const *buffer, int size) +{ + size_t clen, alen; + int i, offset; + char **rval; + Dl_info info; + + clen = size * sizeof(char *); + rval = malloc(clen); + if (rval == NULL) + return NULL; + for (i = 0; i < size; i++) { + if (dladdr(buffer[i], &info) != 0) { + if (info.dli_sname == NULL) + info.dli_sname = "???"; + if (info.dli_saddr == NULL) + info.dli_saddr = buffer[i]; + offset = buffer[i] - info.dli_saddr; + /* "0x01234567 <function+offset> at filename" */ + alen = 2 + /* "0x" */ + (sizeof(void *) * 2) + /* "01234567" */ + 2 + /* " <" */ + strlen(info.dli_sname) + /* "function" */ + 1 + /* "+" */ + 10 + /* "offset */ + 5 + /* "> at " */ + strlen(info.dli_fname) + /* "filename" */ + 1; /* "\0" */ + rval = realloc_safe(rval, clen + alen); + if (rval == NULL) + return NULL; + snprintf((char *) rval + clen, alen, "%p <%s+%d> at %s", + buffer[i], info.dli_sname, offset, info.dli_fname); + } else { + alen = 2 + /* "0x" */ + (sizeof(void *) * 2) + /* "01234567" */ + 1; /* "\0" */ + rval = realloc_safe(rval, clen + alen); + if (rval == NULL) + return NULL; + snprintf((char *) rval + clen, alen, "%p", buffer[i]); + } + rval[i] = (char *) clen; + clen += alen; + } + + for (i = 0; i < size; i++) + rval[i] += (long) rval; + + return rval; +} + +void +backtrace_symbols_fd(void *const *buffer, int size, int fd) +{ + int i, len, offset; + char *buf; + Dl_info info; + + for (i = 0; i < size; i++) { + if (dladdr(buffer[i], &info) != 0) { + if (info.dli_sname == NULL) + info.dli_sname = "???"; + if (info.dli_saddr == NULL) + info.dli_saddr = buffer[i]; + offset = buffer[i] - info.dli_saddr; + /* "0x01234567 <function+offset> at filename" */ + len = 2 + /* "0x" */ + (sizeof(void *) * 2) + /* "01234567" */ + 2 + /* " <" */ + strlen(info.dli_sname) + /* "function" */ + 1 + /* "+" */ + D10(offset) + /* "offset */ + 5 + /* "> at " */ + strlen(info.dli_fname) + /* "filename" */ + 2; /* "\n\0" */ + buf = alloca(len); + if (buf == NULL) + return; + snprintf(buf, len, "%p <%s+%d> at %s\n", + buffer[i], info.dli_sname, offset, info.dli_fname); + } else { + len = 2 + /* "0x" */ + (sizeof(void *) * 2) + /* "01234567" */ + 2; /* "\n\0" */ + buf = alloca(len); + if (buf == NULL) + return; + snprintf(buf, len, "%p\n", buffer[i]); + } + if (write(fd, buf, strlen(buf)) == -1) + return; + } +} +#endif diff --git a/contrib/libexecinfo/execinfo_compat.h b/contrib/libexecinfo/execinfo_compat.h new file mode 100644 index 00000000000..ae84cfb1f35 --- /dev/null +++ b/contrib/libexecinfo/execinfo_compat.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2003 Maxim Sobolev <sobomax@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: execinfo.h,v 1.2 2004/07/19 05:20:29 sobomax Exp $ + */ + +#ifndef _EXECINFO_H_ +#define _EXECINFO_H_ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#ifndef HAVE_BACKTRACE +#ifdef __cplusplus +extern "C" { +#endif + +extern int backtrace(void **, int); +extern char **backtrace_symbols(void *const *, int); +extern void backtrace_symbols_fd(void *const *, int, int); + +#ifdef __cplusplus +} +#endif +#endif + +#endif /* _EXECINFO_H_ */ diff --git a/contrib/libgen/basename_r.c b/contrib/libgen/basename_r.c new file mode 100644 index 00000000000..2c3a87afe1c --- /dev/null +++ b/contrib/libgen/basename_r.c @@ -0,0 +1,40 @@ +/* + * borrowed from glibc-2.12.1/string/basename.c + * Modified to return "." for NULL or "", as required for SUSv2. + */ +#include <string.h> +#include <stdlib.h> +#ifdef THREAD_UNSAFE_BASENAME + +/* Return the name-within-directory of a file name. + Copyright (C) 1996,97,98,2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +char * +basename_r (filename) + const char *filename; +{ + char *p; + + if ((filename == NULL) || (*filename == '\0')) + return "."; + + p = strrchr (filename, '/'); + return p ? p + 1 : (char *) filename; +} +#endif /* THREAD_UNSAFE_BASENAME */ diff --git a/contrib/libgen/dirname_r.c b/contrib/libgen/dirname_r.c new file mode 100644 index 00000000000..131cbcf2a96 --- /dev/null +++ b/contrib/libgen/dirname_r.c @@ -0,0 +1,243 @@ +/* + * Borrowed from glibc-2.12.1/string/memrchr.c + * Based on strlen implementation by Torbjorn Granlund (tege@sics.se), + * Removed code for long bigger than 32 bytes, renamed __ptr_t as void * + * changed reg_char type to char. + */ +#include <string.h> +#include <stdlib.h> +#ifdef THREAD_UNSAFE_DIRNAME + +/* memrchr -- find the last occurrence of a byte in a memory block + Copyright (C) 1991, 93, 96, 97, 99, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Based on strlen implementation by Torbjorn Granlund (tege@sics.se), + with help from Dan Sahlin (dan@sics.se) and + commentary by Jim Blandy (jimb@ai.mit.edu); + adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu), + and implemented by Roland McGrath (roland@ai.mit.edu). + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +void * +__memrchr (s, c_in, n) + const void * s; + int c_in; + size_t n; +{ + const unsigned char *char_ptr; + const unsigned long int *longword_ptr; + unsigned long int longword, magic_bits, charmask; + unsigned char c; + + c = (unsigned char) c_in; + + /* Handle the last few characters by reading one character at a time. + Do this until CHAR_PTR is aligned on a longword boundary. */ + for (char_ptr = (const unsigned char *) s + n; + n > 0 && ((unsigned long int) char_ptr + & (sizeof (longword) - 1)) != 0; + --n) + if (*--char_ptr == c) + return (void *) char_ptr; + + /* All these elucidatory comments refer to 4-byte longwords, + but the theory applies equally well to 8-byte longwords. */ + + longword_ptr = (const unsigned long int *) char_ptr; + + /* Bits 31, 24, 16, and 8 of this number are zero. Call these bits + the "holes." Note that there is a hole just to the left of + each byte, with an extra at the end: + + bits: 01111110 11111110 11111110 11111111 + bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD + + The 1-bits make sure that carries propagate to the next 0-bit. + The 0-bits provide holes for carries to fall into. */ + + if (sizeof (longword) != 4 && sizeof (longword) != 8) + abort (); + + magic_bits = 0x7efefeff; + + /* Set up a longword, each of whose bytes is C. */ + charmask = c | (c << 8); + charmask |= charmask << 16; + + /* Instead of the traditional loop which tests each character, + we will test a longword at a time. The tricky part is testing + if *any of the four* bytes in the longword in question are zero. */ + while (n >= sizeof (longword)) + { + /* We tentatively exit the loop if adding MAGIC_BITS to + LONGWORD fails to change any of the hole bits of LONGWORD. + + 1) Is this safe? Will it catch all the zero bytes? + Suppose there is a byte with all zeros. Any carry bits + propagating from its left will fall into the hole at its + least significant bit and stop. Since there will be no + carry from its most significant bit, the LSB of the + byte to the left will be unchanged, and the zero will be + detected. + + 2) Is this worthwhile? Will it ignore everything except + zero bytes? Suppose every byte of LONGWORD has a bit set + somewhere. There will be a carry into bit 8. If bit 8 + is set, this will carry into bit 16. If bit 8 is clear, + one of bits 9-15 must be set, so there will be a carry + into bit 16. Similarly, there will be a carry into bit + 24. If one of bits 24-30 is set, there will be a carry + into bit 31, so all of the hole bits will be changed. + + The one misfire occurs when bits 24-30 are clear and bit + 31 is set; in this case, the hole at bit 31 is not + changed. If we had access to the processor carry flag, + we could close this loophole by putting the fourth hole + at bit 32! + + So it ignores everything except 128's, when they're aligned + properly. + + 3) But wait! Aren't we looking for C, not zero? + Good point. So what we do is XOR LONGWORD with a longword, + each of whose bytes is C. This turns each byte that is C + into a zero. */ + + longword = *--longword_ptr ^ charmask; + + /* Add MAGIC_BITS to LONGWORD. */ + if ((((longword + magic_bits) + + /* Set those bits that were unchanged by the addition. */ + ^ ~longword) + + /* Look at only the hole bits. If any of the hole bits + are unchanged, most likely one of the bytes was a + zero. */ + & ~magic_bits) != 0) + { + /* Which of the bytes was C? If none of them were, it was + a misfire; continue the search. */ + + const unsigned char *cp = (const unsigned char *) longword_ptr; + + if (cp[3] == c) + return (void *) &cp[3]; + if (cp[2] == c) + return (void *) &cp[2]; + if (cp[1] == c) + return (void *) &cp[1]; + if (cp[0] == c) + return (void *) cp; + } + + n -= sizeof (longword); + } + + char_ptr = (const unsigned char *) longword_ptr; + + while (n-- > 0) + { + if (*--char_ptr == c) + return (void *) char_ptr; + } + + return 0; +} + +/* + * Borrowed from glibc-2.12.1/misc/dirname.c + */ + +/* dirname - return directory part of PATH. + Copyright (C) 1996, 2000, 2001, 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +char * +dirname_r (char *path) +{ + static const char dot[] = "."; + char *last_slash; + + /* Find last '/'. */ + last_slash = path != NULL ? strrchr (path, '/') : NULL; + + if (last_slash != NULL && last_slash != path && last_slash[1] == '\0') + { + /* Determine whether all remaining characters are slashes. */ + char *runp; + + for (runp = last_slash; runp != path; --runp) + if (runp[-1] != '/') + break; + + /* The '/' is the last character, we have to look further. */ + if (runp != path) + last_slash = __memrchr (path, '/', runp - path); + } + + if (last_slash != NULL) + { + /* Determine whether all remaining characters are slashes. */ + char *runp; + + for (runp = last_slash; runp != path; --runp) + if (runp[-1] != '/') + break; + + /* Terminate the path. */ + if (runp == path) + { + /* The last slash is the first character in the string. We have to + return "/". As a special case we have to return "//" if there + are exactly two slashes at the beginning of the string. See + XBD 4.10 Path Name Resolution for more information. */ + if (last_slash == path + 1) + ++last_slash; + else + last_slash = path + 1; + } + else + last_slash = runp; + + last_slash[0] = '\0'; + } + else + /* This assignment is ill-designed but the XPG specs require to + return a string containing "." in any case no directory part is + found and so a static and constant string is required. */ + path = (char *) dot; + + return path; +} +#endif /* THREAD_UNSAFE_DIRNAME */ diff --git a/contrib/macfuse/COPYING.txt b/contrib/macfuse/COPYING.txt new file mode 100644 index 00000000000..3f89bb08dc5 --- /dev/null +++ b/contrib/macfuse/COPYING.txt @@ -0,0 +1,128 @@ +MacFUSE is a package developed by Google and is covered under the following +BSD-style license: + + ================================================================ + Copyright (c) 2007-2009 Google Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ================================================================ + +Note that Google's patches to the FUSE library (libfuse/*.patch) (and to +the SSHFS user-space program (filesystems/sshfs/*.patch) are also released +under the BSD license. + +Portions of this package were derived from code developed by other authors. +Please read further for specific details. + +* fusefs/fuse_kernel.h is an unmodified copy of the interface header from + the Linux FUSE distribution (http://fuse.sourceforge.net). fuse_kernel.h + can be redistributed either under the GPL or under the BSD license. It + is being redistributed here under the BSD license. + +* Unless otherwise noted, parts of MacFUSE (multiple files in fusefs/) contain + code derived from the FreeBSD version of FUSE (http://fuse4bsd.creo.hu), + which is covered by the following BSD-style license: + + ================================================================ + Copyright (C) 2005 Csaba Henk. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + ================================================================ + +* fusefs/fuse_nodehash.c is a modified version of HashNode.c from an + Apple Developer Technical Support (DTS) sample code example. The original + source, which is available on http://developer.apple.com/samplecode/, has + the following disclaimer: + + ================================================================ + Disclaimer: IMPORTANT: This Apple software is supplied to you by + Apple Computer, Inc. Apple") in consideration of your agreement + to the following terms, and your use, installation, modification + or redistribution of this Apple software constitutes acceptance + of these terms. If you do not agree with these terms, please do + not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, + and subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software + (the "Apple Software"), to use, reproduce, modify and redistribute + the Apple Software, with or without modifications, in source and/or + binary forms; provided that if you redistribute the Apple Software + in its entirety and without modifications, you must retain this + notice and the following text and disclaimers in all such + redistributions of the Apple Software. Neither the name, + trademarks, service marks or logos of Apple Computer, Inc. may be + used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, + express or implied, are granted by Apple herein, including but + not limited to any patent rights that may be infringed by your + derivative works or by other works in which the Apple Software + may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR + ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, + INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, + REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, + HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING + NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ================================================================ + +* Parts of the mount_fusefs and the load_fusefs command-line programs + (implemented in fusefs/mount_fusefs/ and fusefs/load_fusefs/, respectively) + come from Apple's Darwin sources and are covered under the Apple Public + Source License (APSL). You can read the APSL at: + + http://www.publicsource.apple.com/apsl/ diff --git a/contrib/macfuse/fuse_ioctl.h b/contrib/macfuse/fuse_ioctl.h new file mode 100644 index 00000000000..054968cb13e --- /dev/null +++ b/contrib/macfuse/fuse_ioctl.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2006-2008 Google. All Rights Reserved. + * Amit Singh <singh@> + */ + +#ifndef _FUSE_IOCTL_H_ +#define _FUSE_IOCTL_H_ + +#include <stdint.h> +#include <sys/ioctl.h> + +/* FUSEDEVIOCxxx */ + +/* Get mounter's pid. */ +#define FUSEDEVGETMOUNTERPID _IOR('F', 1, u_int32_t) + +/* Check if FUSE_INIT kernel-user handshake is complete. */ +#define FUSEDEVIOCGETHANDSHAKECOMPLETE _IOR('F', 2, u_int32_t) + +/* Mark the daemon as dead. */ +#define FUSEDEVIOCSETDAEMONDEAD _IOW('F', 3, u_int32_t) + +/* Tell the kernel which operations the daemon implements. */ +#define FUSEDEVIOCSETIMPLEMENTEDBITS _IOW('F', 4, u_int64_t) + +/* Get device's random "secret". */ +#define FUSEDEVIOCGETRANDOM _IOR('F', 5, u_int32_t) + +/* + * The 'AVFI' (alter-vnode-for-inode) ioctls all require an inode number + * as an argument. In the user-space library, you can get the inode number + * from a path by using fuse_lookup_inode_by_path_np() [lib/fuse.c]. + * + * To see an example of using this, see the implementation of + * fuse_purge_path_np() in lib/fuse_darwin.c. + */ + +struct fuse_avfi_ioctl { + uint64_t inode; + uint64_t cmd; + uint32_t ubc_flags; + uint32_t note; + off_t size; +}; + +/* Alter the vnode (if any) specified by the given inode. */ +#define FUSEDEVIOCALTERVNODEFORINODE _IOW('F', 6, struct fuse_avfi_ioctl) +#define FSCTLALTERVNODEFORINODE IOCBASECMD(FUSEDEVIOCALTERVNODEFORINODE) + +/* + * Possible cmd values for AVFI. + */ + +#define FUSE_AVFI_MARKGONE 0x00000001 /* no ubc_flags */ +#define FUSE_AVFI_PURGEATTRCACHE 0x00000002 /* no ubc_flags */ +#define FUSE_AVFI_PURGEVNCACHE 0x00000004 /* no ubc_flags */ +#define FUSE_AVFI_UBC 0x00000008 /* uses ubc_flags */ +#define FUSE_AVFI_UBC_SETSIZE 0x00000010 /* uses ubc_flags, size */ +#define FUSE_AVFI_KNOTE 0x00000020 /* uses note */ + +#define FUSE_SETACLSTATE _IOW('h', 10, int32_t) +#define FSCTLSETACLSTATE IOCBASECMD(FUSE_SETACLSTATE) + +#endif /* _FUSE_IOCTL_H_ */ diff --git a/contrib/macfuse/fuse_param.h b/contrib/macfuse/fuse_param.h new file mode 100644 index 00000000000..347db9464bc --- /dev/null +++ b/contrib/macfuse/fuse_param.h @@ -0,0 +1,164 @@ +/* + * 'rebel' branch modifications: + * Copyright (C) 2010 Tuxera. All Rights Reserved. + */ + +/* + * Copyright (C) 2006-2008 Google. All Rights Reserved. + * Amit Singh <singh@> + */ + +#ifndef _FUSE_PARAM_H_ +#define _FUSE_PARAM_H_ + +#include <AvailabilityMacros.h> + +/* Compile-time tunables (M_OSXFUSE*) */ + +#define M_OSXFUSE_ENABLE_FIFOFS 0 +#define M_OSXFUSE_ENABLE_INTERRUPT 1 +#define M_OSXFUSE_ENABLE_SPECFS 0 +#define M_OSXFUSE_ENABLE_TSLOCKING 1 +#define M_OSXFUSE_ENABLE_UNSUPPORTED 1 +#define M_OSXFUSE_ENABLE_XATTR 1 +#define M_OSXFUSE_ENABLE_DSELECT 1 + +#if M_OSXFUSE_ENABLE_UNSUPPORTED +# define M_OSXFUSE_ENABLE_EXCHANGE 1 +# define M_OSXFUSE_ENABLE_KUNC 0 +# define M_OSXFUSE_ENABLE_INTERIM_FSNODE_LOCK 1 +#endif /* M_OSXFUSE_ENABLE_UNSUPPORTED */ + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 +# if M_OSXFUSE_ENABLE_UNSUPPORTED + /* + * In Mac OS X 10.5 the file system implementation is responsible for + * posting kqueue events. Starting with Mac OS X 10.6 VFS took over that + * job. + */ +# define M_OSXFUSE_ENABLE_KQUEUE 1 +# endif +#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < 1060 */ + +#if M_OSXFUSE_ENABLE_INTERIM_FSNODE_LOCK +# define M_OSXFUSE_ENABLE_HUGE_LOCK 0 +# define M_OSXFUSE_ENABLE_LOCK_LOGGING 0 +# define FUSE_VNOP_EXPORT __private_extern__ +#else +# define FUSE_VNOP_EXPORT static +#endif /* M_OSXFUSE_ENABLE_INTERIM_FSNODE_LOCK */ + +/* User Control */ + +#define OSXFUSE_POSTUNMOUNT_SIGNAL SIGKILL + +#define MACOSX_ADMIN_GROUP_NAME "admin" + +#define SYSCTL_OSXFUSE_TUNABLES_ADMIN "osxfuse.tunables.admin_group" +#define SYSCTL_OSXFUSE_VERSION_NUMBER "osxfuse.version.number" + +/* Paths */ + +#define OSXFUSE_BUNDLE_PATH "/Library/Filesystems/osxfusefs.fs" +#define OSXFUSE_KEXT OSXFUSE_BUNDLE_PATH "/Support/osxfusefs.kext" +#define OSXFUSE_LOAD_PROG OSXFUSE_BUNDLE_PATH "/Support/load_osxfusefs" +#define OSXFUSE_MOUNT_PROG OSXFUSE_BUNDLE_PATH "/Support/mount_osxfusefs" +#define SYSTEM_KEXTLOAD "/sbin/kextload" +#define SYSTEM_KEXTUNLOAD "/sbin/kextunload" + +/* Compatible API version */ + +#define OSXFUSE_MIN_USER_VERSION_MAJOR 7 +#define OSXFUSE_MIN_USER_VERSION_MINOR 5 + +/* Device Interface */ + +/* + * This is the prefix ("osxfuse" by default) of the name of a FUSE device node + * in devfs. The suffix is the device number. "/dev/osxfuse0" is the first FUSE + * device by default. If you change the prefix from the default to something + * else, the user-space FUSE library will need to know about it too. + */ +#define OSXFUSE_DEVICE_BASENAME "osxfuse" + +/* + * This is the number of /dev/osxfuse<n> nodes we will create. <n> goes from + * 0 to (OSXFUSE_NDEVICES - 1). + */ +#define OSXFUSE_NDEVICES 24 + +/* + * This is the default block size of the virtual storage devices that are + * implicitly implemented by the FUSE kernel extension. This can be changed + * on a per-mount basis (there's one such virtual device for each mount). + */ +#define FUSE_DEFAULT_BLOCKSIZE 4096 + +#define FUSE_MIN_BLOCKSIZE 512 +#define FUSE_MAX_BLOCKSIZE MAXPHYS + +#ifndef MAX_UPL_TRANSFER +#define MAX_UPL_TRANSFER 256 +#endif + +/* + * This is default I/O size used while accessing the virtual storage devices. + * This can be changed on a per-mount basis. + * + * Nevertheless, the I/O size must be at least as big as the block size. + */ +#define FUSE_DEFAULT_IOSIZE (16 * PAGE_SIZE) + +#define FUSE_MIN_IOSIZE 512 +#define FUSE_MAX_IOSIZE (MAX_UPL_TRANSFER * PAGE_SIZE) + +#define FUSE_DEFAULT_INIT_TIMEOUT 10 /* s */ +#define FUSE_MIN_INIT_TIMEOUT 1 /* s */ +#define FUSE_MAX_INIT_TIMEOUT 300 /* s */ +#define FUSE_INIT_WAIT_INTERVAL 100000 /* us */ + +#define FUSE_INIT_TIMEOUT_DEFAULT_BUTTON_TITLE "OK" +#define FUSE_INIT_TIMEOUT_NOTICE_MESSAGE \ + "Timed out waiting for the file system to initialize. The volume has " \ + "been ejected. You can use the init_timeout mount option to wait longer." + +#define FUSE_DEFAULT_DAEMON_TIMEOUT 60 /* s */ +#define FUSE_MIN_DAEMON_TIMEOUT 0 /* s */ +#define FUSE_MAX_DAEMON_TIMEOUT 600 /* s */ + +#define FUSE_DAEMON_TIMEOUT_DEFAULT_BUTTON_TITLE "Keep Trying" +#define FUSE_DAEMON_TIMEOUT_OTHER_BUTTON_TITLE "Force Eject" +#define FUSE_DAEMON_TIMEOUT_ALTERNATE_BUTTON_TITLE "Don't Warn Again" +#define FUSE_DAEMON_TIMEOUT_ALERT_MESSAGE \ + "There was a timeout waiting for the file system to respond. You can " \ + "eject this volume immediately, but unsaved changes may be lost." +#define FUSE_DAEMON_TIMEOUT_ALERT_TIMEOUT 120 /* s */ + +#ifdef KERNEL + +/* + * This is the soft upper limit on the number of "request tickets" FUSE's + * user-kernel IPC layer can have for a given mount. This can be modified + * through the fuse.* sysctl interface. + */ +#define FUSE_DEFAULT_MAX_FREE_TICKETS 1024 +#define FUSE_DEFAULT_IOV_PERMANENT_BUFSIZE (1 << 19) +#define FUSE_DEFAULT_IOV_CREDIT 16 + +/* User-Kernel IPC Buffer */ + +#define FUSE_MIN_USERKERNEL_BUFSIZE (128 * 1024) +#define FUSE_MAX_USERKERNEL_BUFSIZE (16 * 1024 * 1024) + +#define FUSE_REASONABLE_XATTRSIZE FUSE_MIN_USERKERNEL_BUFSIZE + +#endif /* KERNEL */ + +#define FUSE_DEFAULT_USERKERNEL_BUFSIZE (16 * 1024 * 1024) + +#define FUSE_LINK_MAX LINK_MAX +#define FUSE_UIO_BACKUP_MAX 8 + +#define FUSE_MAXNAMLEN 255 + +#endif /* _FUSE_PARAM_H_ */ diff --git a/contrib/macfuse/mount_darwin.c b/contrib/macfuse/mount_darwin.c new file mode 100644 index 00000000000..d1d1c34e761 --- /dev/null +++ b/contrib/macfuse/mount_darwin.c @@ -0,0 +1,264 @@ +/* + * Derived from mount_bsd.c from the fuse distribution. + * + * FUSE: Filesystem in Userspace + * Copyright (C) 2005-2006 Csaba Henk <csaba.henk@creo.hu> + * Copyright (C) 2007-2009 Amit Singh <asingh@gmail.com> + * Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com> + * + * This program can be distributed under the terms of the GNU LGPLv2. + * See the file COPYING.LIB. + */ + +#undef _POSIX_C_SOURCE +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/sysctl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <paths.h> + +#include <libproc.h> +#include <sys/utsname.h> + +#include <sys/param.h> +#include <sys/mount.h> +#include <AssertMacros.h> + +#include "fuse_param.h" +#include "fuse_ioctl.h" + +#include "glusterfs/glusterfs.h" +#include "glusterfs/logging.h" +#include "glusterfs/common-utils.h" + +#define GFFUSE_LOGERR(...) \ + gf_log ("glusterfs-fuse", GF_LOG_ERROR, ## __VA_ARGS__) + +int +gf_fuse_mount (const char *mountpoint, char *fsname, char *mnt_param, + pid_t *mnt_pid, int status_fd) /* Not used on OS X */ +{ + int fd = 0; + int pid = 0; + int ret = 0; + char *fdnam = NULL; + char *dev = NULL; + char vstr[4]; + unsigned vval = 0; + int i = 0; + + const char *mountprog = OSXFUSE_MOUNT_PROG; + sig_t chldf = SIG_ERR; + char version[MAXHOSTNAMELEN + 1] = { 0 }; + size_t version_len = MAXHOSTNAMELEN; + size_t version_len_desired = 0; + int r = 0; + char devpath[MAXPATHLEN] = { 0 };; + + if (!mountpoint) { + gf_log ("glustefs-fuse", GF_LOG_ERROR, + "missing or invalid mount point"); + goto err; + } + + /* mount_fusefs should not try to spawn the daemon */ + setenv("MOUNT_FUSEFS_SAFE", "1", 1); + + /* to notify mount_fusefs it's called from lib */ + setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1); + + chldf = signal(SIGCHLD, SIG_DFL); /* So that we can wait4() below. */ + + if (chldf == SIG_ERR) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "signal() returned SIG_ERR: %s", + strerror(errno)); + goto err; + } + + /* check for user<->kernel match. */ + ret = sysctlbyname(SYSCTL_OSXFUSE_VERSION_NUMBER, version, + &version_len, NULL, (size_t)0); + if (ret != 0) { + gf_log ("glustefs-fuse", GF_LOG_ERROR, + "sysctlbyname() returned error: %s", + strerror(errno)); + goto err; + } + + /* sysctlbyname() includes the trailing '\0' in version_len */ + version_len_desired = sizeof ("2.x.y"); + + if (version_len != version_len_desired) { + gf_log ("glusterfs-fuse", GF_LOG_ERROR, + "version length mismatch for OSXFUSE %s", + version); + ret = -1; + goto err; + } + + for (i = 0; i < 3; i++) + vstr[i] = version[2*i]; + vstr[3] = '\0'; + + vval = strtoul(vstr, NULL, 10); + if (vval < 264) { + GFFUSE_LOGERR("OSXFUSE version %s is not supported", version); + ret = -1; + goto err; + } + + gf_log("glusterfs-fuse", GF_LOG_INFO, + "OSXFUSE kext version supported %s", version); + + fdnam = getenv("FUSE_DEV_FD"); + if (fdnam) { + fd = strtol(fdnam, NULL, 10); + if (fd < 0) { + GFFUSE_LOGERR("invalid value given in FUSE_DEV_FD"); + ret = -1; + goto err; + } + goto mount; + } + + dev = getenv("FUSE_DEV_NAME"); + if (!dev) { + for (r = 0; r < OSXFUSE_NDEVICES; r++) { + snprintf(devpath, MAXPATHLEN - 1, + _PATH_DEV OSXFUSE_DEVICE_BASENAME "%d", r); + if ((fd = open(devpath, O_RDWR)) < 0) { + GFFUSE_LOGERR("failed to open device %s (%s)", + devpath, + strerror(errno)); + goto err; + } + dev = devpath; + goto mount; + } + } + + fd = open(dev, O_RDWR); + if (fd < 0) { + GFFUSE_LOGERR("failed to open device %s (%s)", dev, + strerror(errno)); + ret = -1; + goto err; + } + +mount: + signal(SIGCHLD, chldf); + + pid = fork(); + if (pid == -1) { + GFFUSE_LOGERR("fork() failed (%s)", strerror(errno)); + ret = -1; + goto err; + } + + if (pid == 0) { + pid = fork(); + if (pid == -1) { + GFFUSE_LOGERR("fork() failed (%s)", strerror(errno)); + ret = -1; + goto err; + } + + if (pid == 0) { + const char *argv[32]; + int a = 0; + char *opts = NULL; + + if (asprintf(&opts, "%s,fssubtype=glusterfs", + mnt_param) == -1) { + GFFUSE_LOGERR("asprintf() error: %s", + strerror(errno)); + ret = -1; + goto err; + } + + if (!fdnam) + asprintf(&fdnam, "%d", fd); + + argv[a++] = mountprog; + if (opts) { + argv[a++] = "-o"; + argv[a++] = opts; + } + argv[a++] = fdnam; + argv[a++] = mountpoint; + argv[a++] = NULL; + + { + char title[MAXPATHLEN + 1] = { 0 }; + u_int32_t len = MAXPATHLEN; + int ret = proc_pidpath(getpid(), title, len); + if (ret) { + setenv("MOUNT_FUSEFS_DAEMON_PATH", + title, 1); + } + } + execvp(mountprog, (char **) argv); + GFFUSE_LOGERR("OSXFUSE: failed to exec mount" + " program (%s)", strerror(errno)); + _exit(1); + } + _exit(0); + } + ret = fd; +err: + if (ret == -1) { + if (fd > 0) { + close(fd); + } + } + return ret; +} + +void +gf_fuse_unmount(const char *mountpoint, int fd) +{ + int ret; + struct stat sbuf; + char dev[128]; + char resolved_path[PATH_MAX]; + char *ep, *rp = NULL; + + unsigned int hs_complete = 0; + + ret = ioctl(fd, FUSEDEVIOCGETHANDSHAKECOMPLETE, &hs_complete); + if (ret || !hs_complete) { + return; + } + + if (fstat(fd, &sbuf) == -1) { + return; + } + + devname_r(sbuf.st_rdev, S_IFCHR, dev, 128); + + if (strncmp(dev, OSXFUSE_DEVICE_BASENAME, + sizeof(OSXFUSE_DEVICE_BASENAME) - 1)) { + return; + } + + strtol(dev + sizeof(OSXFUSE_DEVICE_BASENAME) - 1, &ep, 10); + if (*ep != '\0') { + return; + } + + rp = realpath(mountpoint, resolved_path); + if (rp) { + ret = unmount(resolved_path, 0); + } + + close(fd); + return; +} diff --git a/contrib/mount/mntent.c b/contrib/mount/mntent.c new file mode 100644 index 00000000000..9a7e5f39bdb --- /dev/null +++ b/contrib/mount/mntent.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 1980, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2001 + * David Rufino <daverufino@btinternet.com> + * Copyright (c) 2014 + * Red Hat, Inc. <http://www.redhat.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(GF_LINUX_HOST_OS) + +#include <stdlib.h> +#include <string.h> +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#include "mntent_compat.h" + +#ifdef __NetBSD__ +typedef struct statvfs gf_statfs_t; +#else +typedef struct statfs gf_statfs_t; +#endif + +typedef struct _mntent_state { + struct mntent mntent; + gf_statfs_t *statfs; + int count; + int pos; + /* A buffer big enough to store all defined flags as a string. + * Increase it if necessary when more flags are defined. */ + char buf[256]; +} mntent_state_t; + +typedef struct _mntflag { + unsigned long value; + const char *on; + const char *off; +} mntflag_t; + +static mntflag_t mntflags[] = { + { MNT_RDONLY, "ro", "rw" }, + { MNT_SYNCHRONOUS, "sync", NULL }, + { MNT_NOEXEC, "noexec", NULL }, + { MNT_NOSUID, "nosuid", NULL }, +#if !defined(__FreeBSD__) + { MNT_NODEV, "nodev", NULL }, +#endif /* __FreeBSD__ */ + { MNT_UNION, "union", NULL }, + { MNT_ASYNC, "async", NULL }, +#if !defined(GF_DARWIN_HOST_OS) + { MNT_NOATIME, "noatime", NULL }, +#if !defined(__NetBSD__) + { MNT_NOCLUSTERR, "noclusterr", NULL }, + { MNT_NOCLUSTERW, "noclusterw", NULL }, + { MNT_NOSYMFOLLOW, "nosymfollow", NULL }, + { MNT_SUIDDIR, "suiddir", NULL }, +#endif /* !__NetBSD__ */ +#endif /* !GF_DARWIN_HOST_OS */ + { 0, NULL, NULL } +}; + +char * +hasmntopt (const struct mntent *mnt, const char *option) +{ + char *opt, *optbuf; + int len; + + optbuf = strdup(mnt->mnt_opts); + if (optbuf == NULL) { + return NULL; + } + + opt = optbuf; + len = 0; + while (*opt) { + while (opt[len] != 0) { + if (opt[len] == ' ') { + opt[len++] = 0; + break; + } + len++; + } + if ((*opt != 0) && (strcasecmp(opt, option) == 0)) { + break; + } + opt += len; + len = 0; + } + free(optbuf); + if (len == 0) { + return NULL; + } + + return opt - optbuf + mnt->mnt_opts; +} + +static int +writeopt(const char *text, char *buf, int buflen, int pos) +{ + int len; + + /* buflen must be > 0 */ + + if (text == NULL) { + return pos; + } + + buf += pos; + if (pos > 0) { + /* We are sure we have at least one byte to store the space. + * We don't need to check buflen here. */ + *buf++ = ' '; + pos++; + } + len = strlen(text) + 1; + pos += len; + if (pos >= buflen) { + /* There won't be enough space for the text and the + * terminating null character. We copy as much as we can + * of the text and mark the end of the string with '...' */ + memcpy(buf, text, buflen - pos + len); + if (buflen > 3) { + strcpy(buf + buflen - 4, "..."); + } else { + strncpy(buf, "...", buflen - 1); + buf[buflen - 1] = 0; + } + pos = buflen; + } else { + memcpy(buf, text, len); + } + + return pos; +} + +static char * +flags2opts (int flags, char *buf, int buflen) +{ + char other[16]; + mntflag_t *flg; + int pos; + + if (buflen == 0) { + return NULL; + } + + pos = 0; + for (flg = mntflags; flg->value != 0; flg++) { + pos = writeopt((flags & flg->value) == 0 ? flg->off : flg->on, + buf, buflen, pos); + flags &= ~flg->value; + } + + if (flags != 0) { + sprintf(other, "[0x%x]", flags); + writeopt(other, buf, buflen, pos); + } + + return buf; +} + +static void +statfs_to_mntent (struct mntent *mntent, gf_statfs_t *mntbuf, char *buf, + int buflen) +{ + int f_flags; + + mntent->mnt_fsname = mntbuf->f_mntfromname; + mntent->mnt_dir = mntbuf->f_mntonname; + mntent->mnt_type = mntbuf->f_fstypename; + +#ifdef __NetBSD__ + f_flags = mntbuf->f_flag; +#else + f_flags = mntbuf->f_flags; +#endif + mntent->mnt_opts = flags2opts (f_flags, buf, buflen); + + mntent->mnt_freq = mntent->mnt_passno = 0; +} + +struct mntent * +getmntent_r (FILE *fp, struct mntent *mntent, char *buf, int buflen) +{ + mntent_state_t *state = (mntent_state_t *)fp; + + if (state->pos >= state->count) { + return NULL; + } + + statfs_to_mntent(mntent, &state->statfs[state->pos++], buf, buflen); + + return mntent; +} + +struct mntent * +getmntent (FILE *fp) +{ + mntent_state_t *state = (mntent_state_t *)fp; + + return getmntent_r(fp, &state->mntent, state->buf, + sizeof(state->buf)); +} + +FILE * +setmntent (const char *filename, const char *type) +{ + mntent_state_t *state; + + /* We don't really need to access any file so we'll use the FILE* as + * a fake file to store state information. + */ + + state = malloc(sizeof(mntent_state_t)); + if (state != NULL) { + state->pos = 0; + state->count = getmntinfo(&state->statfs, MNT_NOWAIT); + } + + return (FILE *)state; +} + +int +endmntent (FILE *fp) +{ + free(fp); + + return 1; /* endmntent() always returns 1 */ +} + +#endif /* !GF_LINUX_HOST_OS */ diff --git a/contrib/mount/mntent_compat.h b/contrib/mount/mntent_compat.h new file mode 100644 index 00000000000..ca82e9aa60f --- /dev/null +++ b/contrib/mount/mntent_compat.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _MNTENT_H +#define _MNTENT_H + +#if !defined(GF_LINUX_HOST_OS) +#include <stdio.h> + +struct mntent { + char *mnt_fsname; + char *mnt_dir; + char *mnt_type; + char *mnt_opts; + int mnt_freq; + int mnt_passno; +}; + +struct mntent *getmntent (FILE *fp); +struct mntent *getmntent_r (FILE *fp, struct mntent *result, + char *buffer, int bufsize); +FILE *setmntent (const char *filename, const char *type); +int endmntent(FILE *fp); +char * hasmntopt (const struct mntent *mnt, const char *option); + +/* Dummy - /etc/mtab has no meaning on OSX platform */ +#define _PATH_MOUNTED "/etc/mtab" + +#endif /* GF_DARWIN_HOST_OS || __NetBSD__ */ +#endif /* _MNTENT_H */ diff --git a/contrib/rbtree/rb.c b/contrib/rbtree/rb.c index d1339b97d0a..6184c507e72 100644 --- a/contrib/rbtree/rb.c +++ b/contrib/rbtree/rb.c @@ -1,26 +1,30 @@ /* Produced by texiweb from libavl.w. */ /* libavl - library for manipulation of binary trees. - Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software + Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + This code is also covered by the following earlier license notice: This program is free software; 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. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. - - The author may be contacted at <blp@gnu.org> on the Internet, or - write to Ben Pfaff, Stanford University, Computer Science Dept., 353 - Serra Mall, Stanford CA 94305, USA. */ #include <assert.h> diff --git a/contrib/rbtree/rb.h b/contrib/rbtree/rb.h index c8858b55682..97b44cfd412 100644 --- a/contrib/rbtree/rb.h +++ b/contrib/rbtree/rb.h @@ -1,26 +1,30 @@ /* Produced by texiweb from libavl.w. */ /* libavl - library for manipulation of binary trees. - Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software + Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + This code is also covered by the following earlier license notice: This program is free software; 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. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. - - The author may be contacted at <blp@gnu.org> on the Internet, or - write to Ben Pfaff, Stanford University, Computer Science Dept., 353 - Serra Mall, Stanford CA 94305, USA. */ #ifndef RB_H @@ -51,7 +55,7 @@ void rb_free (struct libavl_allocator *, void *); /* Maximum RB height. */ #ifndef RB_MAX_HEIGHT -#define RB_MAX_HEIGHT 48 +#define RB_MAX_HEIGHT 128 #endif /* Tree data structure. */ diff --git a/contrib/timer-wheel/find_last_bit.c b/contrib/timer-wheel/find_last_bit.c new file mode 100644 index 00000000000..192fee802a8 --- /dev/null +++ b/contrib/timer-wheel/find_last_bit.c @@ -0,0 +1,61 @@ +/* bit search implementation + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * Copyright (C) 2008 IBM Corporation + * 'find_last_bit' is written by Rusty Russell <rusty@rustcorp.com.au> + * (Inspired by David Howell's find_next_bit implementation) + * + * Rewritten by Yury Norov <yury.norov@gmail.com> to decrease + * size and improve performance, 2015. + * + * This program is free software; 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. + */ + +/** + * @find_last_bit + * optimized implementation of find last bit in + */ + +#ifndef BITS_PER_LONG +#ifdef __LP64__ +#define BITS_PER_LONG 64 +#else +#define BITS_PER_LONG 32 +#endif +#endif + +unsigned long gw_tw_fls (unsigned long word) +{ + int num = BITS_PER_LONG; + +#if BITS_PER_LONG == 64 + if (!(word & (~0ul << 32))) { + num -= 32; + word <<= 32; + } +#endif + if (!(word & (~0ul << (BITS_PER_LONG-16)))) { + num -= 16; + word <<= 16; + } + if (!(word & (~0ul << (BITS_PER_LONG-8)))) { + num -= 8; + word <<= 8; + } + if (!(word & (~0ul << (BITS_PER_LONG-4)))) { + num -= 4; + word <<= 4; + } + if (!(word & (~0ul << (BITS_PER_LONG-2)))) { + num -= 2; + word <<= 2; + } + if (!(word & (~0ul << (BITS_PER_LONG-1)))) + num -= 1; + return num; +} diff --git a/contrib/timer-wheel/timer-wheel.c b/contrib/timer-wheel/timer-wheel.c new file mode 100644 index 00000000000..58e0607bf0c --- /dev/null +++ b/contrib/timer-wheel/timer-wheel.c @@ -0,0 +1,374 @@ +/* + * linux/kernel/timer.c + * + * Kernel internal timers + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + */ +/* + This program is free software; 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/select.h> + +#include "timer-wheel.h" + +static inline void +__gf_tw_add_timer (struct tvec_base *base, struct gf_tw_timer_list *timer) +{ + int i; + unsigned long idx; + unsigned long expires; + struct list_head *vec; + + expires = timer->expires; + + idx = expires - base->timer_sec; + + if (idx < TVR_SIZE) { + i = expires & TVR_MASK; + vec = base->tv1.vec + i; + } else if (idx < 1 << (TVR_BITS + TVN_BITS)) { + i = (expires >> TVR_BITS) & TVN_MASK; + vec = base->tv2.vec + i; + } else if (idx < 1 << (TVR_BITS + 2*TVN_BITS)) { + i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; + vec = base->tv3.vec + i; + } else if (idx < 1 << (TVR_BITS + 3*TVN_BITS)) { + i = (expires >> (TVR_BITS + 2*TVN_BITS)) & TVN_MASK; + vec = base->tv4.vec + i; + } else if (idx < 0) { + vec = base->tv1.vec + (base->timer_sec & TVR_MASK); + } else { + i = (expires >> (TVR_BITS + 3*TVN_BITS)) & TVN_MASK; + vec = base->tv5.vec + i; + } + + list_add_tail (&timer->entry, vec); +} + +unsigned long gf_tw_find_last_bit(const unsigned long *, unsigned long); + +#if defined(__GNUC__) || defined(__clang__) +static inline unsigned long gf_tw_fls (unsigned long word) +{ + return BITS_PER_LONG - __builtin_clzl(word); +} +#else +extern unsigned long gf_tw_fls (unsigned long); +#endif + +static inline unsigned long +apply_slack(struct tvec_base *base, struct gf_tw_timer_list *timer) +{ + long delta; + unsigned long mask, expires, expires_limit; + + expires = timer->expires; + + delta = expires - base->timer_sec; + if (delta < 256) + return expires; + + expires_limit = expires + delta / 256; + mask = expires ^ expires_limit; + if (mask == 0) + return expires; + + int bit = gf_tw_fls (mask); + mask = (1UL << bit) - 1; + + expires_limit = expires_limit & ~(mask); + return expires_limit; +} + +static inline void +__gf_tw_detach_timer (struct gf_tw_timer_list *timer) +{ + struct list_head *entry = &timer->entry; + + list_del (entry); + entry->next = NULL; +} + +static inline int +cascade (struct tvec_base *base, struct tvec *tv, int index) +{ + struct gf_tw_timer_list *timer, *tmp; + struct list_head tv_list; + + list_replace_init (tv->vec + index, &tv_list); + + list_for_each_entry_safe (timer, tmp, &tv_list, entry) { + __gf_tw_add_timer (base, timer); + } + + return index; +} + +#define INDEX(N) ((base->timer_sec >> (TVR_BITS + N * TVN_BITS)) & TVN_MASK) + +/** + * run expired timers + */ +static inline void +run_timers (struct tvec_base *base) +{ + unsigned long index, call_time; + struct gf_tw_timer_list *timer; + + struct list_head work_list; + struct list_head *head = &work_list; + + pthread_spin_lock (&base->lock); + { + index = base->timer_sec & TVR_MASK; + + if (!index && + (!cascade (base, &base->tv2, INDEX(0))) && + (!cascade (base, &base->tv3, INDEX(1))) && + (!cascade (base, &base->tv4, INDEX(2)))) + cascade (base, &base->tv5, INDEX(3)); + + call_time = base->timer_sec++; + list_replace_init (base->tv1.vec + index, head); + while (!list_empty(head)) { + void (*fn)(struct gf_tw_timer_list *, void *, unsigned long); + void *data; + + timer = list_first_entry (head, struct gf_tw_timer_list, entry); + fn = timer->function; + data = timer->data; + + __gf_tw_detach_timer (timer); + pthread_spin_unlock(&base->lock); + { + /* It is required to run the actual function outside + of the locked zone, so we don't bother about + locked operations inside that function */ + fn(timer, data, call_time); + } + pthread_spin_lock(&base->lock); + } + } + pthread_spin_unlock (&base->lock); + +} + +void *runner (void *arg) +{ + struct timeval tv = {0,}; + struct tvec_base *base = arg; + + while (1) { + run_timers (base); + + tv.tv_sec = 1; + tv.tv_usec = 0; + (void) select (0, NULL, NULL, NULL, &tv); + } + + return NULL; + +} + +static inline int timer_pending (struct gf_tw_timer_list *timer) +{ + struct list_head *entry = &timer->entry; + + return (entry->next != NULL); +} + +static inline int __detach_if_pending (struct gf_tw_timer_list *timer) +{ + if (!timer_pending (timer)) + return 0; + + __gf_tw_detach_timer (timer); + return 1; +} + +static inline int __mod_timer (struct tvec_base *base, + struct gf_tw_timer_list *timer, int pending_only) +{ + int ret = 0; + + ret = __detach_if_pending (timer); + if (!ret && pending_only) + goto done; + + ret = 1; + __gf_tw_add_timer (base, timer); + + done: + return ret; +} + +/* interface */ + +/** + * Add a timer in the timer wheel + */ +void gf_tw_add_timer (struct tvec_base *base, struct gf_tw_timer_list *timer) +{ + pthread_spin_lock (&base->lock); + { + timer->expires += base->timer_sec; + timer->expires = apply_slack (base, timer); + __gf_tw_add_timer (base, timer); + } + pthread_spin_unlock (&base->lock); +} + +/** + * Remove a timer from the timer wheel + */ +int gf_tw_del_timer (struct tvec_base *base, struct gf_tw_timer_list *timer) +{ + int ret = 0; + + pthread_spin_lock (&base->lock); + { + if (timer_pending (timer)) { + ret = 1; + __gf_tw_detach_timer (timer); + } + } + pthread_spin_unlock (&base->lock); + + return ret; +} + +int gf_tw_mod_timer_pending (struct tvec_base *base, + struct gf_tw_timer_list *timer, + unsigned long expires) +{ + int ret = 1; + + pthread_spin_lock (&base->lock); + { + timer->expires = expires + base->timer_sec; + timer->expires = apply_slack (base, timer); + + ret = __mod_timer (base, timer, 1); + } + pthread_spin_unlock (&base->lock); + + return ret; +} + +int gf_tw_mod_timer (struct tvec_base *base, + struct gf_tw_timer_list *timer, unsigned long expires) +{ + int ret = 1; + + pthread_spin_lock (&base->lock); + { + /* fast path optimization */ + if (timer_pending (timer) && timer->expires == expires) + goto unblock; + + timer->expires = expires + base->timer_sec; + timer->expires = apply_slack (base, timer); + + ret = __mod_timer (base, timer, 0); + } + unblock: + pthread_spin_unlock (&base->lock); + + return ret; +} + +int gf_tw_cleanup_timers (struct tvec_base *base) +{ + int ret = 0; + void *res = NULL; + + /* terminate runner */ + ret = pthread_cancel (base->runner); + if (ret != 0) + goto error_return; + ret = pthread_join (base->runner, &res); + if (ret != 0) + goto error_return; + if (res != PTHREAD_CANCELED) + goto error_return; + + /* destroy lock */ + ret = pthread_spin_destroy (&base->lock); + if (ret != 0) + goto error_return; + + /* deallocated timer base */ + free (base); + return 0; + + error_return: + return -1; +} + +/** + * Initialize various timer wheel lists and spawn a thread that + * invokes run_timers() + */ +struct tvec_base *gf_tw_init_timers () +{ + int j = 0; + int ret = 0; + struct timeval tv = {0,}; + struct tvec_base *base = NULL; + + base = malloc (sizeof (*base)); + if (!base) + goto error_return; + + ret = pthread_spin_init (&base->lock, 0); + if (ret != 0) + goto error_dealloc; + + for (j = 0; j < TVN_SIZE; j++) { + INIT_LIST_HEAD (base->tv5.vec + j); + INIT_LIST_HEAD (base->tv4.vec + j); + INIT_LIST_HEAD (base->tv3.vec + j); + INIT_LIST_HEAD (base->tv2.vec + j); + } + + for (j = 0; j < TVR_SIZE; j++) { + INIT_LIST_HEAD (base->tv1.vec + j); + } + + ret = gettimeofday (&tv, 0); + if (ret < 0) + goto destroy_lock; + base->timer_sec = tv.tv_sec; + + ret = pthread_create (&base->runner, NULL, runner, base); + if (ret != 0) + goto destroy_lock; + return base; + + destroy_lock: + (void) pthread_spin_destroy (&base->lock); + error_dealloc: + free (base); + error_return: + return NULL; +} diff --git a/contrib/timer-wheel/timer-wheel.h b/contrib/timer-wheel/timer-wheel.h new file mode 100644 index 00000000000..5637735ec22 --- /dev/null +++ b/contrib/timer-wheel/timer-wheel.h @@ -0,0 +1,77 @@ +/* + This program is free software; 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef __TIMER_WHEEL_H +#define __TIMER_WHEEL_H + +#include "glusterfs/locking.h" + +#include "glusterfs/list.h" + +#define TVR_BITS 8 +#define TVN_BITS 6 +#define TVR_SIZE (1 << TVR_BITS) +#define TVN_SIZE (1 << TVN_BITS) +#define TVR_MASK (TVR_SIZE - 1) +#define TVN_MASK (TVN_SIZE - 1) + +#define BITS_PER_LONG 64 + +struct tvec { + struct list_head vec[TVN_SIZE]; +}; + +struct tvec_root { + struct list_head vec[TVR_SIZE]; +}; + +struct tvec_base { + pthread_spinlock_t lock; /* base lock */ + + pthread_t runner; /* run_timer() */ + + unsigned long timer_sec; /* time counter */ + + struct tvec_root tv1; + struct tvec tv2; + struct tvec tv3; + struct tvec tv4; + struct tvec tv5; +}; + +struct gf_tw_timer_list { + void *data; + unsigned long expires; + + /** callback routine */ + void (*function)(struct gf_tw_timer_list *, void *, unsigned long); + + struct list_head entry; +}; + +/** The API! */ +struct tvec_base *gf_tw_init_timers (); +int gf_tw_cleanup_timers (struct tvec_base *); +void gf_tw_add_timer (struct tvec_base *, struct gf_tw_timer_list *); +int gf_tw_del_timer (struct tvec_base *, struct gf_tw_timer_list *); + +int gf_tw_mod_timer_pending (struct tvec_base *, + struct gf_tw_timer_list *, unsigned long); + +int gf_tw_mod_timer (struct tvec_base *, + struct gf_tw_timer_list *, unsigned long); + +#endif diff --git a/contrib/umountd/Makefile.am b/contrib/umountd/Makefile.am new file mode 100644 index 00000000000..b39e000f5aa --- /dev/null +++ b/contrib/umountd/Makefile.am @@ -0,0 +1,11 @@ +sbin_PROGRAMS = umountd +umountd_SOURCES = umountd.c +umountd_CFLAGS = $(GF_CFLAGS) -DDATADIR=\"$(localstatedir)\" +umountd_LDADD = $(top_builddir)/libglusterfs/src/libglusterfs.la ${GF_LDADD} +umountd_LDFLAGS = $(GF_LDFLAGS) + +AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ + -I$(top_srcdir)/rpc/xdr/src -I$(top_builddir)/rpc/xdr/src +AM_CFLAGS = -Wall $(GF_CFLAGS) + +CLEANFILES = diff --git a/contrib/umountd/umountd.c b/contrib/umountd/umountd.c new file mode 100644 index 00000000000..3f933ecb554 --- /dev/null +++ b/contrib/umountd/umountd.c @@ -0,0 +1,255 @@ +/* + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <dirent.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mount.h> + +#include "glusterfs/glusterfs.h" +#include "glusterfs/globals.h" +#include "glusterfs/logging.h" +#include "glusterfs/syscall.h" +#include "glusterfs/mem-types.h" + +static void +usage (void) +{ + fprintf (stderr, "Usage: umountd [-d dev] [-t timeout] [-r] path\n"); + exit (EXIT_FAILURE); +} + + +static int +sanity_check (char *path, dev_t *devp) +{ + struct stat st; + struct stat parent_st; + int ret; + char pathtmp[PATH_MAX]; + char *parent; + + if (path == NULL) + usage (); + + if ((ret = stat (path, &st)) != 0) { + switch (errno) { + case ENOTCONN: + /* volume is stopped */ + break; + default: + gf_log ("umountd", GF_LOG_ERROR, + "Cannot access %s: %s\n", + path, strerror (errno)); + goto out; + } + } + + /* If dev was not specified, get it from path */ + if (*devp == -1 && ret == 0) + *devp = st.st_dev; + + snprintf (pathtmp, PATH_MAX, "%s", path); + parent = dirname (pathtmp); + + if (stat (parent, &parent_st) != 0) { + gf_log ("umountd", GF_LOG_ERROR, + "Cannot access %s: %s\n", + parent, strerror (errno)); + goto out; + } + + if (st.st_dev == parent_st.st_dev) { + gf_log ("umountd", GF_LOG_ERROR, + "No filesystem mounted on %s\n", path); + goto out; + } + + ret = 0; + +out: + return ret; +} + +static void +log_rotate (int signum) +{ + gf_log_logrotate (1); + + if (signal (SIGHUP, *log_rotate) == SIG_ERR) { + gf_log ("umountd", GF_LOG_ERROR, "signal () failed"); + exit (EXIT_FAILURE); + } + + return; +} + +static int +logging_init (void) +{ + glusterfs_ctx_t *ctx; + char log_file[PATH_MAX]; + int ret = -1; + + ctx = glusterfs_ctx_new (); + if (!ctx) { + fprintf (stderr, "glusterfs_ctx_new failed\n"); + goto out; + } + + ret = glusterfs_globals_init (ctx); + if (ret) { + fprintf (stderr, "glusterfs_globals_init failed\n"); + goto out; + } + + THIS->ctx = ctx; + xlator_mem_acct_init (THIS, gf_common_mt_end); + + snprintf (log_file, PATH_MAX, + "%s/umountd.log", DEFAULT_LOG_FILE_DIRECTORY); + + ret = gf_log_init (ctx, log_file, "umountd"); + if (ret) { + fprintf (stderr, "gf_log_init failed\n"); + goto out; + } + + if (signal (SIGHUP, *log_rotate) == SIG_ERR) { + gf_log ("umountd", GF_LOG_ERROR, "signal () failed"); + goto out; + } + + ret = 0; +out: + return ret; +} + +static int +umountd_async (char *path, dev_t dev, int frmdir, int timeout) +{ + int ret = -1; + struct stat stbuf = {0, }; + int unmount_ret = 0; + + do { + unmount_ret = unmount (path, 0); + if (unmount_ret == 0) + gf_log ("umountd", GF_LOG_INFO, "Unmounted %s", path); + + if (unmount_ret != 0 && errno != EBUSY) { + gf_log ("umountd", GF_LOG_WARNING, + "umount %s failed: %s", + path, strerror (errno)); + } + + ret = sys_lstat (path, &stbuf); + if (ret != 0) { + gf_log ("umountd", GF_LOG_WARNING, + "Cannot stat device from %s", + path, strerror (errno)); + break; + } + + if (stbuf.st_dev != dev) { + if (unmount_ret != 0) + gf_log ("umountd", GF_LOG_INFO, + "device mismatch " + "(expect %lld, found %lld), " + "someone else unmounted %s", + dev, stbuf.st_dev, path); + ret = 0; + break; + } + + sleep (timeout); + } while (1/*CONSTCOND*/); + + if (ret) { + gf_log ("umountd", GF_LOG_ERROR, + "Asynchronous unmount of %s failed: %s", + path, strerror (errno)); + } else { + if (frmdir) { + ret = rmdir (path); + if (ret) + gf_log ("umountd", GF_LOG_WARNING, + "rmdir %s failed: %s", + path, strerror (errno)); + else + gf_log ("umountd", GF_LOG_INFO, + "Removed %s", path); + } + } + + return ret; +} + +int +main (int argc, char **argv) +{ + char *path = NULL; + dev_t dev = -1; + int frmdir = 0; + int timeout = 30; + int f; + + while ((f = getopt (argc, argv, "d:rt:")) != -1) { + switch (f) { + case 'p': + path = optarg; + break; + case 'd': + dev = strtoll (optarg, NULL, 10); + break; + case 't': + timeout = atoi (optarg); + break; + case 'r': + frmdir = 1; + break; + default: + usage (); + break; + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage (); + + path = argv[0]; + + if (logging_init () != 0) + exit (EXIT_FAILURE); + + if (sanity_check (path, &dev) != 0) + exit (EXIT_FAILURE); + + if (daemon (0, 0) != 0) + exit (EXIT_FAILURE); + + if (umountd_async (path, dev, frmdir, timeout) != 0) + exit (EXIT_FAILURE); + + return EXIT_SUCCESS; +} diff --git a/contrib/userspace-rcu/rculist-extra.h b/contrib/userspace-rcu/rculist-extra.h new file mode 100644 index 00000000000..274cf9f9d7a --- /dev/null +++ b/contrib/userspace-rcu/rculist-extra.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2002 Free Software Foundation, Inc. + * (originally part of the GNU C Library) + * Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. + * + * Copyright (C) 2009 Pierre-Marc Fournier + * Conversion to RCU list. + * Copyright (C) 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef URCU_RCULIST_EXTRA_H +#define URCU_RCULIST_EXTRA_H +/* Copying this definition from liburcu-0.8 as liburcu-0.7 does not have this + * particular list api + */ +/* Add new element at the tail of the list. */ + +static inline +void cds_list_add_tail_rcu(struct cds_list_head *newp, + struct cds_list_head *head) +{ + newp->next = head; + newp->prev = head->prev; + rcu_assign_pointer(head->prev->next, newp); + head->prev = newp; +} + +#endif diff --git a/contrib/userspace-rcu/static-wfcqueue.h b/contrib/userspace-rcu/static-wfcqueue.h new file mode 100644 index 00000000000..37d14ad674b --- /dev/null +++ b/contrib/userspace-rcu/static-wfcqueue.h @@ -0,0 +1,685 @@ +#ifndef _URCU_WFCQUEUE_STATIC_H +#define _URCU_WFCQUEUE_STATIC_H + +/* + * urcu/static/wfcqueue.h + * + * Userspace RCU library - Concurrent Queue with Wait-Free Enqueue/Blocking Dequeue + * + * TO BE INCLUDED ONLY IN LGPL-COMPATIBLE CODE. See urcu/wfcqueue.h for + * linking dynamically with the userspace rcu library. + * + * Copyright 2010-2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * Copyright 2011-2012 - Lai Jiangshan <laijs@cn.fujitsu.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Copied from userspace-rcu 0.10 because version 0.7 doesn't contain it. */ + +#include <pthread.h> +#include <assert.h> +#include <poll.h> +#include <stdbool.h> +#include <urcu/compiler.h> +#include <urcu/uatomic.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Concurrent queue with wait-free enqueue/blocking dequeue. + * + * This queue has been designed and implemented collaboratively by + * Mathieu Desnoyers and Lai Jiangshan. Inspired from + * half-wait-free/half-blocking queue implementation done by Paul E. + * McKenney. + * + * Mutual exclusion of cds_wfcq_* / __cds_wfcq_* API + * + * Synchronization table: + * + * External synchronization techniques described in the API below is + * required between pairs marked with "X". No external synchronization + * required between pairs marked with "-". + * + * Legend: + * [1] cds_wfcq_enqueue + * [2] __cds_wfcq_splice (destination queue) + * [3] __cds_wfcq_dequeue + * [4] __cds_wfcq_splice (source queue) + * [5] __cds_wfcq_first + * [6] __cds_wfcq_next + * + * [1] [2] [3] [4] [5] [6] + * [1] - - - - - - + * [2] - - - - - - + * [3] - - X X X X + * [4] - - X - X X + * [5] - - X X - - + * [6] - - X X - - + * + * Mutual exclusion can be ensured by holding cds_wfcq_dequeue_lock(). + * + * For convenience, cds_wfcq_dequeue_blocking() and + * cds_wfcq_splice_blocking() hold the dequeue lock. + * + * Besides locking, mutual exclusion of dequeue, splice and iteration + * can be ensured by performing all of those operations from a single + * thread, without requiring any lock. + */ + +#define WFCQ_ADAPT_ATTEMPTS 10 /* Retry if being set */ +#define WFCQ_WAIT 10 /* Wait 10 ms if being set */ + +/* + * cds_wfcq_node_init: initialize wait-free queue node. + */ +static inline void _cds_wfcq_node_init(struct cds_wfcq_node *node) +{ + node->next = NULL; +} + +/* + * cds_wfcq_init: initialize wait-free queue (with lock). Pair with + * cds_wfcq_destroy(). + */ +static inline void _cds_wfcq_init(struct cds_wfcq_head *head, + struct cds_wfcq_tail *tail) +{ + int ret; + + /* Set queue head and tail */ + _cds_wfcq_node_init(&head->node); + tail->p = &head->node; + ret = pthread_mutex_init(&head->lock, NULL); + assert(!ret); +} + +/* + * cds_wfcq_destroy: destroy wait-free queue (with lock). Pair with + * cds_wfcq_init(). + */ +static inline void _cds_wfcq_destroy(struct cds_wfcq_head *head, + struct cds_wfcq_tail *tail) +{ + int ret = pthread_mutex_destroy(&head->lock); + assert(!ret); +} + +/* + * __cds_wfcq_init: initialize wait-free queue (without lock). Don't + * pair with any destroy function. + */ +static inline void ___cds_wfcq_init(struct __cds_wfcq_head *head, + struct cds_wfcq_tail *tail) +{ + /* Set queue head and tail */ + _cds_wfcq_node_init(&head->node); + tail->p = &head->node; +} + +/* + * cds_wfcq_empty: return whether wait-free queue is empty. + * + * No memory barrier is issued. No mutual exclusion is required. + * + * We perform the test on head->node.next to check if the queue is + * possibly empty, but we confirm this by checking if the tail pointer + * points to the head node because the tail pointer is the linearisation + * point of the enqueuers. Just checking the head next pointer could + * make a queue appear empty if an enqueuer is preempted for a long time + * between xchg() and setting the previous node's next pointer. + */ +static inline bool _cds_wfcq_empty(cds_wfcq_head_ptr_t u_head, + struct cds_wfcq_tail *tail) +{ + struct __cds_wfcq_head *head = u_head._h; + /* + * Queue is empty if no node is pointed by head->node.next nor + * tail->p. Even though the tail->p check is sufficient to find + * out of the queue is empty, we first check head->node.next as a + * common case to ensure that dequeuers do not frequently access + * enqueuer's tail->p cache line. + */ + return CMM_LOAD_SHARED(head->node.next) == NULL + && CMM_LOAD_SHARED(tail->p) == &head->node; +} + +static inline void _cds_wfcq_dequeue_lock(struct cds_wfcq_head *head, + struct cds_wfcq_tail *tail) +{ + int ret; + + ret = pthread_mutex_lock(&head->lock); + assert(!ret); +} + +static inline void _cds_wfcq_dequeue_unlock(struct cds_wfcq_head *head, + struct cds_wfcq_tail *tail) +{ + int ret; + + ret = pthread_mutex_unlock(&head->lock); + assert(!ret); +} + +static inline bool ___cds_wfcq_append(cds_wfcq_head_ptr_t u_head, + struct cds_wfcq_tail *tail, + struct cds_wfcq_node *new_head, + struct cds_wfcq_node *new_tail) +{ + struct __cds_wfcq_head *head = u_head._h; + struct cds_wfcq_node *old_tail; + + /* + * Implicit memory barrier before uatomic_xchg() orders earlier + * stores to data structure containing node and setting + * node->next to NULL before publication. + */ + old_tail = uatomic_xchg(&tail->p, new_tail); + + /* + * Implicit memory barrier after uatomic_xchg() orders store to + * q->tail before store to old_tail->next. + * + * At this point, dequeuers see a NULL tail->p->next, which + * indicates that the queue is being appended to. The following + * store will append "node" to the queue from a dequeuer + * perspective. + */ + CMM_STORE_SHARED(old_tail->next, new_head); + /* + * Return false if queue was empty prior to adding the node, + * else return true. + */ + return old_tail != &head->node; +} + +/* + * cds_wfcq_enqueue: enqueue a node into a wait-free queue. + * + * Issues a full memory barrier before enqueue. No mutual exclusion is + * required. + * + * Returns false if the queue was empty prior to adding the node. + * Returns true otherwise. + */ +static inline bool _cds_wfcq_enqueue(cds_wfcq_head_ptr_t head, + struct cds_wfcq_tail *tail, + struct cds_wfcq_node *new_tail) +{ + return ___cds_wfcq_append(head, tail, new_tail, new_tail); +} + +/* + * CDS_WFCQ_WAIT_SLEEP: + * + * By default, this sleeps for the given @msec milliseconds. + * This is a macro which LGPL users may #define themselves before + * including wfcqueue.h to override the default behavior (e.g. + * to log a warning or perform other background work). + */ +#ifndef CDS_WFCQ_WAIT_SLEEP +#define CDS_WFCQ_WAIT_SLEEP(msec) ___cds_wfcq_wait_sleep(msec) +#endif + +static inline void ___cds_wfcq_wait_sleep(int msec) +{ + (void) poll(NULL, 0, msec); +} + +/* + * ___cds_wfcq_busy_wait: adaptative busy-wait. + * + * Returns 1 if nonblocking and needs to block, 0 otherwise. + */ +static inline bool +___cds_wfcq_busy_wait(int *attempt, int blocking) +{ + if (!blocking) + return 1; + if (++(*attempt) >= WFCQ_ADAPT_ATTEMPTS) { + CDS_WFCQ_WAIT_SLEEP(WFCQ_WAIT); /* Wait for 10ms */ + *attempt = 0; + } else { + caa_cpu_relax(); + } + return 0; +} + +/* + * Waiting for enqueuer to complete enqueue and return the next node. + */ +static inline struct cds_wfcq_node * +___cds_wfcq_node_sync_next(struct cds_wfcq_node *node, int blocking) +{ + struct cds_wfcq_node *next; + int attempt = 0; + + /* + * Adaptative busy-looping waiting for enqueuer to complete enqueue. + */ + while ((next = CMM_LOAD_SHARED(node->next)) == NULL) { + if (___cds_wfcq_busy_wait(&attempt, blocking)) + return CDS_WFCQ_WOULDBLOCK; + } + + return next; +} + +static inline struct cds_wfcq_node * +___cds_wfcq_first(cds_wfcq_head_ptr_t u_head, + struct cds_wfcq_tail *tail, + int blocking) +{ + struct __cds_wfcq_head *head = u_head._h; + struct cds_wfcq_node *node; + + if (_cds_wfcq_empty(__cds_wfcq_head_cast(head), tail)) + return NULL; + node = ___cds_wfcq_node_sync_next(&head->node, blocking); + /* Load head->node.next before loading node's content */ + cmm_smp_read_barrier_depends(); + return node; +} + +/* + * __cds_wfcq_first_blocking: get first node of a queue, without dequeuing. + * + * Content written into the node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + * Dequeue/splice/iteration mutual exclusion should be ensured by the + * caller. + * + * Used by for-like iteration macros in urcu/wfqueue.h: + * __cds_wfcq_for_each_blocking() + * __cds_wfcq_for_each_blocking_safe() + * + * Returns NULL if queue is empty, first node otherwise. + */ +static inline struct cds_wfcq_node * +___cds_wfcq_first_blocking(cds_wfcq_head_ptr_t head, + struct cds_wfcq_tail *tail) +{ + return ___cds_wfcq_first(head, tail, 1); +} + + +/* + * __cds_wfcq_first_nonblocking: get first node of a queue, without dequeuing. + * + * Same as __cds_wfcq_first_blocking, but returns CDS_WFCQ_WOULDBLOCK if + * it needs to block. + */ +static inline struct cds_wfcq_node * +___cds_wfcq_first_nonblocking(cds_wfcq_head_ptr_t head, + struct cds_wfcq_tail *tail) +{ + return ___cds_wfcq_first(head, tail, 0); +} + +static inline struct cds_wfcq_node * +___cds_wfcq_next(cds_wfcq_head_ptr_t head, + struct cds_wfcq_tail *tail, + struct cds_wfcq_node *node, + int blocking) +{ + struct cds_wfcq_node *next; + + /* + * Even though the following tail->p check is sufficient to find + * out if we reached the end of the queue, we first check + * node->next as a common case to ensure that iteration on nodes + * do not frequently access enqueuer's tail->p cache line. + */ + if ((next = CMM_LOAD_SHARED(node->next)) == NULL) { + /* Load node->next before tail->p */ + cmm_smp_rmb(); + if (CMM_LOAD_SHARED(tail->p) == node) + return NULL; + next = ___cds_wfcq_node_sync_next(node, blocking); + } + /* Load node->next before loading next's content */ + cmm_smp_read_barrier_depends(); + return next; +} + +/* + * __cds_wfcq_next_blocking: get next node of a queue, without dequeuing. + * + * Content written into the node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + * Dequeue/splice/iteration mutual exclusion should be ensured by the + * caller. + * + * Used by for-like iteration macros in urcu/wfqueue.h: + * __cds_wfcq_for_each_blocking() + * __cds_wfcq_for_each_blocking_safe() + * + * Returns NULL if reached end of queue, non-NULL next queue node + * otherwise. + */ +static inline struct cds_wfcq_node * +___cds_wfcq_next_blocking(cds_wfcq_head_ptr_t head, + struct cds_wfcq_tail *tail, + struct cds_wfcq_node *node) +{ + return ___cds_wfcq_next(head, tail, node, 1); +} + +/* + * __cds_wfcq_next_blocking: get next node of a queue, without dequeuing. + * + * Same as __cds_wfcq_next_blocking, but returns CDS_WFCQ_WOULDBLOCK if + * it needs to block. + */ +static inline struct cds_wfcq_node * +___cds_wfcq_next_nonblocking(cds_wfcq_head_ptr_t head, + struct cds_wfcq_tail *tail, + struct cds_wfcq_node *node) +{ + return ___cds_wfcq_next(head, tail, node, 0); +} + +static inline struct cds_wfcq_node * +___cds_wfcq_dequeue_with_state(cds_wfcq_head_ptr_t u_head, + struct cds_wfcq_tail *tail, + int *state, + int blocking) +{ + struct __cds_wfcq_head *head = u_head._h; + struct cds_wfcq_node *node, *next; + + if (state) + *state = 0; + + if (_cds_wfcq_empty(__cds_wfcq_head_cast(head), tail)) { + return NULL; + } + + node = ___cds_wfcq_node_sync_next(&head->node, blocking); + if (!blocking && node == CDS_WFCQ_WOULDBLOCK) { + return CDS_WFCQ_WOULDBLOCK; + } + + if ((next = CMM_LOAD_SHARED(node->next)) == NULL) { + /* + * @node is probably the only node in the queue. + * Try to move the tail to &q->head. + * q->head.next is set to NULL here, and stays + * NULL if the cmpxchg succeeds. Should the + * cmpxchg fail due to a concurrent enqueue, the + * q->head.next will be set to the next node. + * The implicit memory barrier before + * uatomic_cmpxchg() orders load node->next + * before loading q->tail. + * The implicit memory barrier before uatomic_cmpxchg + * orders load q->head.next before loading node's + * content. + */ + _cds_wfcq_node_init(&head->node); + if (uatomic_cmpxchg(&tail->p, node, &head->node) == node) { + if (state) + *state |= CDS_WFCQ_STATE_LAST; + return node; + } + next = ___cds_wfcq_node_sync_next(node, blocking); + /* + * In nonblocking mode, if we would need to block to + * get node's next, set the head next node pointer + * (currently NULL) back to its original value. + */ + if (!blocking && next == CDS_WFCQ_WOULDBLOCK) { + head->node.next = node; + return CDS_WFCQ_WOULDBLOCK; + } + } + + /* + * Move queue head forward. + */ + head->node.next = next; + + /* Load q->head.next before loading node's content */ + cmm_smp_read_barrier_depends(); + return node; +} + +/* + * __cds_wfcq_dequeue_with_state_blocking: dequeue node from queue, with state. + * + * Content written into the node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + * It is valid to reuse and free a dequeued node immediately. + * Dequeue/splice/iteration mutual exclusion should be ensured by the + * caller. + */ +static inline struct cds_wfcq_node * +___cds_wfcq_dequeue_with_state_blocking(cds_wfcq_head_ptr_t head, + struct cds_wfcq_tail *tail, int *state) +{ + return ___cds_wfcq_dequeue_with_state(head, tail, state, 1); +} + +/* + * ___cds_wfcq_dequeue_blocking: dequeue node from queue. + * + * Same as __cds_wfcq_dequeue_with_state_blocking, but without saving + * state. + */ +static inline struct cds_wfcq_node * +___cds_wfcq_dequeue_blocking(cds_wfcq_head_ptr_t head, + struct cds_wfcq_tail *tail) +{ + return ___cds_wfcq_dequeue_with_state_blocking(head, tail, NULL); +} + +/* + * __cds_wfcq_dequeue_with_state_nonblocking: dequeue node, with state. + * + * Same as __cds_wfcq_dequeue_blocking, but returns CDS_WFCQ_WOULDBLOCK + * if it needs to block. + */ +static inline struct cds_wfcq_node * +___cds_wfcq_dequeue_with_state_nonblocking(cds_wfcq_head_ptr_t head, + struct cds_wfcq_tail *tail, int *state) +{ + return ___cds_wfcq_dequeue_with_state(head, tail, state, 0); +} + +/* + * ___cds_wfcq_dequeue_nonblocking: dequeue node from queue. + * + * Same as __cds_wfcq_dequeue_with_state_nonblocking, but without saving + * state. + */ +static inline struct cds_wfcq_node * +___cds_wfcq_dequeue_nonblocking(cds_wfcq_head_ptr_t head, + struct cds_wfcq_tail *tail) +{ + return ___cds_wfcq_dequeue_with_state_nonblocking(head, tail, NULL); +} + +/* + * __cds_wfcq_splice: enqueue all src_q nodes at the end of dest_q. + * + * Dequeue all nodes from src_q. + * dest_q must be already initialized. + * Mutual exclusion for src_q should be ensured by the caller as + * specified in the "Synchronisation table". + * Returns enum cds_wfcq_ret which indicates the state of the src or + * dest queue. + */ +static inline enum cds_wfcq_ret +___cds_wfcq_splice( + cds_wfcq_head_ptr_t u_dest_q_head, + struct cds_wfcq_tail *dest_q_tail, + cds_wfcq_head_ptr_t u_src_q_head, + struct cds_wfcq_tail *src_q_tail, + int blocking) +{ + struct __cds_wfcq_head *dest_q_head = u_dest_q_head._h; + struct __cds_wfcq_head *src_q_head = u_src_q_head._h; + struct cds_wfcq_node *head, *tail; + int attempt = 0; + + /* + * Initial emptiness check to speed up cases where queue is + * empty: only require loads to check if queue is empty. + */ + if (_cds_wfcq_empty(__cds_wfcq_head_cast(src_q_head), src_q_tail)) + return CDS_WFCQ_RET_SRC_EMPTY; + + for (;;) { + /* + * Open-coded _cds_wfcq_empty() by testing result of + * uatomic_xchg, as well as tail pointer vs head node + * address. + */ + head = uatomic_xchg(&src_q_head->node.next, NULL); + if (head) + break; /* non-empty */ + if (CMM_LOAD_SHARED(src_q_tail->p) == &src_q_head->node) + return CDS_WFCQ_RET_SRC_EMPTY; + if (___cds_wfcq_busy_wait(&attempt, blocking)) + return CDS_WFCQ_RET_WOULDBLOCK; + } + + /* + * Memory barrier implied before uatomic_xchg() orders store to + * src_q->head before store to src_q->tail. This is required by + * concurrent enqueue on src_q, which exchanges the tail before + * updating the previous tail's next pointer. + */ + tail = uatomic_xchg(&src_q_tail->p, &src_q_head->node); + + /* + * Append the spliced content of src_q into dest_q. Does not + * require mutual exclusion on dest_q (wait-free). + */ + if (___cds_wfcq_append(__cds_wfcq_head_cast(dest_q_head), dest_q_tail, + head, tail)) + return CDS_WFCQ_RET_DEST_NON_EMPTY; + else + return CDS_WFCQ_RET_DEST_EMPTY; +} + +/* + * __cds_wfcq_splice_blocking: enqueue all src_q nodes at the end of dest_q. + * + * Dequeue all nodes from src_q. + * dest_q must be already initialized. + * Mutual exclusion for src_q should be ensured by the caller as + * specified in the "Synchronisation table". + * Returns enum cds_wfcq_ret which indicates the state of the src or + * dest queue. Never returns CDS_WFCQ_RET_WOULDBLOCK. + */ +static inline enum cds_wfcq_ret +___cds_wfcq_splice_blocking( + cds_wfcq_head_ptr_t dest_q_head, + struct cds_wfcq_tail *dest_q_tail, + cds_wfcq_head_ptr_t src_q_head, + struct cds_wfcq_tail *src_q_tail) +{ + return ___cds_wfcq_splice(dest_q_head, dest_q_tail, + src_q_head, src_q_tail, 1); +} + +/* + * __cds_wfcq_splice_nonblocking: enqueue all src_q nodes at the end of dest_q. + * + * Same as __cds_wfcq_splice_blocking, but returns + * CDS_WFCQ_RET_WOULDBLOCK if it needs to block. + */ +static inline enum cds_wfcq_ret +___cds_wfcq_splice_nonblocking( + cds_wfcq_head_ptr_t dest_q_head, + struct cds_wfcq_tail *dest_q_tail, + cds_wfcq_head_ptr_t src_q_head, + struct cds_wfcq_tail *src_q_tail) +{ + return ___cds_wfcq_splice(dest_q_head, dest_q_tail, + src_q_head, src_q_tail, 0); +} + +/* + * cds_wfcq_dequeue_with_state_blocking: dequeue a node from a wait-free queue. + * + * Content written into the node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + * Mutual exclusion with cds_wfcq_splice_blocking and dequeue lock is + * ensured. + * It is valid to reuse and free a dequeued node immediately. + */ +static inline struct cds_wfcq_node * +_cds_wfcq_dequeue_with_state_blocking(struct cds_wfcq_head *head, + struct cds_wfcq_tail *tail, int *state) +{ + struct cds_wfcq_node *retval; + + _cds_wfcq_dequeue_lock(head, tail); + retval = ___cds_wfcq_dequeue_with_state_blocking(cds_wfcq_head_cast(head), + tail, state); + _cds_wfcq_dequeue_unlock(head, tail); + return retval; +} + +/* + * cds_wfcq_dequeue_blocking: dequeue node from queue. + * + * Same as cds_wfcq_dequeue_blocking, but without saving state. + */ +static inline struct cds_wfcq_node * +_cds_wfcq_dequeue_blocking(struct cds_wfcq_head *head, + struct cds_wfcq_tail *tail) +{ + return _cds_wfcq_dequeue_with_state_blocking(head, tail, NULL); +} + +/* + * cds_wfcq_splice_blocking: enqueue all src_q nodes at the end of dest_q. + * + * Dequeue all nodes from src_q. + * dest_q must be already initialized. + * Content written into the node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + * Mutual exclusion with cds_wfcq_dequeue_blocking and dequeue lock is + * ensured. + * Returns enum cds_wfcq_ret which indicates the state of the src or + * dest queue. Never returns CDS_WFCQ_RET_WOULDBLOCK. + */ +static inline enum cds_wfcq_ret +_cds_wfcq_splice_blocking( + struct cds_wfcq_head *dest_q_head, + struct cds_wfcq_tail *dest_q_tail, + struct cds_wfcq_head *src_q_head, + struct cds_wfcq_tail *src_q_tail) +{ + enum cds_wfcq_ret ret; + + _cds_wfcq_dequeue_lock(src_q_head, src_q_tail); + ret = ___cds_wfcq_splice_blocking(cds_wfcq_head_cast(dest_q_head), dest_q_tail, + cds_wfcq_head_cast(src_q_head), src_q_tail); + _cds_wfcq_dequeue_unlock(src_q_head, src_q_tail); + return ret; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _URCU_WFCQUEUE_STATIC_H */ diff --git a/contrib/userspace-rcu/static-wfstack.h b/contrib/userspace-rcu/static-wfstack.h new file mode 100644 index 00000000000..29b81c3aac3 --- /dev/null +++ b/contrib/userspace-rcu/static-wfstack.h @@ -0,0 +1,455 @@ +#ifndef _URCU_STATIC_WFSTACK_H +#define _URCU_STATIC_WFSTACK_H + +/* + * urcu/static/wfstack.h + * + * Userspace RCU library - Stack with with wait-free push, blocking traversal. + * + * TO BE INCLUDED ONLY IN LGPL-COMPATIBLE CODE. See urcu/wfstack.h for + * linking dynamically with the userspace rcu library. + * + * Copyright 2010-2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Adapted from userspace-rcu 0.10 because version 0.7 doesn't support a stack + * without mutex. */ + +#include <pthread.h> +#include <assert.h> +#include <poll.h> +#include <stdbool.h> +#include <urcu/compiler.h> +#include <urcu/uatomic.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define CDS_WFS_END ((void *) 0x1UL) +#define CDS_WFS_ADAPT_ATTEMPTS 10 /* Retry if being set */ +#define CDS_WFS_WAIT 10 /* Wait 10 ms if being set */ + +/* + * Stack with wait-free push, blocking traversal. + * + * Stack implementing push, pop, pop_all operations, as well as iterator + * on the stack head returned by pop_all. + * + * Wait-free operations: cds_wfs_push, __cds_wfs_pop_all, cds_wfs_empty, + * cds_wfs_first. + * Blocking operations: cds_wfs_pop, cds_wfs_pop_all, cds_wfs_next, + * iteration on stack head returned by pop_all. + * + * Synchronization table: + * + * External synchronization techniques described in the API below is + * required between pairs marked with "X". No external synchronization + * required between pairs marked with "-". + * + * cds_wfs_push __cds_wfs_pop __cds_wfs_pop_all + * cds_wfs_push - - - + * __cds_wfs_pop - X X + * __cds_wfs_pop_all - X - + * + * cds_wfs_pop and cds_wfs_pop_all use an internal mutex to provide + * synchronization. + */ + +/* + * cds_wfs_node_init: initialize wait-free stack node. + */ +static inline +void _cds_wfs_node_init(struct cds_wfs_node *node) +{ + node->next = NULL; +} + +/* + * __cds_wfs_init: initialize wait-free stack. Don't pair with + * any destroy function. + */ +static inline void ___cds_wfs_init(struct __cds_wfs_stack *s) +{ + s->head = CDS_WFS_END; +} + +/* + * cds_wfs_init: initialize wait-free stack. Pair with + * cds_wfs_destroy(). + */ +static inline +void _cds_wfs_init(struct cds_wfs_stack *s) +{ + int ret; + + s->head = CDS_WFS_END; + ret = pthread_mutex_init(&s->lock, NULL); + assert(!ret); +} + +/* + * cds_wfs_destroy: destroy wait-free stack. Pair with + * cds_wfs_init(). + */ +static inline +void _cds_wfs_destroy(struct cds_wfs_stack *s) +{ + int ret = pthread_mutex_destroy(&s->lock); + assert(!ret); +} + +static inline bool ___cds_wfs_end(void *node) +{ + return node == CDS_WFS_END; +} + +/* + * cds_wfs_empty: return whether wait-free stack is empty. + * + * No memory barrier is issued. No mutual exclusion is required. + */ +static inline bool _cds_wfs_empty(cds_wfs_stack_ptr_t u_stack) +{ + struct __cds_wfs_stack *s = u_stack._s; + + return ___cds_wfs_end(CMM_LOAD_SHARED(s->head)); +} + +/* + * cds_wfs_push: push a node into the stack. + * + * Issues a full memory barrier before push. No mutual exclusion is + * required. + * + * Returns 0 if the stack was empty prior to adding the node. + * Returns non-zero otherwise. + */ +static inline +int _cds_wfs_push(cds_wfs_stack_ptr_t u_stack, struct cds_wfs_node *node) +{ + struct __cds_wfs_stack *s = u_stack._s; + struct cds_wfs_head *old_head, *new_head; + + assert(node->next == NULL); + new_head = caa_container_of(node, struct cds_wfs_head, node); + /* + * uatomic_xchg() implicit memory barrier orders earlier stores + * to node (setting it to NULL) before publication. + */ + old_head = uatomic_xchg(&s->head, new_head); + /* + * At this point, dequeuers see a NULL node->next, they should + * busy-wait until node->next is set to old_head. + */ + CMM_STORE_SHARED(node->next, &old_head->node); + return !___cds_wfs_end(old_head); +} + +/* + * Waiting for push to complete enqueue and return the next node. + */ +static inline struct cds_wfs_node * +___cds_wfs_node_sync_next(struct cds_wfs_node *node, int blocking) +{ + struct cds_wfs_node *next; + int attempt = 0; + + /* + * Adaptative busy-looping waiting for push to complete. + */ + while ((next = CMM_LOAD_SHARED(node->next)) == NULL) { + if (!blocking) + return CDS_WFS_WOULDBLOCK; + if (++attempt >= CDS_WFS_ADAPT_ATTEMPTS) { + (void) poll(NULL, 0, CDS_WFS_WAIT); /* Wait for 10ms */ + attempt = 0; + } else { + caa_cpu_relax(); + } + } + + return next; +} + +static inline +struct cds_wfs_node * +___cds_wfs_pop(cds_wfs_stack_ptr_t u_stack, int *state, int blocking) +{ + struct cds_wfs_head *head, *new_head; + struct cds_wfs_node *next; + struct __cds_wfs_stack *s = u_stack._s; + + if (state) + *state = 0; + for (;;) { + head = CMM_LOAD_SHARED(s->head); + if (___cds_wfs_end(head)) { + return NULL; + } + next = ___cds_wfs_node_sync_next(&head->node, blocking); + if (!blocking && next == CDS_WFS_WOULDBLOCK) { + return CDS_WFS_WOULDBLOCK; + } + new_head = caa_container_of(next, struct cds_wfs_head, node); + if (uatomic_cmpxchg(&s->head, head, new_head) == head) { + if (state && ___cds_wfs_end(new_head)) + *state |= CDS_WFS_STATE_LAST; + return &head->node; + } + if (!blocking) { + return CDS_WFS_WOULDBLOCK; + } + /* busy-loop if head changed under us */ + } +} + +/* + * __cds_wfs_pop_with_state_blocking: pop a node from the stack, with state. + * + * Returns NULL if stack is empty. + * + * __cds_wfs_pop_blocking needs to be synchronized using one of the + * following techniques: + * + * 1) Calling __cds_wfs_pop_blocking under rcu read lock critical + * section. The caller must wait for a grace period to pass before + * freeing the returned node or modifying the cds_wfs_node structure. + * 2) Using mutual exclusion (e.g. mutexes) to protect + * __cds_wfs_pop_blocking and __cds_wfs_pop_all callers. + * 3) Ensuring that only ONE thread can call __cds_wfs_pop_blocking() + * and __cds_wfs_pop_all(). (multi-provider/single-consumer scheme). + * + * "state" saves state flags atomically sampled with pop operation. + */ +static inline +struct cds_wfs_node * +___cds_wfs_pop_with_state_blocking(cds_wfs_stack_ptr_t u_stack, int *state) +{ + return ___cds_wfs_pop(u_stack, state, 1); +} + +static inline +struct cds_wfs_node * +___cds_wfs_pop_blocking(cds_wfs_stack_ptr_t u_stack) +{ + return ___cds_wfs_pop_with_state_blocking(u_stack, NULL); +} + +/* + * __cds_wfs_pop_with_state_nonblocking: pop a node from the stack. + * + * Same as __cds_wfs_pop_with_state_blocking, but returns + * CDS_WFS_WOULDBLOCK if it needs to block. + * + * "state" saves state flags atomically sampled with pop operation. + */ +static inline +struct cds_wfs_node * +___cds_wfs_pop_with_state_nonblocking(cds_wfs_stack_ptr_t u_stack, int *state) +{ + return ___cds_wfs_pop(u_stack, state, 0); +} + +/* + * __cds_wfs_pop_nonblocking: pop a node from the stack. + * + * Same as __cds_wfs_pop_blocking, but returns CDS_WFS_WOULDBLOCK if + * it needs to block. + */ +static inline +struct cds_wfs_node * +___cds_wfs_pop_nonblocking(cds_wfs_stack_ptr_t u_stack) +{ + return ___cds_wfs_pop_with_state_nonblocking(u_stack, NULL); +} + +/* + * __cds_wfs_pop_all: pop all nodes from a stack. + * + * __cds_wfs_pop_all does not require any synchronization with other + * push, nor with other __cds_wfs_pop_all, but requires synchronization + * matching the technique used to synchronize __cds_wfs_pop_blocking: + * + * 1) If __cds_wfs_pop_blocking is called under rcu read lock critical + * section, both __cds_wfs_pop_blocking and cds_wfs_pop_all callers + * must wait for a grace period to pass before freeing the returned + * node or modifying the cds_wfs_node structure. However, no RCU + * read-side critical section is needed around __cds_wfs_pop_all. + * 2) Using mutual exclusion (e.g. mutexes) to protect + * __cds_wfs_pop_blocking and __cds_wfs_pop_all callers. + * 3) Ensuring that only ONE thread can call __cds_wfs_pop_blocking() + * and __cds_wfs_pop_all(). (multi-provider/single-consumer scheme). + */ +static inline +struct cds_wfs_head * +___cds_wfs_pop_all(cds_wfs_stack_ptr_t u_stack) +{ + struct __cds_wfs_stack *s = u_stack._s; + struct cds_wfs_head *head; + + /* + * Implicit memory barrier after uatomic_xchg() matches implicit + * memory barrier before uatomic_xchg() in cds_wfs_push. It + * ensures that all nodes of the returned list are consistent. + * There is no need to issue memory barriers when iterating on + * the returned list, because the full memory barrier issued + * prior to each uatomic_cmpxchg, which each write to head, are + * taking care to order writes to each node prior to the full + * memory barrier after this uatomic_xchg(). + */ + head = uatomic_xchg(&s->head, CDS_WFS_END); + if (___cds_wfs_end(head)) + return NULL; + return head; +} + +/* + * cds_wfs_pop_lock: lock stack pop-protection mutex. + */ +static inline void _cds_wfs_pop_lock(struct cds_wfs_stack *s) +{ + int ret; + + ret = pthread_mutex_lock(&s->lock); + assert(!ret); +} + +/* + * cds_wfs_pop_unlock: unlock stack pop-protection mutex. + */ +static inline void _cds_wfs_pop_unlock(struct cds_wfs_stack *s) +{ + int ret; + + ret = pthread_mutex_unlock(&s->lock); + assert(!ret); +} + +/* + * Call __cds_wfs_pop_with_state_blocking with an internal pop mutex held. + */ +static inline +struct cds_wfs_node * +_cds_wfs_pop_with_state_blocking(struct cds_wfs_stack *s, int *state) +{ + struct cds_wfs_node *retnode; + + _cds_wfs_pop_lock(s); + retnode = ___cds_wfs_pop_with_state_blocking(s, state); + _cds_wfs_pop_unlock(s); + return retnode; +} + +/* + * Call _cds_wfs_pop_with_state_blocking without saving any state. + */ +static inline +struct cds_wfs_node * +_cds_wfs_pop_blocking(struct cds_wfs_stack *s) +{ + return _cds_wfs_pop_with_state_blocking(s, NULL); +} + +/* + * Call __cds_wfs_pop_all with an internal pop mutex held. + */ +static inline +struct cds_wfs_head * +_cds_wfs_pop_all_blocking(struct cds_wfs_stack *s) +{ + struct cds_wfs_head *rethead; + + _cds_wfs_pop_lock(s); + rethead = ___cds_wfs_pop_all(s); + _cds_wfs_pop_unlock(s); + return rethead; +} + +/* + * cds_wfs_first: get first node of a popped stack. + * + * Content written into the node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + * + * Used by for-like iteration macros in urcu/wfstack.h: + * cds_wfs_for_each_blocking() + * cds_wfs_for_each_blocking_safe() + * + * Returns NULL if popped stack is empty, top stack node otherwise. + */ +static inline struct cds_wfs_node * +_cds_wfs_first(struct cds_wfs_head *head) +{ + if (___cds_wfs_end(head)) + return NULL; + return &head->node; +} + +static inline struct cds_wfs_node * +___cds_wfs_next(struct cds_wfs_node *node, int blocking) +{ + struct cds_wfs_node *next; + + next = ___cds_wfs_node_sync_next(node, blocking); + /* + * CDS_WFS_WOULDBLOCK != CSD_WFS_END, so we can check for end + * even if ___cds_wfs_node_sync_next returns CDS_WFS_WOULDBLOCK, + * and still return CDS_WFS_WOULDBLOCK. + */ + if (___cds_wfs_end(next)) + return NULL; + return next; +} + +/* + * cds_wfs_next_blocking: get next node of a popped stack. + * + * Content written into the node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + * + * Used by for-like iteration macros in urcu/wfstack.h: + * cds_wfs_for_each_blocking() + * cds_wfs_for_each_blocking_safe() + * + * Returns NULL if reached end of popped stack, non-NULL next stack + * node otherwise. + */ +static inline struct cds_wfs_node * +_cds_wfs_next_blocking(struct cds_wfs_node *node) +{ + return ___cds_wfs_next(node, 1); +} + + +/* + * cds_wfs_next_nonblocking: get next node of a popped stack. + * + * Same as cds_wfs_next_blocking, but returns CDS_WFS_WOULDBLOCK if it + * needs to block. + */ +static inline struct cds_wfs_node * +_cds_wfs_next_nonblocking(struct cds_wfs_node *node) +{ + return ___cds_wfs_next(node, 0); +} + +#ifdef __cplusplus +} +#endif + +#endif /* _URCU_STATIC_WFSTACK_H */ diff --git a/contrib/userspace-rcu/wfcqueue.h b/contrib/userspace-rcu/wfcqueue.h new file mode 100644 index 00000000000..0292585ac79 --- /dev/null +++ b/contrib/userspace-rcu/wfcqueue.h @@ -0,0 +1,216 @@ +#ifndef _URCU_WFCQUEUE_H +#define _URCU_WFCQUEUE_H + +/* + * urcu/wfcqueue.h + * + * Userspace RCU library - Concurrent Queue with Wait-Free Enqueue/Blocking Dequeue + * + * Copyright 2010-2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * Copyright 2011-2012 - Lai Jiangshan <laijs@cn.fujitsu.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Adapted from userspace-rcu 0.10 because version 0.7 doesn't contain it. + * The non-LGPL section has been removed. */ + +#include <pthread.h> +#include <assert.h> +#include <stdbool.h> +#include <urcu/compiler.h> +#include <urcu/arch.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Concurrent queue with wait-free enqueue/blocking dequeue. + * + * This queue has been designed and implemented collaboratively by + * Mathieu Desnoyers and Lai Jiangshan. Inspired from + * half-wait-free/half-blocking queue implementation done by Paul E. + * McKenney. + */ + +#define CDS_WFCQ_WOULDBLOCK ((struct cds_wfcq_node *) -1UL) + +enum cds_wfcq_ret { + CDS_WFCQ_RET_WOULDBLOCK = -1, + CDS_WFCQ_RET_DEST_EMPTY = 0, + CDS_WFCQ_RET_DEST_NON_EMPTY = 1, + CDS_WFCQ_RET_SRC_EMPTY = 2, +}; + +enum cds_wfcq_state { + CDS_WFCQ_STATE_LAST = (1U << 0), +}; + +struct cds_wfcq_node { + struct cds_wfcq_node *next; +}; + +/* + * Do not put head and tail on the same cache-line if concurrent + * enqueue/dequeue are expected from many CPUs. This eliminates + * false-sharing between enqueue and dequeue. + */ +struct __cds_wfcq_head { + struct cds_wfcq_node node; +}; + +struct cds_wfcq_head { + struct cds_wfcq_node node; + pthread_mutex_t lock; +}; + +#ifndef __cplusplus +/* + * The transparent union allows calling functions that work on both + * struct cds_wfcq_head and struct __cds_wfcq_head on any of those two + * types. + */ +typedef union { + struct __cds_wfcq_head *_h; + struct cds_wfcq_head *h; +} __attribute__((__transparent_union__)) cds_wfcq_head_ptr_t; + +/* + * This static inline is only present for compatibility with C++. It is + * effect-less in C. + */ +static inline struct __cds_wfcq_head *__cds_wfcq_head_cast(struct __cds_wfcq_head *head) +{ + return head; +} + +/* + * This static inline is only present for compatibility with C++. It is + * effect-less in C. + */ +static inline struct cds_wfcq_head *cds_wfcq_head_cast(struct cds_wfcq_head *head) +{ + return head; +} +#else /* #ifndef __cplusplus */ + +/* C++ ignores transparent union. */ +typedef union { + struct __cds_wfcq_head *_h; + struct cds_wfcq_head *h; +} cds_wfcq_head_ptr_t; + +/* C++ ignores transparent union. Requires an explicit conversion. */ +static inline cds_wfcq_head_ptr_t __cds_wfcq_head_cast(struct __cds_wfcq_head *head) +{ + cds_wfcq_head_ptr_t ret = { ._h = head }; + return ret; +} +/* C++ ignores transparent union. Requires an explicit conversion. */ +static inline cds_wfcq_head_ptr_t cds_wfcq_head_cast(struct cds_wfcq_head *head) +{ + cds_wfcq_head_ptr_t ret = { .h = head }; + return ret; +} +#endif /* #else #ifndef __cplusplus */ + +struct cds_wfcq_tail { + struct cds_wfcq_node *p; +}; + +#include "static-wfcqueue.h" + +#define cds_wfcq_node_init _cds_wfcq_node_init +#define cds_wfcq_init _cds_wfcq_init +#define __cds_wfcq_init ___cds_wfcq_init +#define cds_wfcq_destroy _cds_wfcq_destroy +#define cds_wfcq_empty _cds_wfcq_empty +#define cds_wfcq_enqueue _cds_wfcq_enqueue + +/* Dequeue locking */ +#define cds_wfcq_dequeue_lock _cds_wfcq_dequeue_lock +#define cds_wfcq_dequeue_unlock _cds_wfcq_dequeue_unlock + +/* Locking performed within cds_wfcq calls. */ +#define cds_wfcq_dequeue_blocking _cds_wfcq_dequeue_blocking +#define cds_wfcq_dequeue_with_state_blocking \ + _cds_wfcq_dequeue_with_state_blocking +#define cds_wfcq_splice_blocking _cds_wfcq_splice_blocking +#define cds_wfcq_first_blocking _cds_wfcq_first_blocking +#define cds_wfcq_next_blocking _cds_wfcq_next_blocking + +/* Locking ensured by caller by holding cds_wfcq_dequeue_lock() */ +#define __cds_wfcq_dequeue_blocking ___cds_wfcq_dequeue_blocking +#define __cds_wfcq_dequeue_with_state_blocking \ + ___cds_wfcq_dequeue_with_state_blocking +#define __cds_wfcq_splice_blocking ___cds_wfcq_splice_blocking +#define __cds_wfcq_first_blocking ___cds_wfcq_first_blocking +#define __cds_wfcq_next_blocking ___cds_wfcq_next_blocking + +/* + * Locking ensured by caller by holding cds_wfcq_dequeue_lock(). + * Non-blocking: deque, first, next return CDS_WFCQ_WOULDBLOCK if they + * need to block. splice returns nonzero if it needs to block. + */ +#define __cds_wfcq_dequeue_nonblocking ___cds_wfcq_dequeue_nonblocking +#define __cds_wfcq_dequeue_with_state_nonblocking \ + ___cds_wfcq_dequeue_with_state_nonblocking +#define __cds_wfcq_splice_nonblocking ___cds_wfcq_splice_nonblocking +#define __cds_wfcq_first_nonblocking ___cds_wfcq_first_nonblocking +#define __cds_wfcq_next_nonblocking ___cds_wfcq_next_nonblocking + +/* + * __cds_wfcq_for_each_blocking: Iterate over all nodes in a queue, + * without dequeuing them. + * @head: head of the queue (struct cds_wfcq_head or __cds_wfcq_head pointer). + * @tail: tail of the queue (struct cds_wfcq_tail pointer). + * @node: iterator on the queue (struct cds_wfcq_node pointer). + * + * Content written into each node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + * Dequeue/splice/iteration mutual exclusion should be ensured by the + * caller. + */ +#define __cds_wfcq_for_each_blocking(head, tail, node) \ + for (node = __cds_wfcq_first_blocking(head, tail); \ + node != NULL; \ + node = __cds_wfcq_next_blocking(head, tail, node)) + +/* + * __cds_wfcq_for_each_blocking_safe: Iterate over all nodes in a queue, + * without dequeuing them. Safe against deletion. + * @head: head of the queue (struct cds_wfcq_head or __cds_wfcq_head pointer). + * @tail: tail of the queue (struct cds_wfcq_tail pointer). + * @node: iterator on the queue (struct cds_wfcq_node pointer). + * @n: struct cds_wfcq_node pointer holding the next pointer (used + * internally). + * + * Content written into each node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + * Dequeue/splice/iteration mutual exclusion should be ensured by the + * caller. + */ +#define __cds_wfcq_for_each_blocking_safe(head, tail, node, n) \ + for (node = __cds_wfcq_first_blocking(head, tail), \ + n = (node ? __cds_wfcq_next_blocking(head, tail, node) : NULL); \ + node != NULL; \ + node = n, n = (node ? __cds_wfcq_next_blocking(head, tail, node) : NULL)) + +#ifdef __cplusplus +} +#endif + +#endif /* _URCU_WFCQUEUE_H */ diff --git a/contrib/userspace-rcu/wfstack.h b/contrib/userspace-rcu/wfstack.h new file mode 100644 index 00000000000..738fd1cfd33 --- /dev/null +++ b/contrib/userspace-rcu/wfstack.h @@ -0,0 +1,178 @@ +#ifndef _URCU_WFSTACK_H +#define _URCU_WFSTACK_H + +/* + * urcu/wfstack.h + * + * Userspace RCU library - Stack with wait-free push, blocking traversal. + * + * Copyright 2010-2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Adapted from userspace-rcu 0.10 because version 0.7 doesn't support a stack + * without mutex. The non-LGPL section has been removed. */ + +#include <pthread.h> +#include <assert.h> +#include <stdbool.h> +#include <urcu/compiler.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Stack with wait-free push, blocking traversal. + * + * Stack implementing push, pop, pop_all operations, as well as iterator + * on the stack head returned by pop_all. + * + * Wait-free operations: cds_wfs_push, __cds_wfs_pop_all, cds_wfs_empty, + * cds_wfs_first. + * Blocking operations: cds_wfs_pop, cds_wfs_pop_all, cds_wfs_next, + * iteration on stack head returned by pop_all. + * + * Synchronization table: + * + * External synchronization techniques described in the API below is + * required between pairs marked with "X". No external synchronization + * required between pairs marked with "-". + * + * cds_wfs_push __cds_wfs_pop __cds_wfs_pop_all + * cds_wfs_push - - - + * __cds_wfs_pop - X X + * __cds_wfs_pop_all - X - + * + * cds_wfs_pop and cds_wfs_pop_all use an internal mutex to provide + * synchronization. + */ + +#define CDS_WFS_WOULDBLOCK ((void *) -1UL) + +enum cds_wfs_state { + CDS_WFS_STATE_LAST = (1U << 0), +}; + +/* + * struct cds_wfs_node is returned by __cds_wfs_pop, and also used as + * iterator on stack. It is not safe to dereference the node next + * pointer when returned by __cds_wfs_pop_blocking. + */ +struct cds_wfs_node { + struct cds_wfs_node *next; +}; + +/* + * struct cds_wfs_head is returned by __cds_wfs_pop_all, and can be used + * to begin iteration on the stack. "node" needs to be the first field of + * cds_wfs_head, so the end-of-stack pointer value can be used for both + * types. + */ +struct cds_wfs_head { + struct cds_wfs_node node; +}; + +struct __cds_wfs_stack { + struct cds_wfs_head *head; +}; + +struct cds_wfs_stack { + struct cds_wfs_head *head; + pthread_mutex_t lock; +}; + +/* + * The transparent union allows calling functions that work on both + * struct cds_wfs_stack and struct __cds_wfs_stack on any of those two + * types. + */ +typedef union { + struct __cds_wfs_stack *_s; + struct cds_wfs_stack *s; +} __attribute__((__transparent_union__)) cds_wfs_stack_ptr_t; + +#include "static-wfstack.h" + +#define cds_wfs_node_init _cds_wfs_node_init +#define cds_wfs_init _cds_wfs_init +#define cds_wfs_destroy _cds_wfs_destroy +#define __cds_wfs_init ___cds_wfs_init +#define cds_wfs_empty _cds_wfs_empty +#define cds_wfs_push _cds_wfs_push + +/* Locking performed internally */ +#define cds_wfs_pop_blocking _cds_wfs_pop_blocking +#define cds_wfs_pop_with_state_blocking _cds_wfs_pop_with_state_blocking +#define cds_wfs_pop_all_blocking _cds_wfs_pop_all_blocking + +/* + * For iteration on cds_wfs_head returned by __cds_wfs_pop_all or + * cds_wfs_pop_all_blocking. + */ +#define cds_wfs_first _cds_wfs_first +#define cds_wfs_next_blocking _cds_wfs_next_blocking +#define cds_wfs_next_nonblocking _cds_wfs_next_nonblocking + +/* Pop locking with internal mutex */ +#define cds_wfs_pop_lock _cds_wfs_pop_lock +#define cds_wfs_pop_unlock _cds_wfs_pop_unlock + +/* Synchronization ensured by the caller. See synchronization table. */ +#define __cds_wfs_pop_blocking ___cds_wfs_pop_blocking +#define __cds_wfs_pop_with_state_blocking \ + ___cds_wfs_pop_with_state_blocking +#define __cds_wfs_pop_nonblocking ___cds_wfs_pop_nonblocking +#define __cds_wfs_pop_with_state_nonblocking \ + ___cds_wfs_pop_with_state_nonblocking +#define __cds_wfs_pop_all ___cds_wfs_pop_all + +#ifdef __cplusplus +} +#endif + +/* + * cds_wfs_for_each_blocking: Iterate over all nodes returned by + * __cds_wfs_pop_all(). + * @head: head of the queue (struct cds_wfs_head pointer). + * @node: iterator (struct cds_wfs_node pointer). + * + * Content written into each node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + */ +#define cds_wfs_for_each_blocking(head, node) \ + for (node = cds_wfs_first(head); \ + node != NULL; \ + node = cds_wfs_next_blocking(node)) + +/* + * cds_wfs_for_each_blocking_safe: Iterate over all nodes returned by + * __cds_wfs_pop_all(). Safe against deletion. + * @head: head of the queue (struct cds_wfs_head pointer). + * @node: iterator (struct cds_wfs_node pointer). + * @n: struct cds_wfs_node pointer holding the next pointer (used + * internally). + * + * Content written into each node before enqueue is guaranteed to be + * consistent, but no other memory ordering is ensured. + */ +#define cds_wfs_for_each_blocking_safe(head, node, n) \ + for (node = cds_wfs_first(head), \ + n = (node ? cds_wfs_next_blocking(node) : NULL); \ + node != NULL; \ + node = n, n = (node ? cds_wfs_next_blocking(node) : NULL)) + +#endif /* _URCU_WFSTACK_H */ diff --git a/contrib/xxhash/xxhash.c b/contrib/xxhash/xxhash.c new file mode 100644 index 00000000000..56f80f8811d --- /dev/null +++ b/contrib/xxhash/xxhash.c @@ -0,0 +1,1029 @@ +/* +* xxHash - Fast Hash algorithm +* Copyright (C) 2012-2016, Yann Collet +* +* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* You can contact the author at : +* - xxHash homepage: http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + + +/* ************************************* +* Tuning parameters +***************************************/ +/*!XXH_FORCE_MEMORY_ACCESS : + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. + * It can generate buggy code on targets which do not support unaligned memory accesses. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See http://stackoverflow.com/a/32095106/646947 for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ +# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define XXH_FORCE_MEMORY_ACCESS 2 +# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ + (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7S__) )) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/*!XXH_ACCEPT_NULL_INPUT_POINTER : + * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault. + * When this macro is enabled, xxHash actively checks input for null pointer. + * It it is, result for null input pointers is the same as a null-length input. + */ +#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ +# define XXH_ACCEPT_NULL_INPUT_POINTER 0 +#endif + +/*!XXH_FORCE_NATIVE_FORMAT : + * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. + * Results are therefore identical for little-endian and big-endian CPU. + * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. + * Should endian-independence be of no importance for your application, you may set the #define below to 1, + * to improve speed for Big-endian CPU. + * This option has no impact on Little_Endian CPU. + */ +#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */ +# define XXH_FORCE_NATIVE_FORMAT 0 +#endif + +/*!XXH_FORCE_ALIGN_CHECK : + * This is a minor performance trick, only useful with lots of very small keys. + * It means : check for aligned/unaligned input. + * The check costs one initial branch per hash; + * set it to 0 when the input is guaranteed to be aligned, + * or when alignment doesn't matter for performance. + */ +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/*! Modify the local functions below should you wish to use some other memory routines +* for malloc(), free() */ +#include <stdlib.h> +static void* XXH_malloc(size_t s) { return malloc(s); } +static void XXH_free (void* p) { free(p); } +/*! and for memcpy() */ +#include <string.h> +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + +#include <assert.h> /* assert */ + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# define FORCE_INLINE static __forceinline +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif +# else +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +#endif + + +/* ************************************* +* Basic Types +***************************************/ +#ifndef MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include <stdint.h> + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; +# else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; +# endif +#endif + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; } __attribute__((packed)) unalign; +static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ +static U32 XXH_read32(const void* memPtr) +{ + U32 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) +#endif + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static U32 XXH_swap32 (U32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* ************************************* +* Architecture Macros +***************************************/ +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; + +/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ +#ifndef XXH_CPU_LITTLE_ENDIAN +static int XXH_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} +# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() +#endif + + +/* *************************** +* Memory reads +*****************************/ +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); + else + return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); +} + +FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE32_align(ptr, endian, XXH_unaligned); +} + +static U32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} + + +/* ************************************* +* Macros +***************************************/ +#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } /* use after variable declarations */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +static const U32 PRIME32_1 = 2654435761U; +static const U32 PRIME32_2 = 2246822519U; +static const U32 PRIME32_3 = 3266489917U; +static const U32 PRIME32_4 = 668265263U; +static const U32 PRIME32_5 = 374761393U; + +static U32 XXH32_round(U32 seed, U32 input) +{ + seed += input * PRIME32_2; + seed = XXH_rotl32(seed, 13); + seed *= PRIME32_1; + return seed; +} + +/* mix all bits */ +static U32 XXH32_avalanche(U32 h32) +{ + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + return(h32); +} + +#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) + +static U32 +XXH32_finalize(U32 h32, const void* ptr, size_t len, + XXH_endianess endian, XXH_alignment align) + +{ + const BYTE* p = (const BYTE*)ptr; +#define PROCESS1 \ + h32 += (*p) * PRIME32_5; \ + p++; \ + h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; + +#define PROCESS4 \ + h32 += XXH_get32bits(p) * PRIME32_3; \ + p+=4; \ + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + + switch(len&15) /* or switch(bEnd - p) */ + { + case 12: PROCESS4; + /* fallthrough */ + case 8: PROCESS4; + /* fallthrough */ + case 4: PROCESS4; + return XXH32_avalanche(h32); + + case 13: PROCESS4; + /* fallthrough */ + case 9: PROCESS4; + /* fallthrough */ + case 5: PROCESS4; + PROCESS1; + return XXH32_avalanche(h32); + + case 14: PROCESS4; + /* fallthrough */ + case 10: PROCESS4; + /* fallthrough */ + case 6: PROCESS4; + PROCESS1; + PROCESS1; + return XXH32_avalanche(h32); + + case 15: PROCESS4; + /* fallthrough */ + case 11: PROCESS4; + /* fallthrough */ + case 7: PROCESS4; + /* fallthrough */ + case 3: PROCESS1; + /* fallthrough */ + case 2: PROCESS1; + /* fallthrough */ + case 1: PROCESS1; + /* fallthrough */ + case 0: return XXH32_avalanche(h32); + } + assert(0); + return h32; /* reaching this point is deemed impossible */ +} + + +FORCE_INLINE U32 +XXH32_endian_align(const void* input, size_t len, U32 seed, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U32 h32; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)16; + } +#endif + + if (len>=16) { + const BYTE* const limit = bEnd - 15; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; + v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; + v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; + v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; + } while (p < limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + PRIME32_5; + } + + h32 += (U32)len; + + return XXH32_finalize(h32, p, len&15, endian, align); +} + + +XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, input, len); + return XXH32_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + + + +/*====== Hash streaming ======*/ + +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) +{ + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME32_1 + PRIME32_2; + state.v2 = seed + PRIME32_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME32_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + + +FORCE_INLINE +XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + state->total_len_32 += (unsigned)len; + state->large_len |= (len>=16) | (state->total_len_32>=16); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); + state->memsize += (unsigned)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const U32* p32 = state->mem32; + state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++; + state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++; + state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++; + state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do { + v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4; + v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4; + v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4; + v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + + return XXH_OK; +} + + +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH32_update_endian(state_in, input, len, XXH_bigEndian); +} + + +FORCE_INLINE U32 +XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) +{ + U32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v1, 1) + + XXH_rotl32(state->v2, 7) + + XXH_rotl32(state->v3, 12) + + XXH_rotl32(state->v4, 18); + } else { + h32 = state->v3 /* == seed */ + PRIME32_5; + } + + h32 += state->total_len_32; + + return XXH32_finalize(h32, state->mem32, state->memsize, endian, XXH_aligned); +} + + +XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_digest_endian(state_in, XXH_littleEndian); + else + return XXH32_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +/*! Default XXH result types are basic unsigned 32 and 64 bits. +* The canonical representation follows human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file or buffer, remaining comparable across different systems. +*/ + +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ + +/*====== Memory access ======*/ + +#ifndef MEM_MODULE +# define MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include <stdint.h> + typedef uint64_t U64; +# else + /* if compiler doesn't support unsigned long long, replace by another 64-bit type */ + typedef unsigned long long U64; +# endif +#endif + + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64; +static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + +static U64 XXH_read64(const void* memPtr) +{ + U64 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static U64 XXH_swap64 (U64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + +FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); + else + return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); +} + +FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE64_align(ptr, endian, XXH_unaligned); +} + +static U64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} + + +/*====== xxh64 ======*/ + +static const U64 PRIME64_1 = 11400714785074694791ULL; +static const U64 PRIME64_2 = 14029467366897019727ULL; +static const U64 PRIME64_3 = 1609587929392839161ULL; +static const U64 PRIME64_4 = 9650029242287828579ULL; +static const U64 PRIME64_5 = 2870177450012600261ULL; + +static U64 XXH64_round(U64 acc, U64 input) +{ + acc += input * PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= PRIME64_1; + return acc; +} + +static U64 XXH64_mergeRound(U64 acc, U64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * PRIME64_1 + PRIME64_4; + return acc; +} + +static U64 XXH64_avalanche(U64 h64) +{ + h64 ^= h64 >> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + + +#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) + +static U64 +XXH64_finalize(U64 h64, const void* ptr, size_t len, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1_64 \ + h64 ^= (*p) * PRIME64_5; \ + p++; \ + h64 = XXH_rotl64(h64, 11) * PRIME64_1; + +#define PROCESS4_64 \ + h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; \ + p+=4; \ + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + +#define PROCESS8_64 { \ + U64 const k1 = XXH64_round(0, XXH_get64bits(p)); \ + p+=8; \ + h64 ^= k1; \ + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \ +} + + switch(len&31) { + case 24: PROCESS8_64; + /* fallthrough */ + case 16: PROCESS8_64; + /* fallthrough */ + case 8: PROCESS8_64; + return XXH64_avalanche(h64); + + case 28: PROCESS8_64; + /* fallthrough */ + case 20: PROCESS8_64; + /* fallthrough */ + case 12: PROCESS8_64; + /* fallthrough */ + case 4: PROCESS4_64; + return XXH64_avalanche(h64); + + case 25: PROCESS8_64; + /* fallthrough */ + case 17: PROCESS8_64; + /* fallthrough */ + case 9: PROCESS8_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 29: PROCESS8_64; + /* fallthrough */ + case 21: PROCESS8_64; + /* fallthrough */ + case 13: PROCESS8_64; + /* fallthrough */ + case 5: PROCESS4_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 26: PROCESS8_64; + /* fallthrough */ + case 18: PROCESS8_64; + /* fallthrough */ + case 10: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 30: PROCESS8_64; + /* fallthrough */ + case 22: PROCESS8_64; + /* fallthrough */ + case 14: PROCESS8_64; + /* fallthrough */ + case 6: PROCESS4_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 27: PROCESS8_64; + /* fallthrough */ + case 19: PROCESS8_64; + /* fallthrough */ + case 11: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 31: PROCESS8_64; + /* fallthrough */ + case 23: PROCESS8_64; + /* fallthrough */ + case 15: PROCESS8_64; + /* fallthrough */ + case 7: PROCESS4_64; + /* fallthrough */ + case 3: PROCESS1_64; + /* fallthrough */ + case 2: PROCESS1_64; + /* fallthrough */ + case 1: PROCESS1_64; + /* fallthrough */ + case 0: return XXH64_avalanche(h64); + } + + /* impossible to reach */ + assert(0); + return 0; /* unreachable, but some compilers complain without it */ +} + +FORCE_INLINE U64 +XXH64_endian_align(const void* input, size_t len, U64 seed, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U64 h64; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)32; + } +#endif + + if (len>=32) { + const BYTE* const limit = bEnd - 32; + U64 v1 = seed + PRIME64_1 + PRIME64_2; + U64 v2 = seed + PRIME64_2; + U64 v3 = seed + 0; + U64 v4 = seed - PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; + v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; + v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; + v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; + } while (p<=limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + PRIME64_5; + } + + h64 += (U64) len; + + return XXH64_finalize(h64, p, len, endian, align); +} + + +XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, input, len); + return XXH64_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + +/*====== Hash Streaming ======*/ + +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) +{ + XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME64_1 + PRIME64_2; + state.v2 = seed + PRIME64_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME64_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + +FORCE_INLINE +XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); + state->memsize += (U32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian)); + state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian)); + state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian)); + state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian)); + p += 32-state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const BYTE* const limit = bEnd - 32; + U64 v1 = state->v1; + U64 v2 = state->v2; + U64 v3 = state->v3; + U64 v4 = state->v4; + + do { + v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8; + v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8; + v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8; + v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH64_update_endian(state_in, input, len, XXH_bigEndian); +} + +FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) +{ + U64 h64; + + if (state->total_len >= 32) { + U64 const v1 = state->v1; + U64 const v2 = state->v2; + U64 const v3 = state->v3; + U64 const v4 = state->v4; + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } else { + h64 = state->v3 /*seed*/ + PRIME64_5; + } + + h64 += (U64) state->total_len; + + return XXH64_finalize(h64, state->mem64, (size_t)state->total_len, endian, XXH_aligned); +} + +XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_digest_endian(state_in, XXH_littleEndian); + else + return XXH64_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + +#endif /* XXH_NO_LONG_LONG */ diff --git a/contrib/xxhash/xxhash.h b/contrib/xxhash/xxhash.h new file mode 100644 index 00000000000..d6bad943358 --- /dev/null +++ b/contrib/xxhash/xxhash.h @@ -0,0 +1,328 @@ +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. + +A 64-bit version, named XXH64, is available since r35. +It offers much better speed, but for 64-bit applications only. +Name Speed on 64 bits Speed on 32 bits +XXH64 13.8 GB/s 1.9 GB/s +XXH32 6.8 GB/s 6.0 GB/s +*/ + +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +#if defined (__cplusplus) +extern "C" { +#endif + + +/* **************************** +* Definitions +******************************/ +#include <stddef.h> /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + +/* **************************** + * API modifier + ******************************/ +/** XXH_INLINE_ALL (and XXH_PRIVATE_API) + * This is useful to include xxhash functions in `static` mode + * in order to inline them, and remove their symbol from the public list. + * Inlining can offer dramatic performance improvement on small keys. + * Methodology : + * #define XXH_INLINE_ALL + * #include "xxhash.h" + * `xxhash.c` is automatically included. + * It's not useful to compile and link it as a separate module. + */ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +# endif +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline __attribute__((unused)) +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else + /* this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static +# endif +#else +# define XXH_PUBLIC_API /* do nothing */ +#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ + +/*! XXH_NAMESPACE, aka Namespace Emulation : + * + * If you want to include _and expose_ xxHash functions from within your own library, + * but also want to avoid symbol collisions with other libraries which may also include xxHash, + * + * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library + * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values). + * + * Note that no change is required within the calling program as long as it includes `xxhash.h` : + * regular symbol name will be automatically translated by this header. + */ +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +#endif + + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 6 +#define XXH_VERSION_RELEASE 5 +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) +XXH_PUBLIC_API unsigned XXH_versionNumber (void); + + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +typedef unsigned int XXH32_hash_t; + +/*! XXH32() : + Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". + The memory between input & input+length must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); + +/*====== Streaming ======*/ +typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/* + * Streaming functions generate the xxHash of an input provided in multiple segments. + * Note that, for small input, they are slower than single-call functions, due to state management. + * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. + * + * XXH state must first be allocated, using XXH*_createState() . + * + * Start a new hash by initializing state with a seed, using XXH*_reset(). + * + * Then, feed the hash state by calling XXH*_update() as many times as necessary. + * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. + * + * Finally, a hash value can be produced anytime, by using XXH*_digest(). + * This function returns the nn-bits hash as an int or long long. + * + * It's still possible to continue inserting input into the hash state after a digest, + * and generate some new hashes later on, by calling again XXH*_digest(). + * + * When done, free XXH state space if it was allocated dynamically. + */ + +/*====== Canonical representation ======*/ + +typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + +/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. + * The canonical representation uses human-readable write convention, aka big-endian (large digits first). + * These functions allow transformation of hash result into and from its canonical format. + * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. + */ + + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +typedef unsigned long long XXH64_hash_t; + +/*! XXH64() : + Calculate the 64-bit hash of sequence of length "len" stored at memory address "input". + "seed" can be used to alter the result predictably. + This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark). +*/ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); + +/*====== Streaming ======*/ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[8]; } XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +#endif /* XXH_NO_LONG_LONG */ + + + +#ifdef XXH_STATIC_LINKING_ONLY + +/* ================================================================================================ + This section contains declarations which are not guaranteed to remain stable. + They may change in future versions, becoming incompatible with a different version of the library. + These declarations should only be used with static linking. + Never use them in association with dynamic linking ! +=================================================================================================== */ + +/* These definitions are only present to allow + * static allocation of XXH state, on stack or in a struct for example. + * Never **ever** use members directly. */ + +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include <stdint.h> + +struct XXH32_state_s { + uint32_t total_len_32; + uint32_t large_len; + uint32_t v1; + uint32_t v2; + uint32_t v3; + uint32_t v4; + uint32_t mem32[4]; + uint32_t memsize; + uint32_t reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +struct XXH64_state_s { + uint64_t total_len; + uint64_t v1; + uint64_t v2; + uint64_t v3; + uint64_t v4; + uint64_t mem64[4]; + uint32_t memsize; + uint32_t reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ + +# else + +struct XXH32_state_s { + unsigned total_len_32; + unsigned large_len; + unsigned v1; + unsigned v2; + unsigned v3; + unsigned v4; + unsigned mem32[4]; + unsigned memsize; + unsigned reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +# ifndef XXH_NO_LONG_LONG /* remove 64-bit support */ +struct XXH64_state_s { + unsigned long long total_len; + unsigned long long v1; + unsigned long long v2; + unsigned long long v3; + unsigned long long v4; + unsigned long long mem64[4]; + unsigned memsize; + unsigned reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ +# endif + +# endif + + +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */ +#endif + +#endif /* XXH_STATIC_LINKING_ONLY */ + + +#if defined (__cplusplus) +} +#endif + +#endif /* XXHASH_H_5627135585666179 */ diff --git a/contrib/xxhash/xxhsum.c b/contrib/xxhash/xxhsum.c new file mode 100644 index 00000000000..69931f727f0 --- /dev/null +++ b/contrib/xxhash/xxhsum.c @@ -0,0 +1,1301 @@ +/* +* xxhsum - Command line interface for xxhash algorithms +* Copyright (C) Yann Collet 2012-2016 +* +* GPL v2 License +* +* This program is free software; 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. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +* +* You can contact the author at : +* - xxHash homepage : http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* xxhsum : + * Provides hash value of a file content, or a list of files, or stdin + * Display convention is Big Endian, for both 32 and 64 bits algorithms + */ + +#ifndef XXHASH_C_2097394837 +#define XXHASH_C_2097394837 + +/* ************************************ + * Compiler Options + **************************************/ +/* MS Visual */ +#if defined(_MSC_VER) || defined(_WIN32) +# define _CRT_SECURE_NO_WARNINGS /* removes visual warnings */ +#endif + +/* Under Linux at least, pull in the *64 commands */ +#ifndef _LARGEFILE64_SOURCE +# define _LARGEFILE64_SOURCE +#endif + + +/* ************************************ + * Includes + **************************************/ +#include <stdlib.h> /* malloc, calloc, free, exit */ +#include <stdio.h> /* fprintf, fopen, ftello64, fread, stdin, stdout, _fileno (when present) */ +#include <string.h> /* strcmp */ +#include <sys/types.h> /* stat, stat64, _stat64 */ +#include <sys/stat.h> /* stat, stat64, _stat64 */ +#include <time.h> /* clock_t, clock, CLOCKS_PER_SEC */ +#include <assert.h> /* assert */ + +#define XXH_STATIC_LINKING_ONLY /* *_state_t */ +#include "xxhash.h" + + +/* ************************************ + * OS-Specific Includes + **************************************/ +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) +# include <fcntl.h> /* _O_BINARY */ +# include <io.h> /* _setmode, _isatty */ +# define SET_BINARY_MODE(file) _setmode(_fileno(file), _O_BINARY) +# define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) +#else +# include <unistd.h> /* isatty, STDIN_FILENO */ +# define SET_BINARY_MODE(file) +# define IS_CONSOLE(stdStream) isatty(STDIN_FILENO) +#endif + +#if !defined(S_ISREG) +# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) +#endif + + +/* ************************************ +* Basic Types +**************************************/ +#ifndef MEM_MODULE +# define MEM_MODULE +# if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# include <stdint.h> + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; +# else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; +# endif +#endif + +static unsigned BMK_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} + + +/* ************************************* + * Constants + ***************************************/ +#define LIB_VERSION XXH_VERSION_MAJOR.XXH_VERSION_MINOR.XXH_VERSION_RELEASE +#define QUOTE(str) #str +#define EXPAND_AND_QUOTE(str) QUOTE(str) +#define PROGRAM_VERSION EXPAND_AND_QUOTE(LIB_VERSION) +static const int g_nbBits = (int)(sizeof(void*)*8); +static const char g_lename[] = "little endian"; +static const char g_bename[] = "big endian"; +#define ENDIAN_NAME (BMK_isLittleEndian() ? g_lename : g_bename) +static const char author[] = "Yann Collet"; +#define WELCOME_MESSAGE(exename) "%s %s (%i-bits %s), by %s \n", \ + exename, PROGRAM_VERSION, g_nbBits, ENDIAN_NAME, author + +#define KB *( 1<<10) +#define MB *( 1<<20) +#define GB *(1U<<30) + +static size_t XXH_DEFAULT_SAMPLE_SIZE = 100 KB; +#define NBLOOPS 3 /* Default number of benchmark iterations */ +#define TIMELOOP_S 1 +#define TIMELOOP (TIMELOOP_S * CLOCKS_PER_SEC) /* Minimum timing per iteration */ +#define XXHSUM32_DEFAULT_SEED 0 /* Default seed for algo_xxh32 */ +#define XXHSUM64_DEFAULT_SEED 0 /* Default seed for algo_xxh64 */ + +#define MAX_MEM (2 GB - 64 MB) + +static const char stdinName[] = "-"; +typedef enum { algo_xxh32, algo_xxh64 } algoType; +static const algoType g_defaultAlgo = algo_xxh64; /* required within main() & usage() */ + +/* <16 hex char> <SPC> <SPC> <filename> <'\0'> + * '4096' is typical Linux PATH_MAX configuration. */ +#define DEFAULT_LINE_LENGTH (sizeof(XXH64_hash_t) * 2 + 2 + 4096 + 1) + +/* Maximum acceptable line length. */ +#define MAX_LINE_LENGTH (32 KB) + + +/* ************************************ + * Display macros + **************************************/ +#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#define DISPLAYRESULT(...) fprintf(stdout, __VA_ARGS__) +#define DISPLAYLEVEL(l, ...) do { if (g_displayLevel>=l) DISPLAY(__VA_ARGS__); } while (0) +static int g_displayLevel = 2; + + +/* ************************************ + * Local variables + **************************************/ +static U32 g_nbIterations = NBLOOPS; + + +/* ************************************ + * Benchmark Functions + **************************************/ +static clock_t BMK_clockSpan( clock_t start ) +{ + return clock() - start; /* works even if overflow; Typical max span ~ 30 mn */ +} + + +static size_t BMK_findMaxMem(U64 requiredMem) +{ + size_t const step = 64 MB; + void* testmem = NULL; + + requiredMem = (((requiredMem >> 26) + 1) << 26); + requiredMem += 2*step; + if (requiredMem > MAX_MEM) requiredMem = MAX_MEM; + + while (!testmem) { + if (requiredMem > step) requiredMem -= step; + else requiredMem >>= 1; + testmem = malloc ((size_t)requiredMem); + } + free (testmem); + + /* keep some space available */ + if (requiredMem > step) requiredMem -= step; + else requiredMem >>= 1; + + return (size_t)requiredMem; +} + + +static U64 BMK_GetFileSize(const char* infilename) +{ + int r; +#if defined(_MSC_VER) + struct _stat64 statbuf; + r = _stat64(infilename, &statbuf); +#else + struct stat statbuf; + r = stat(infilename, &statbuf); +#endif + if (r || !S_ISREG(statbuf.st_mode)) return 0; /* No good... */ + return (U64)statbuf.st_size; +} + +typedef U32 (*hashFunction)(const void* buffer, size_t bufferSize, U32 seed); + +static U32 localXXH32(const void* buffer, size_t bufferSize, U32 seed) { return XXH32(buffer, bufferSize, seed); } + +static U32 localXXH64(const void* buffer, size_t bufferSize, U32 seed) { return (U32)XXH64(buffer, bufferSize, seed); } + +static void BMK_benchHash(hashFunction h, const char* hName, const void* buffer, size_t bufferSize) +{ + U32 nbh_perIteration = ((300 MB) / (bufferSize+1)) + 1; /* first loop conservatively aims for 300 MB/s */ + U32 iterationNb; + double fastestH = 100000000.; + + DISPLAYLEVEL(2, "\r%70s\r", ""); /* Clean display line */ + if (g_nbIterations<1) g_nbIterations=1; + for (iterationNb = 1; iterationNb <= g_nbIterations; iterationNb++) { + U32 r=0; + clock_t cStart; + + DISPLAYLEVEL(2, "%1i-%-17.17s : %10u ->\r", iterationNb, hName, (U32)bufferSize); + cStart = clock(); + while (clock() == cStart); /* starts clock() at its exact beginning */ + cStart = clock(); + + { U32 i; + for (i=0; i<nbh_perIteration; i++) + r += h(buffer, bufferSize, i); + } + if (r==0) DISPLAYLEVEL(3,".\r"); /* do something with r to avoid compiler "optimizing" away hash function */ + { double const timeS = ((double)BMK_clockSpan(cStart) / CLOCKS_PER_SEC) / nbh_perIteration; + if (timeS < fastestH) fastestH = timeS; + DISPLAYLEVEL(2, "%1i-%-17.17s : %10u -> %8.0f it/s (%7.1f MB/s) \r", + iterationNb, hName, (U32)bufferSize, + (double)1 / fastestH, + ((double)bufferSize / (1<<20)) / fastestH ); + } + assert(fastestH > 1./2000000000); /* avoid U32 overflow */ + nbh_perIteration = (U32)(1 / fastestH) + 1; /* adjust nbh_perIteration to last roughtly one second */ + } + DISPLAYLEVEL(1, "%-19.19s : %10u -> %8.0f it/s (%7.1f MB/s) \n", hName, (U32)bufferSize, + (double)1 / fastestH, + ((double)bufferSize / (1<<20)) / fastestH); + if (g_displayLevel<1) + DISPLAYLEVEL(0, "%u, ", (U32)((double)1 / fastestH)); +} + + +/* BMK_benchMem(): + * specificTest : 0 == run all tests, 1+ run only specific test + * buffer : is supposed 8-bytes aligned (if malloc'ed, it should be) + * the real allocated size of buffer is supposed to be >= (bufferSize+3). + * @return : 0 on success, 1 if error (invalid mode selected) */ +static int BMK_benchMem(const void* buffer, size_t bufferSize, U32 specificTest) +{ + assert((((size_t)buffer) & 8) == 0); /* ensure alignment */ + + /* XXH32 bench */ + if ((specificTest==0) | (specificTest==1)) + BMK_benchHash(localXXH32, "XXH32", buffer, bufferSize); + + /* Bench XXH32 on Unaligned input */ + if ((specificTest==0) | (specificTest==2)) + BMK_benchHash(localXXH32, "XXH32 unaligned", ((const char*)buffer)+1, bufferSize); + + /* Bench XXH64 */ + if ((specificTest==0) | (specificTest==3)) + BMK_benchHash(localXXH64, "XXH64", buffer, bufferSize); + + /* Bench XXH64 on Unaligned input */ + if ((specificTest==0) | (specificTest==4)) + BMK_benchHash(localXXH64, "XXH64 unaligned", ((const char*)buffer)+3, bufferSize); + + if (specificTest > 4) { + DISPLAY("benchmark mode invalid \n"); + return 1; + } + return 0; +} + + +static size_t BMK_selectBenchedSize(const char* fileName) +{ U64 const inFileSize = BMK_GetFileSize(fileName); + size_t benchedSize = (size_t) BMK_findMaxMem(inFileSize); + if ((U64)benchedSize > inFileSize) benchedSize = (size_t)inFileSize; + if (benchedSize < inFileSize) { + DISPLAY("Not enough memory for '%s' full size; testing %i MB only...\n", fileName, (int)(benchedSize>>20)); + } + return benchedSize; +} + + +static int BMK_benchFiles(const char** fileNamesTable, int nbFiles, U32 specificTest) +{ + int result = 0; + int fileIdx; + + for (fileIdx=0; fileIdx<nbFiles; fileIdx++) { + const char* const inFileName = fileNamesTable[fileIdx]; + FILE* const inFile = fopen( inFileName, "rb" ); + size_t const benchedSize = BMK_selectBenchedSize(inFileName); + char* const buffer = (char*)calloc(benchedSize+16+3, 1); + void* const alignedBuffer = (buffer+15) - (((size_t)(buffer+15)) & 0xF); /* align on next 16 bytes */ + + /* Checks */ + if ((inFile==NULL) || (inFileName==NULL)) { + DISPLAY("Pb opening %s\n", inFileName); + free(buffer); + return 11; + } + if(!buffer) { + DISPLAY("\nError: not enough memory!\n"); + fclose(inFile); + return 12; + } + + /* Fill input buffer */ + DISPLAYLEVEL(1, "\rLoading %s... \n", inFileName); + { size_t const readSize = fread(alignedBuffer, 1, benchedSize, inFile); + fclose(inFile); + if(readSize != benchedSize) { + DISPLAY("\nError: problem reading file '%s' !! \n", inFileName); + free(buffer); + return 13; + } } + + /* bench */ + result |= BMK_benchMem(alignedBuffer, benchedSize, specificTest); + + free(buffer); + } + + return result; +} + + + +static int BMK_benchInternal(size_t keySize, int specificTest) +{ + void* const buffer = calloc(keySize+16+3, 1); + void* const alignedBuffer = ((char*)buffer+15) - (((size_t)((char*)buffer+15)) & 0xF); /* align on next 16 bytes */ + if(!buffer) { + DISPLAY("\nError: not enough memory!\n"); + return 12; + } + + /* bench */ + DISPLAYLEVEL(1, "Sample of "); + if (keySize > 10 KB) { + DISPLAYLEVEL(1, "%u KB", (U32)(keySize >> 10)); + } else { + DISPLAYLEVEL(1, "%u bytes", (U32)keySize); + } + DISPLAYLEVEL(1, "... \n"); + + { int const result = BMK_benchMem(alignedBuffer, keySize, specificTest); + free(buffer); + return result; + } +} + + +static void BMK_checkResult(U32 r1, U32 r2) +{ + static int nbTests = 1; + if (r1==r2) { + DISPLAYLEVEL(3, "\rTest%3i : %08X == %08X ok ", nbTests, r1, r2); + } else { + DISPLAY("\rERROR : Test%3i : %08X <> %08X !!!!! \n", nbTests, r1, r2); + exit(1); + } + nbTests++; +} + + +static void BMK_checkResult64(U64 r1, U64 r2) +{ + static int nbTests = 1; + if (r1!=r2) { + DISPLAY("\rERROR : Test%3i : 64-bit values non equals !!!!! \n", nbTests); + DISPLAY("\r %08X%08X != %08X%08X \n", (U32)(r1>>32), (U32)r1, (U32)(r2>>32), (U32)r2); + exit(1); + } + nbTests++; +} + + +static void BMK_testSequence64(void* sentence, size_t len, U64 seed, U64 Nresult) +{ + XXH64_state_t state; + U64 Dresult; + size_t pos; + + Dresult = XXH64(sentence, len, seed); + BMK_checkResult64(Dresult, Nresult); + + XXH64_reset(&state, seed); + XXH64_update(&state, sentence, len); + Dresult = XXH64_digest(&state); + BMK_checkResult64(Dresult, Nresult); + + XXH64_reset(&state, seed); + for (pos=0; pos<len; pos++) + XXH64_update(&state, ((char*)sentence)+pos, 1); + Dresult = XXH64_digest(&state); + BMK_checkResult64(Dresult, Nresult); +} + + +static void BMK_testSequence(const void* sequence, size_t len, U32 seed, U32 Nresult) +{ + XXH32_state_t state; + U32 Dresult; + size_t pos; + + Dresult = XXH32(sequence, len, seed); + BMK_checkResult(Dresult, Nresult); + + XXH32_reset(&state, seed); + XXH32_update(&state, sequence, len); + Dresult = XXH32_digest(&state); + BMK_checkResult(Dresult, Nresult); + + XXH32_reset(&state, seed); + for (pos=0; pos<len; pos++) + XXH32_update(&state, ((const char*)sequence)+pos, 1); + Dresult = XXH32_digest(&state); + BMK_checkResult(Dresult, Nresult); +} + + +#define SANITY_BUFFER_SIZE 101 +static void BMK_sanityCheck(void) +{ + static const U32 prime = 2654435761U; + BYTE sanityBuffer[SANITY_BUFFER_SIZE]; + U32 byteGen = prime; + + int i; + for (i=0; i<SANITY_BUFFER_SIZE; i++) { + sanityBuffer[i] = (BYTE)(byteGen>>24); + byteGen *= byteGen; + } + + BMK_testSequence(NULL, 0, 0, 0x02CC5D05); + BMK_testSequence(NULL, 0, prime, 0x36B78AE7); + BMK_testSequence(sanityBuffer, 1, 0, 0xB85CBEE5); + BMK_testSequence(sanityBuffer, 1, prime, 0xD5845D64); + BMK_testSequence(sanityBuffer, 14, 0, 0xE5AA0AB4); + BMK_testSequence(sanityBuffer, 14, prime, 0x4481951D); + BMK_testSequence(sanityBuffer, SANITY_BUFFER_SIZE, 0, 0x1F1AA412); + BMK_testSequence(sanityBuffer, SANITY_BUFFER_SIZE, prime, 0x498EC8E2); + + BMK_testSequence64(NULL , 0, 0, 0xEF46DB3751D8E999ULL); + BMK_testSequence64(NULL , 0, prime, 0xAC75FDA2929B17EFULL); + BMK_testSequence64(sanityBuffer, 1, 0, 0x4FCE394CC88952D8ULL); + BMK_testSequence64(sanityBuffer, 1, prime, 0x739840CB819FA723ULL); + BMK_testSequence64(sanityBuffer, 14, 0, 0xCFFA8DB881BC3A3DULL); + BMK_testSequence64(sanityBuffer, 14, prime, 0x5B9611585EFCC9CBULL); + BMK_testSequence64(sanityBuffer, SANITY_BUFFER_SIZE, 0, 0x0EAB543384F878ADULL); + BMK_testSequence64(sanityBuffer, SANITY_BUFFER_SIZE, prime, 0xCAA65939306F1E21ULL); + + DISPLAYLEVEL(3, "\r%70s\r", ""); /* Clean display line */ + DISPLAYLEVEL(3, "Sanity check -- all tests ok\n"); +} + + +/* ******************************************************** +* File Hashing +**********************************************************/ + +static void BMK_display_LittleEndian(const void* ptr, size_t length) +{ + const BYTE* p = (const BYTE*)ptr; + size_t idx; + for (idx=length-1; idx<length; idx--) /* intentional underflow to negative to detect end */ + DISPLAYRESULT("%02x", p[idx]); +} + +static void BMK_display_BigEndian(const void* ptr, size_t length) +{ + const BYTE* p = (const BYTE*)ptr; + size_t idx; + for (idx=0; idx<length; idx++) + DISPLAYRESULT("%02x", p[idx]); +} + +static void BMK_hashStream(void* xxhHashValue, const algoType hashType, FILE* inFile, void* buffer, size_t blockSize) +{ + XXH64_state_t state64; + XXH32_state_t state32; + size_t readSize; + + /* Init */ + XXH32_reset(&state32, XXHSUM32_DEFAULT_SEED); + XXH64_reset(&state64, XXHSUM64_DEFAULT_SEED); + + /* Load file & update hash */ + readSize = 1; + while (readSize) { + readSize = fread(buffer, 1, blockSize, inFile); + switch(hashType) + { + case algo_xxh32: + XXH32_update(&state32, buffer, readSize); + break; + case algo_xxh64: + XXH64_update(&state64, buffer, readSize); + break; + default: + break; + } + } + + switch(hashType) + { + case algo_xxh32: + { U32 const h32 = XXH32_digest(&state32); + memcpy(xxhHashValue, &h32, sizeof(h32)); + break; + } + case algo_xxh64: + { U64 const h64 = XXH64_digest(&state64); + memcpy(xxhHashValue, &h64, sizeof(h64)); + break; + } + default: + break; + } +} + + +typedef enum { big_endian, little_endian} endianess; + +static int BMK_hash(const char* fileName, + const algoType hashType, + const endianess displayEndianess) +{ + FILE* inFile; + size_t const blockSize = 64 KB; + void* buffer; + U32 h32 = 0; + U64 h64 = 0; + + /* Check file existence */ + if (fileName == stdinName) { + inFile = stdin; + SET_BINARY_MODE(stdin); + } + else + inFile = fopen( fileName, "rb" ); + if (inFile==NULL) { + DISPLAY( "Pb opening %s\n", fileName); + return 1; + } + + /* Memory allocation & restrictions */ + buffer = malloc(blockSize); + if(!buffer) { + DISPLAY("\nError: not enough memory!\n"); + fclose(inFile); + return 1; + } + + /* loading notification */ + { const size_t fileNameSize = strlen(fileName); + const char* const fileNameEnd = fileName + fileNameSize; + const int maxInfoFilenameSize = (int)(fileNameSize > 30 ? 30 : fileNameSize); + int infoFilenameSize = 1; + while ((infoFilenameSize < maxInfoFilenameSize) + && (fileNameEnd[-1-infoFilenameSize] != '/') + && (fileNameEnd[-1-infoFilenameSize] != '\\') ) + infoFilenameSize++; + DISPLAY("\rLoading %s... \r", fileNameEnd - infoFilenameSize); + + /* Load file & update hash */ + switch(hashType) + { + case algo_xxh32: + BMK_hashStream(&h32, algo_xxh32, inFile, buffer, blockSize); + break; + case algo_xxh64: + BMK_hashStream(&h64, algo_xxh64, inFile, buffer, blockSize); + break; + default: + break; + } + + fclose(inFile); + free(buffer); + DISPLAY("%s \r", fileNameEnd - infoFilenameSize); /* erase line */ + } + + /* display Hash */ + switch(hashType) + { + case algo_xxh32: + { XXH32_canonical_t hcbe32; + XXH32_canonicalFromHash(&hcbe32, h32); + displayEndianess==big_endian ? + BMK_display_BigEndian(&hcbe32, sizeof(hcbe32)) : BMK_display_LittleEndian(&hcbe32, sizeof(hcbe32)); + DISPLAYRESULT(" %s\n", fileName); + break; + } + case algo_xxh64: + { XXH64_canonical_t hcbe64; + XXH64_canonicalFromHash(&hcbe64, h64); + displayEndianess==big_endian ? + BMK_display_BigEndian(&hcbe64, sizeof(hcbe64)) : BMK_display_LittleEndian(&hcbe64, sizeof(hcbe64)); + DISPLAYRESULT(" %s\n", fileName); + break; + } + default: + break; + } + + return 0; +} + + +static int BMK_hashFiles(const char** fnList, int fnTotal, + algoType hashType, endianess displayEndianess) +{ + int fnNb; + int result = 0; + + if (fnTotal==0) + return BMK_hash(stdinName, hashType, displayEndianess); + + for (fnNb=0; fnNb<fnTotal; fnNb++) + result += BMK_hash(fnList[fnNb], hashType, displayEndianess); + DISPLAY("\r%70s\r", ""); + return result; +} + + +typedef enum { + GetLine_ok, + GetLine_eof, + GetLine_exceedMaxLineLength, + GetLine_outOfMemory, +} GetLineResult; + +typedef enum { + CanonicalFromString_ok, + CanonicalFromString_invalidFormat, +} CanonicalFromStringResult; + +typedef enum { + ParseLine_ok, + ParseLine_invalidFormat, +} ParseLineResult; + +typedef enum { + LineStatus_hashOk, + LineStatus_hashFailed, + LineStatus_failedToOpen, +} LineStatus; + +typedef union { + XXH32_canonical_t xxh32; + XXH64_canonical_t xxh64; +} Canonical; + +typedef struct { + Canonical canonical; + const char* filename; + int xxhBits; /* canonical type : 32:xxh32, 64:xxh64 */ +} ParsedLine; + +typedef struct { + unsigned long nProperlyFormattedLines; + unsigned long nImproperlyFormattedLines; + unsigned long nMismatchedChecksums; + unsigned long nOpenOrReadFailures; + unsigned long nMixedFormatLines; + int xxhBits; + int quit; +} ParseFileReport; + +typedef struct { + const char* inFileName; + FILE* inFile; + int lineMax; + char* lineBuf; + size_t blockSize; + char* blockBuf; + int strictMode; + int statusOnly; + int warn; + int quiet; + ParseFileReport report; +} ParseFileArg; + + +/* Read line from stream. + Returns GetLine_ok, if it reads line successfully. + Returns GetLine_eof, if stream reaches EOF. + Returns GetLine_exceedMaxLineLength, if line length is longer than MAX_LINE_LENGTH. + Returns GetLine_outOfMemory, if line buffer memory allocation failed. + */ +static GetLineResult getLine(char** lineBuf, int* lineMax, FILE* inFile) +{ + GetLineResult result = GetLine_ok; + int len = 0; + + if ((*lineBuf == NULL) || (*lineMax<1)) { + free(*lineBuf); /* in case it's != NULL */ + *lineMax = 0; + *lineBuf = (char*)malloc(DEFAULT_LINE_LENGTH); + if(*lineBuf == NULL) return GetLine_outOfMemory; + *lineMax = DEFAULT_LINE_LENGTH; + } + + for (;;) { + const int c = fgetc(inFile); + if (c == EOF) { + /* If we meet EOF before first character, returns GetLine_eof, + * otherwise GetLine_ok. + */ + if (len == 0) result = GetLine_eof; + break; + } + + /* Make enough space for len+1 (for final NUL) bytes. */ + if (len+1 >= *lineMax) { + char* newLineBuf = NULL; + int newBufSize = *lineMax; + + newBufSize += (newBufSize/2) + 1; /* x 1.5 */ + if (newBufSize > MAX_LINE_LENGTH) newBufSize = MAX_LINE_LENGTH; + if (len+1 >= newBufSize) return GetLine_exceedMaxLineLength; + + newLineBuf = (char*) realloc(*lineBuf, newBufSize); + if (newLineBuf == NULL) return GetLine_outOfMemory; + + *lineBuf = newLineBuf; + *lineMax = newBufSize; + } + + if (c == '\n') break; + (*lineBuf)[len++] = (char) c; + } + + (*lineBuf)[len] = '\0'; + return result; +} + + +/* Converts one hexadecimal character to integer. + * Returns -1, if given character is not hexadecimal. + */ +static int charToHex(char c) +{ + int result = -1; + if (c >= '0' && c <= '9') { + result = (int) (c - '0'); + } else if (c >= 'A' && c <= 'F') { + result = (int) (c - 'A') + 0x0a; + } else if (c >= 'a' && c <= 'f') { + result = (int) (c - 'a') + 0x0a; + } + return result; +} + + +/* Converts XXH32 canonical hexadecimal string hashStr to big endian unsigned char array dst. + * Returns CANONICAL_FROM_STRING_INVALID_FORMAT, if hashStr is not well formatted. + * Returns CANONICAL_FROM_STRING_OK, if hashStr is parsed successfully. + */ +static CanonicalFromStringResult canonicalFromString(unsigned char* dst, + size_t dstSize, + const char* hashStr) +{ + size_t i; + for (i = 0; i < dstSize; ++i) { + int h0, h1; + + h0 = charToHex(hashStr[i*2 + 0]); + if (h0 < 0) return CanonicalFromString_invalidFormat; + + h1 = charToHex(hashStr[i*2 + 1]); + if (h1 < 0) return CanonicalFromString_invalidFormat; + + dst[i] = (unsigned char) ((h0 << 4) | h1); + } + return CanonicalFromString_ok; +} + + +/* Parse single line of xxHash checksum file. + * Returns PARSE_LINE_ERROR_INVALID_FORMAT, if line is not well formatted. + * Returns PARSE_LINE_OK if line is parsed successfully. + * And members of parseLine will be filled by parsed values. + * + * - line must be ended with '\0'. + * - Since parsedLine.filename will point within given argument `line`, + * users must keep `line`s content during they are using parsedLine. + * + * Given xxHash checksum line should have the following format: + * + * <8 or 16 hexadecimal char> <space> <space> <filename...> <'\0'> + */ +static ParseLineResult parseLine(ParsedLine* parsedLine, const char* line) +{ + const char* const firstSpace = strchr(line, ' '); + const char* const secondSpace = firstSpace + 1; + + parsedLine->filename = NULL; + parsedLine->xxhBits = 0; + + if (firstSpace == NULL || *secondSpace != ' ') return ParseLine_invalidFormat; + + switch (firstSpace - line) + { + case 8: + { XXH32_canonical_t* xxh32c = &parsedLine->canonical.xxh32; + if (canonicalFromString(xxh32c->digest, sizeof(xxh32c->digest), line) + != CanonicalFromString_ok) { + return ParseLine_invalidFormat; + } + parsedLine->xxhBits = 32; + break; + } + + case 16: + { XXH64_canonical_t* xxh64c = &parsedLine->canonical.xxh64; + if (canonicalFromString(xxh64c->digest, sizeof(xxh64c->digest), line) + != CanonicalFromString_ok) { + return ParseLine_invalidFormat; + } + parsedLine->xxhBits = 64; + break; + } + + default: + return ParseLine_invalidFormat; + break; + } + + parsedLine->filename = secondSpace + 1; + return ParseLine_ok; +} + + +/*! Parse xxHash checksum file. + */ +static void parseFile1(ParseFileArg* parseFileArg) +{ + const char* const inFileName = parseFileArg->inFileName; + ParseFileReport* const report = &parseFileArg->report; + + unsigned long lineNumber = 0; + memset(report, 0, sizeof(*report)); + + while (!report->quit) { + FILE* fp = NULL; + LineStatus lineStatus = LineStatus_hashFailed; + GetLineResult getLineResult; + ParsedLine parsedLine; + memset(&parsedLine, 0, sizeof(parsedLine)); + + lineNumber++; + if (lineNumber == 0) { + /* This is unlikely happen, but md5sum.c has this + * error check. */ + DISPLAY("%s : too many checksum lines\n", inFileName); + report->quit = 1; + break; + } + + getLineResult = getLine(&parseFileArg->lineBuf, &parseFileArg->lineMax, + parseFileArg->inFile); + if (getLineResult != GetLine_ok) { + if (getLineResult == GetLine_eof) break; + + switch (getLineResult) + { + case GetLine_ok: + case GetLine_eof: + /* These cases never happen. See above getLineResult related "if"s. + They exist just for make gcc's -Wswitch-enum happy. */ + break; + + default: + DISPLAY("%s : %lu: unknown error\n", inFileName, lineNumber); + break; + + case GetLine_exceedMaxLineLength: + DISPLAY("%s : %lu: too long line\n", inFileName, lineNumber); + break; + + case GetLine_outOfMemory: + DISPLAY("%s : %lu: out of memory\n", inFileName, lineNumber); + break; + } + report->quit = 1; + break; + } + + if (parseLine(&parsedLine, parseFileArg->lineBuf) != ParseLine_ok) { + report->nImproperlyFormattedLines++; + if (parseFileArg->warn) { + DISPLAY("%s : %lu: improperly formatted XXHASH checksum line\n" + , inFileName, lineNumber); + } + continue; + } + + if (report->xxhBits != 0 && report->xxhBits != parsedLine.xxhBits) { + /* Don't accept xxh32/xxh64 mixed file */ + report->nImproperlyFormattedLines++; + report->nMixedFormatLines++; + if (parseFileArg->warn) { + DISPLAY("%s : %lu: improperly formatted XXHASH checksum line (XXH32/64)\n" + , inFileName, lineNumber); + } + continue; + } + + report->nProperlyFormattedLines++; + if (report->xxhBits == 0) { + report->xxhBits = parsedLine.xxhBits; + } + + fp = fopen(parsedLine.filename, "rb"); + if (fp == NULL) { + lineStatus = LineStatus_failedToOpen; + } else { + lineStatus = LineStatus_hashFailed; + switch (parsedLine.xxhBits) + { + case 32: + { XXH32_hash_t xxh; + BMK_hashStream(&xxh, algo_xxh32, fp, parseFileArg->blockBuf, parseFileArg->blockSize); + if (xxh == XXH32_hashFromCanonical(&parsedLine.canonical.xxh32)) { + lineStatus = LineStatus_hashOk; + } } + break; + + case 64: + { XXH64_hash_t xxh; + BMK_hashStream(&xxh, algo_xxh64, fp, parseFileArg->blockBuf, parseFileArg->blockSize); + if (xxh == XXH64_hashFromCanonical(&parsedLine.canonical.xxh64)) { + lineStatus = LineStatus_hashOk; + } } + break; + + default: + break; + } + fclose(fp); + } + + switch (lineStatus) + { + default: + DISPLAY("%s : unknown error\n", inFileName); + report->quit = 1; + break; + + case LineStatus_failedToOpen: + report->nOpenOrReadFailures++; + if (!parseFileArg->statusOnly) { + DISPLAYRESULT("%s : %lu: FAILED open or read %s\n" + , inFileName, lineNumber, parsedLine.filename); + } + break; + + case LineStatus_hashOk: + case LineStatus_hashFailed: + { int b = 1; + if (lineStatus == LineStatus_hashOk) { + /* If --quiet is specified, don't display "OK" */ + if (parseFileArg->quiet) b = 0; + } else { + report->nMismatchedChecksums++; + } + + if (b && !parseFileArg->statusOnly) { + DISPLAYRESULT("%s: %s\n", parsedLine.filename + , lineStatus == LineStatus_hashOk ? "OK" : "FAILED"); + } } + break; + } + } /* while (!report->quit) */ +} + + +/* Parse xxHash checksum file. + * Returns 1, if all procedures were succeeded. + * Returns 0, if any procedures was failed. + * + * If strictMode != 0, return error code if any line is invalid. + * If statusOnly != 0, don't generate any output. + * If warn != 0, print a warning message to stderr. + * If quiet != 0, suppress "OK" line. + * + * "All procedures are succeeded" means: + * - Checksum file contains at least one line and less than SIZE_T_MAX lines. + * - All files are properly opened and read. + * - All hash values match with its content. + * - (strict mode) All lines in checksum file are consistent and well formatted. + * + */ +static int checkFile(const char* inFileName, + const endianess displayEndianess, + U32 strictMode, + U32 statusOnly, + U32 warn, + U32 quiet) +{ + int result = 0; + FILE* inFile = NULL; + ParseFileArg parseFileArgBody; + ParseFileArg* const parseFileArg = &parseFileArgBody; + ParseFileReport* const report = &parseFileArg->report; + + if (displayEndianess != big_endian) { + /* Don't accept little endian */ + DISPLAY( "Check file mode doesn't support little endian\n" ); + return 0; + } + + /* note : stdinName is special constant pointer. It is not a string. */ + if (inFileName == stdinName) { + /* note : Since we expect text input for xxhash -c mode, + * Don't set binary mode for stdin */ + inFile = stdin; + } else { + inFile = fopen( inFileName, "rt" ); + } + + if (inFile == NULL) { + DISPLAY( "Pb opening %s\n", inFileName); + return 0; + } + + parseFileArg->inFileName = inFileName; + parseFileArg->inFile = inFile; + parseFileArg->lineMax = DEFAULT_LINE_LENGTH; + parseFileArg->lineBuf = (char*) malloc((size_t) parseFileArg->lineMax); + parseFileArg->blockSize = 64 * 1024; + parseFileArg->blockBuf = (char*) malloc(parseFileArg->blockSize); + parseFileArg->strictMode = strictMode; + parseFileArg->statusOnly = statusOnly; + parseFileArg->warn = warn; + parseFileArg->quiet = quiet; + + parseFile1(parseFileArg); + + free(parseFileArg->blockBuf); + free(parseFileArg->lineBuf); + + if (inFile != stdin) fclose(inFile); + + /* Show error/warning messages. All messages are copied from md5sum.c + */ + if (report->nProperlyFormattedLines == 0) { + DISPLAY("%s: no properly formatted XXHASH checksum lines found\n", inFileName); + } else if (!statusOnly) { + if (report->nImproperlyFormattedLines) { + DISPLAYRESULT("%lu lines are improperly formatted\n" + , report->nImproperlyFormattedLines); + } + if (report->nOpenOrReadFailures) { + DISPLAYRESULT("%lu listed files could not be read\n" + , report->nOpenOrReadFailures); + } + if (report->nMismatchedChecksums) { + DISPLAYRESULT("%lu computed checksums did NOT match\n" + , report->nMismatchedChecksums); + } } + + /* Result (exit) code logic is copied from + * gnu coreutils/src/md5sum.c digest_check() */ + result = report->nProperlyFormattedLines != 0 + && report->nMismatchedChecksums == 0 + && report->nOpenOrReadFailures == 0 + && (!strictMode || report->nImproperlyFormattedLines == 0) + && report->quit == 0; + return result; +} + + +static int checkFiles(const char** fnList, int fnTotal, + const endianess displayEndianess, + U32 strictMode, + U32 statusOnly, + U32 warn, + U32 quiet) +{ + int ok = 1; + + /* Special case for stdinName "-", + * note: stdinName is not a string. It's special pointer. */ + if (fnTotal==0) { + ok &= checkFile(stdinName, displayEndianess, strictMode, statusOnly, warn, quiet); + } else { + int fnNb; + for (fnNb=0; fnNb<fnTotal; fnNb++) + ok &= checkFile(fnList[fnNb], displayEndianess, strictMode, statusOnly, warn, quiet); + } + return ok ? 0 : 1; +} + + +/* ******************************************************** +* Main +**********************************************************/ + +static int usage(const char* exename) +{ + DISPLAY( WELCOME_MESSAGE(exename) ); + DISPLAY( "Usage :\n"); + DISPLAY( " %s [arg] [filenames]\n", exename); + DISPLAY( "When no filename provided, or - provided : use stdin as input\n"); + DISPLAY( "Arguments :\n"); + DISPLAY( " -H# : hash selection : 0=32bits, 1=64bits (default: %i)\n", (int)g_defaultAlgo); + DISPLAY( " -c : read xxHash sums from the [filenames] and check them\n"); + DISPLAY( " -h : help \n"); + return 0; +} + + +static int usage_advanced(const char* exename) +{ + usage(exename); + DISPLAY( "Advanced :\n"); + DISPLAY( " --little-endian : hash printed using little endian convention (default: big endian)\n"); + DISPLAY( " -V, --version : display version\n"); + DISPLAY( " -h, --help : display long help and exit\n"); + DISPLAY( " -b : benchmark mode \n"); + DISPLAY( " -i# : number of iterations (benchmark mode; default %i)\n", g_nbIterations); + DISPLAY( "\n"); + DISPLAY( "The following four options are useful only when verifying checksums (-c):\n"); + DISPLAY( "--strict : don't print OK for each successfully verified file\n"); + DISPLAY( "--status : don't output anything, status code shows success\n"); + DISPLAY( "--quiet : exit non-zero for improperly formatted checksum lines\n"); + DISPLAY( "--warn : warn about improperly formatted checksum lines\n"); + return 0; +} + +static int badusage(const char* exename) +{ + DISPLAY("Wrong parameters\n"); + usage(exename); + return 1; +} + +/*! readU32FromChar() : + @return : unsigned integer value read from input in `char` format, + 0 is no figure at *stringPtr position. + Interprets K, KB, KiB, M, MB and MiB suffix. + Modifies `*stringPtr`, advancing it to position where reading stopped. + Note : function result can overflow if digit string > MAX_UINT */ +static unsigned readU32FromChar(const char** stringPtr) +{ + unsigned result = 0; + while ((**stringPtr >='0') && (**stringPtr <='9')) + result *= 10, result += **stringPtr - '0', (*stringPtr)++ ; + if ((**stringPtr=='K') || (**stringPtr=='M')) { + result <<= 10; + if (**stringPtr=='M') result <<= 10; + (*stringPtr)++ ; + if (**stringPtr=='i') (*stringPtr)++; + if (**stringPtr=='B') (*stringPtr)++; + } + return result; +} + +int main(int argc, const char** argv) +{ + int i, filenamesStart = 0; + const char* const exename = argv[0]; + U32 benchmarkMode = 0; + U32 fileCheckMode = 0; + U32 strictMode = 0; + U32 statusOnly = 0; + U32 warn = 0; + U32 quiet = 0; + U32 specificTest = 0; + size_t keySize = XXH_DEFAULT_SAMPLE_SIZE; + algoType algo = g_defaultAlgo; + endianess displayEndianess = big_endian; + + /* special case : xxh32sum default to 32 bits checksum */ + if (strstr(exename, "xxh32sum") != NULL) algo = algo_xxh32; + + for(i=1; i<argc; i++) { + const char* argument = argv[i]; + + if(!argument) continue; /* Protection, if argument empty */ + + if (!strcmp(argument, "--little-endian")) { displayEndianess = little_endian; continue; } + if (!strcmp(argument, "--check")) { fileCheckMode = 1; continue; } + if (!strcmp(argument, "--strict")) { strictMode = 1; continue; } + if (!strcmp(argument, "--status")) { statusOnly = 1; continue; } + if (!strcmp(argument, "--quiet")) { quiet = 1; continue; } + if (!strcmp(argument, "--warn")) { warn = 1; continue; } + if (!strcmp(argument, "--help")) { return usage_advanced(exename); } + if (!strcmp(argument, "--version")) { DISPLAY(WELCOME_MESSAGE(exename)); return 0; } + + if (*argument!='-') { + if (filenamesStart==0) filenamesStart=i; /* only supports a continuous list of filenames */ + continue; + } + + /* command selection */ + argument++; /* note : *argument=='-' */ + + while (*argument!=0) { + switch(*argument) + { + /* Display version */ + case 'V': + DISPLAY(WELCOME_MESSAGE(exename)); return 0; + + /* Display help on usage */ + case 'h': + return usage_advanced(exename); + + /* select hash algorithm */ + case 'H': + algo = (algoType)(argument[1] - '0'); + argument+=2; + break; + + /* File check mode */ + case 'c': + fileCheckMode=1; + argument++; + break; + + /* Warning mode (file check mode only, alias of "--warning") */ + case 'w': + warn=1; + argument++; + break; + + /* Trigger benchmark mode */ + case 'b': + argument++; + benchmarkMode = 1; + specificTest = readU32FromChar(&argument); /* select one specific test (hidden option) */ + break; + + /* Modify Nb Iterations (benchmark only) */ + case 'i': + argument++; + g_nbIterations = readU32FromChar(&argument); + break; + + /* Modify Block size (benchmark only) */ + case 'B': + argument++; + keySize = readU32FromChar(&argument); + break; + + /* Modify verbosity of benchmark output (hidden option) */ + case 'q': + argument++; + g_displayLevel--; + break; + + default: + return badusage(exename); + } + } + } /* for(i=1; i<argc; i++) */ + + /* Check benchmark mode */ + if (benchmarkMode) { + DISPLAYLEVEL(2, WELCOME_MESSAGE(exename) ); + BMK_sanityCheck(); + if (filenamesStart==0) return BMK_benchInternal(keySize, specificTest); + return BMK_benchFiles(argv+filenamesStart, argc-filenamesStart, specificTest); + } + + /* Check if input is defined as console; trigger an error in this case */ + if ( (filenamesStart==0) && IS_CONSOLE(stdin) ) return badusage(exename); + + if (filenamesStart==0) filenamesStart = argc; + if (fileCheckMode) { + return checkFiles(argv+filenamesStart, argc-filenamesStart, + displayEndianess, strictMode, statusOnly, warn, quiet); + } else { + return BMK_hashFiles(argv+filenamesStart, argc-filenamesStart, algo, displayEndianess); + } +} + +#endif /* XXHASH_C_2097394837 */ |
