From 26f0f1a6a2d6b8ef8de42edc7db0a3ab7063702a Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Sun, 17 Jun 2018 15:44:07 +0200 Subject: [PATCH] containerfs: initial implementation A FUSE implementation of overlay. Not as stable and mature as the kernel FS. Signed-off-by: Giuseppe Scrivano --- AUTHORS | 0 COPYING | 1 + ChangeLog | 0 Makefile.am | 12 + README.md | 11 + configure.ac | 36 + lib/Makefile.am | 218 +++ lib/bitrotate.c | 3 + lib/bitrotate.h | 136 ++ lib/hash.c | 1225 +++++++++++++++++ lib/hash.h | 103 ++ lib/limits.h | 75 ++ lib/limits.in.h | 74 ++ lib/stdbool.in.h | 132 ++ lib/stdint.in.h | 726 ++++++++++ lib/sys_types.in.h | 95 ++ lib/xalloc-oversized.h | 60 + main.c | 2856 ++++++++++++++++++++++++++++++++++++++++ 18 files changed, 5763 insertions(+) create mode 100644 AUTHORS create mode 120000 COPYING create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 README.md create mode 100644 configure.ac create mode 100644 lib/Makefile.am create mode 100644 lib/bitrotate.c create mode 100644 lib/bitrotate.h create mode 100644 lib/hash.c create mode 100644 lib/hash.h create mode 100644 lib/limits.h create mode 100644 lib/limits.in.h create mode 100644 lib/stdbool.in.h create mode 100644 lib/stdint.in.h create mode 100644 lib/sys_types.in.h create mode 100644 lib/xalloc-oversized.h create mode 100644 main.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/COPYING b/COPYING new file mode 120000 index 0000000..88798ab --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +/usr/share/automake-1.15/COPYING \ No newline at end of file diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..d71489e --- /dev/null +++ b/Makefile.am @@ -0,0 +1,12 @@ +SUBDIRS = lib + +bin_PROGRAMS = containerfs + +ACLOCAL_AMFLAGS = -Im4 + +EXTRA_DIST = m4/gnulib-cache.m4 + +containerfs_CFLAGS = -I . -I lib $(FUSE_CFLAGS) +containerfs_LDFLAGS = $(FUSE_LIBS) +containerfs_LDADD = lib/libgnu.a +containerfs_SOURCES = main.c diff --git a/README.md b/README.md new file mode 100644 index 0000000..1043ebb --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +containerfs +========== + +An over complex implementation of overlay in FUSE. + +Usage: +======================================================= + +``` +./containerfs -o lowerdir=lowerdir/a:lowerdir/b,upperdir=up,workdir=workdir merged +``` diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..8811f8a --- /dev/null +++ b/configure.ac @@ -0,0 +1,36 @@ +AC_PREREQ([2.69]) +AC_INIT([containerfs], [0.1], [giuseppe@scrivano.org]) +AC_CONFIG_SRCDIR([main.c]) +AC_CONFIG_HEADERS([config.h]) + +AC_CONFIG_AUX_DIR([build-aux]) + +AM_INIT_AUTOMAKE([1.9]) + +AC_PROG_CC + +gl_EARLY +gl_INIT + +AC_CHECK_HEADERS([fcntl.h inttypes.h limits.h stddef.h stdint.h stdlib.h string.h unistd.h]) + +AC_CHECK_HEADER_STDBOOL +AC_TYPE_MODE_T +AC_FUNC_OBSTACK +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_UINT8_T +AC_CHECK_TYPES([ptrdiff_t]) + +PKG_CHECK_MODULES([FUSE], [fuse3 >= 3.2.1], [AC_DEFINE([HAVE_FUSE], 1, [Define if libfuse is available])], [ ]) + +AC_FUNC_ERROR_AT_LINE +AC_FUNC_MALLOC +AC_CHECK_FUNCS([memset strdup]) + +AC_CONFIG_FILES([Makefile lib/Makefile]) +AC_OUTPUT diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..35c5485 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,218 @@ +## DO NOT EDIT! GENERATED AUTOMATICALLY! +## Process this file with automake to produce Makefile.in. +# Copyright (C) 2002-2017 Free Software Foundation, Inc. +# +# This file is free software; 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 file is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this file. If not, see . +# +# As a special exception to the GNU General Public License, +# this file may be distributed as part of a program that +# contains a configuration script generated by Autoconf, under +# the same distribution terms as the rest of that program. +# +# Generated by gnulib-tool. +# Reproduce by: gnulib-tool --import --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --no-conditional-dependencies --no-libtool --macro-prefix=gl hash + +AUTOMAKE_OPTIONS = 1.9.6 gnits + +SUBDIRS = +noinst_HEADERS = +noinst_LIBRARIES = +noinst_LTLIBRARIES = +EXTRA_DIST = +BUILT_SOURCES = +SUFFIXES = +MOSTLYCLEANFILES = core *.stackdump +MOSTLYCLEANDIRS = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = +# No GNU Make output. + +AM_CPPFLAGS = +AM_CFLAGS = + +noinst_LIBRARIES += libgnu.a + +libgnu_a_SOURCES = +libgnu_a_LIBADD = $(gl_LIBOBJS) +libgnu_a_DEPENDENCIES = $(gl_LIBOBJS) +EXTRA_libgnu_a_SOURCES = + +## begin gnulib module absolute-header + +# Use this preprocessor expression to decide whether #include_next works. +# Do not rely on a 'configure'-time test for this, since the expression +# might appear in an installed header, which is used by some other compiler. +HAVE_INCLUDE_NEXT = (__GNUC__ || 60000000 <= __DECC_VER) + +## end gnulib module absolute-header + +## begin gnulib module bitrotate + +libgnu_a_SOURCES += bitrotate.h bitrotate.c + +## end gnulib module bitrotate + +## begin gnulib module hash + +libgnu_a_SOURCES += hash.c + +EXTRA_DIST += hash.h + +## end gnulib module hash + +## begin gnulib module limits-h + +BUILT_SOURCES += $(LIMITS_H) + +# We need the following in order to create when the system +# doesn't have one that is compatible with GNU. +if GL_GENERATE_LIMITS_H +limits.h: limits.in.h $(top_builddir)/config.status + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \ + sed -e 's|@''GUARD_PREFIX''@|GL|g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_LIMITS_H''@|$(NEXT_LIMITS_H)|g' \ + < $(srcdir)/limits.in.h; \ + } > $@-t && \ + mv $@-t $@ +else +limits.h: $(top_builddir)/config.status + rm -f $@ +endif +MOSTLYCLEANFILES += limits.h limits.h-t + +EXTRA_DIST += limits.in.h + +## end gnulib module limits-h + +## begin gnulib module stdbool + +BUILT_SOURCES += $(STDBOOL_H) + +# We need the following in order to create when the system +# doesn't have one that works. +if GL_GENERATE_STDBOOL_H +stdbool.h: stdbool.in.h $(top_builddir)/config.status + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's/@''HAVE__BOOL''@/$(HAVE__BOOL)/g' < $(srcdir)/stdbool.in.h; \ + } > $@-t && \ + mv $@-t $@ +else +stdbool.h: $(top_builddir)/config.status + rm -f $@ +endif +MOSTLYCLEANFILES += stdbool.h stdbool.h-t + +EXTRA_DIST += stdbool.in.h + +## end gnulib module stdbool + +## begin gnulib module stdint + +BUILT_SOURCES += $(STDINT_H) + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +if GL_GENERATE_STDINT_H +stdint.h: stdint.in.h $(top_builddir)/config.status + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's|@''GUARD_PREFIX''@|GL|g' \ + -e 's/@''HAVE_STDINT_H''@/$(HAVE_STDINT_H)/g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_STDINT_H''@|$(NEXT_STDINT_H)|g' \ + -e 's/@''HAVE_C99_STDINT_H''@/$(HAVE_C99_STDINT_H)/g' \ + -e 's/@''HAVE_SYS_TYPES_H''@/$(HAVE_SYS_TYPES_H)/g' \ + -e 's/@''HAVE_INTTYPES_H''@/$(HAVE_INTTYPES_H)/g' \ + -e 's/@''HAVE_SYS_INTTYPES_H''@/$(HAVE_SYS_INTTYPES_H)/g' \ + -e 's/@''HAVE_SYS_BITYPES_H''@/$(HAVE_SYS_BITYPES_H)/g' \ + -e 's/@''HAVE_WCHAR_H''@/$(HAVE_WCHAR_H)/g' \ + -e 's/@''HAVE_LONG_LONG_INT''@/$(HAVE_LONG_LONG_INT)/g' \ + -e 's/@''HAVE_UNSIGNED_LONG_LONG_INT''@/$(HAVE_UNSIGNED_LONG_LONG_INT)/g' \ + -e 's/@''APPLE_UNIVERSAL_BUILD''@/$(APPLE_UNIVERSAL_BUILD)/g' \ + -e 's/@''BITSIZEOF_PTRDIFF_T''@/$(BITSIZEOF_PTRDIFF_T)/g' \ + -e 's/@''PTRDIFF_T_SUFFIX''@/$(PTRDIFF_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_SIG_ATOMIC_T''@/$(BITSIZEOF_SIG_ATOMIC_T)/g' \ + -e 's/@''HAVE_SIGNED_SIG_ATOMIC_T''@/$(HAVE_SIGNED_SIG_ATOMIC_T)/g' \ + -e 's/@''SIG_ATOMIC_T_SUFFIX''@/$(SIG_ATOMIC_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_SIZE_T''@/$(BITSIZEOF_SIZE_T)/g' \ + -e 's/@''SIZE_T_SUFFIX''@/$(SIZE_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_WCHAR_T''@/$(BITSIZEOF_WCHAR_T)/g' \ + -e 's/@''HAVE_SIGNED_WCHAR_T''@/$(HAVE_SIGNED_WCHAR_T)/g' \ + -e 's/@''WCHAR_T_SUFFIX''@/$(WCHAR_T_SUFFIX)/g' \ + -e 's/@''BITSIZEOF_WINT_T''@/$(BITSIZEOF_WINT_T)/g' \ + -e 's/@''HAVE_SIGNED_WINT_T''@/$(HAVE_SIGNED_WINT_T)/g' \ + -e 's/@''WINT_T_SUFFIX''@/$(WINT_T_SUFFIX)/g' \ + -e 's/@''GNULIB_OVERRIDES_WINT_T''@/$(GNULIB_OVERRIDES_WINT_T)/g' \ + < $(srcdir)/stdint.in.h; \ + } > $@-t && \ + mv $@-t $@ +else +stdint.h: $(top_builddir)/config.status + rm -f $@ +endif +MOSTLYCLEANFILES += stdint.h stdint.h-t + +EXTRA_DIST += stdint.in.h + +## end gnulib module stdint + +## begin gnulib module sys_types + +BUILT_SOURCES += sys/types.h + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +sys/types.h: sys_types.in.h $(top_builddir)/config.status + $(AM_V_at)$(MKDIR_P) sys + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's|@''GUARD_PREFIX''@|GL|g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_SYS_TYPES_H''@|$(NEXT_SYS_TYPES_H)|g' \ + -e 's|@''WINDOWS_64_BIT_OFF_T''@|$(WINDOWS_64_BIT_OFF_T)|g' \ + -e 's|@''WINDOWS_STAT_INODES''@|$(WINDOWS_STAT_INODES)|g' \ + < $(srcdir)/sys_types.in.h; \ + } > $@-t && \ + mv $@-t $@ +MOSTLYCLEANFILES += sys/types.h sys/types.h-t + +EXTRA_DIST += sys_types.in.h + +## end gnulib module sys_types + +## begin gnulib module xalloc-oversized + + +EXTRA_DIST += xalloc-oversized.h + +## end gnulib module xalloc-oversized + + +mostlyclean-local: mostlyclean-generic + @for dir in '' $(MOSTLYCLEANDIRS); do \ + if test -n "$$dir" && test -d $$dir; then \ + echo "rmdir $$dir"; rmdir $$dir; \ + fi; \ + done; \ + : diff --git a/lib/bitrotate.c b/lib/bitrotate.c new file mode 100644 index 0000000..a8f6028 --- /dev/null +++ b/lib/bitrotate.c @@ -0,0 +1,3 @@ +#include +#define BITROTATE_INLINE _GL_EXTERN_INLINE +#include "bitrotate.h" diff --git a/lib/bitrotate.h b/lib/bitrotate.h new file mode 100644 index 0000000..ea14afb --- /dev/null +++ b/lib/bitrotate.h @@ -0,0 +1,136 @@ +/* bitrotate.h - Rotate bits in integers + Copyright (C) 2008-2017 Free Software Foundation, Inc. + + This program is free software: 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Simon Josefsson , 2008. */ + +#ifndef _GL_BITROTATE_H +#define _GL_BITROTATE_H + +#include +#include +#include + +#ifndef _GL_INLINE_HEADER_BEGIN + #error "Please include config.h first." +#endif +_GL_INLINE_HEADER_BEGIN +#ifndef BITROTATE_INLINE +# define BITROTATE_INLINE _GL_INLINE +#endif + +#ifdef UINT64_MAX +/* Given an unsigned 64-bit argument X, return the value corresponding + to rotating the bits N steps to the left. N must be between 1 and + 63 inclusive. */ +BITROTATE_INLINE uint64_t +rotl64 (uint64_t x, int n) +{ + return ((x << n) | (x >> (64 - n))) & UINT64_MAX; +} + +/* Given an unsigned 64-bit argument X, return the value corresponding + to rotating the bits N steps to the right. N must be between 1 to + 63 inclusive.*/ +BITROTATE_INLINE uint64_t +rotr64 (uint64_t x, int n) +{ + return ((x >> n) | (x << (64 - n))) & UINT64_MAX; +} +#endif + +/* Given an unsigned 32-bit argument X, return the value corresponding + to rotating the bits N steps to the left. N must be between 1 and + 31 inclusive. */ +BITROTATE_INLINE uint32_t +rotl32 (uint32_t x, int n) +{ + return ((x << n) | (x >> (32 - n))) & UINT32_MAX; +} + +/* Given an unsigned 32-bit argument X, return the value corresponding + to rotating the bits N steps to the right. N must be between 1 to + 31 inclusive.*/ +BITROTATE_INLINE uint32_t +rotr32 (uint32_t x, int n) +{ + return ((x >> n) | (x << (32 - n))) & UINT32_MAX; +} + +/* Given a size_t argument X, return the value corresponding + to rotating the bits N steps to the left. N must be between 1 and + (CHAR_BIT * sizeof (size_t) - 1) inclusive. */ +BITROTATE_INLINE size_t +rotl_sz (size_t x, int n) +{ + return ((x << n) | (x >> ((CHAR_BIT * sizeof x) - n))) & SIZE_MAX; +} + +/* Given a size_t argument X, return the value corresponding + to rotating the bits N steps to the right. N must be between 1 to + (CHAR_BIT * sizeof (size_t) - 1) inclusive. */ +BITROTATE_INLINE size_t +rotr_sz (size_t x, int n) +{ + return ((x >> n) | (x << ((CHAR_BIT * sizeof x) - n))) & SIZE_MAX; +} + +/* Given an unsigned 16-bit argument X, return the value corresponding + to rotating the bits N steps to the left. N must be between 1 to + 15 inclusive, but on most relevant targets N can also be 0 and 16 + because 'int' is at least 32 bits and the arguments must widen + before shifting. */ +BITROTATE_INLINE uint16_t +rotl16 (uint16_t x, int n) +{ + return ((x << n) | (x >> (16 - n))) & UINT16_MAX; +} + +/* Given an unsigned 16-bit argument X, return the value corresponding + to rotating the bits N steps to the right. N must be in 1 to 15 + inclusive, but on most relevant targets N can also be 0 and 16 + because 'int' is at least 32 bits and the arguments must widen + before shifting. */ +BITROTATE_INLINE uint16_t +rotr16 (uint16_t x, int n) +{ + return ((x >> n) | (x << (16 - n))) & UINT16_MAX; +} + +/* Given an unsigned 8-bit argument X, return the value corresponding + to rotating the bits N steps to the left. N must be between 1 to 7 + inclusive, but on most relevant targets N can also be 0 and 8 + because 'int' is at least 32 bits and the arguments must widen + before shifting. */ +BITROTATE_INLINE uint8_t +rotl8 (uint8_t x, int n) +{ + return ((x << n) | (x >> (8 - n))) & UINT8_MAX; +} + +/* Given an unsigned 8-bit argument X, return the value corresponding + to rotating the bits N steps to the right. N must be in 1 to 7 + inclusive, but on most relevant targets N can also be 0 and 8 + because 'int' is at least 32 bits and the arguments must widen + before shifting. */ +BITROTATE_INLINE uint8_t +rotr8 (uint8_t x, int n) +{ + return ((x >> n) | (x << (8 - n))) & UINT8_MAX; +} + +_GL_INLINE_HEADER_END + +#endif /* _GL_BITROTATE_H */ diff --git a/lib/hash.c b/lib/hash.c new file mode 100644 index 0000000..04f64d1 --- /dev/null +++ b/lib/hash.c @@ -0,0 +1,1225 @@ +/* hash - hashing table processing. + + Copyright (C) 1998-2004, 2006-2007, 2009-2017 Free Software Foundation, Inc. + + Written by Jim Meyering, 1992. + + This program is free software: 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* A generic hash table package. */ + +/* Define USE_OBSTACK to 1 if you want the allocator to use obstacks instead + of malloc. If you change USE_OBSTACK, you have to recompile! */ + +#include + +#include "hash.h" + +#include "bitrotate.h" +#include "xalloc-oversized.h" + +#include +#include +#include + +#if USE_OBSTACK +# include "obstack.h" +# ifndef obstack_chunk_alloc +# define obstack_chunk_alloc malloc +# endif +# ifndef obstack_chunk_free +# define obstack_chunk_free free +# endif +#endif + +struct hash_entry + { + void *data; + struct hash_entry *next; + }; + +struct hash_table + { + /* The array of buckets starts at BUCKET and extends to BUCKET_LIMIT-1, + for a possibility of N_BUCKETS. Among those, N_BUCKETS_USED buckets + are not empty, there are N_ENTRIES active entries in the table. */ + struct hash_entry *bucket; + struct hash_entry const *bucket_limit; + size_t n_buckets; + size_t n_buckets_used; + size_t n_entries; + + /* Tuning arguments, kept in a physically separate structure. */ + const Hash_tuning *tuning; + + /* Three functions are given to 'hash_initialize', see the documentation + block for this function. In a word, HASHER randomizes a user entry + into a number up from 0 up to some maximum minus 1; COMPARATOR returns + true if two user entries compare equally; and DATA_FREER is the cleanup + function for a user entry. */ + Hash_hasher hasher; + Hash_comparator comparator; + Hash_data_freer data_freer; + + /* A linked list of freed struct hash_entry structs. */ + struct hash_entry *free_entry_list; + +#if USE_OBSTACK + /* Whenever obstacks are used, it is possible to allocate all overflowed + entries into a single stack, so they all can be freed in a single + operation. It is not clear if the speedup is worth the trouble. */ + struct obstack entry_stack; +#endif + }; + +/* A hash table contains many internal entries, each holding a pointer to + some user-provided data (also called a user entry). An entry indistinctly + refers to both the internal entry and its associated user entry. A user + entry contents may be hashed by a randomization function (the hashing + function, or just "hasher" for short) into a number (or "slot") between 0 + and the current table size. At each slot position in the hash table, + starts a linked chain of entries for which the user data all hash to this + slot. A bucket is the collection of all entries hashing to the same slot. + + A good "hasher" function will distribute entries rather evenly in buckets. + In the ideal case, the length of each bucket is roughly the number of + entries divided by the table size. Finding the slot for a data is usually + done in constant time by the "hasher", and the later finding of a precise + entry is linear in time with the size of the bucket. Consequently, a + larger hash table size (that is, a larger number of buckets) is prone to + yielding shorter chains, *given* the "hasher" function behaves properly. + + Long buckets slow down the lookup algorithm. One might use big hash table + sizes in hope to reduce the average length of buckets, but this might + become inordinate, as unused slots in the hash table take some space. The + best bet is to make sure you are using a good "hasher" function (beware + that those are not that easy to write! :-), and to use a table size + larger than the actual number of entries. */ + +/* If an insertion makes the ratio of nonempty buckets to table size larger + than the growth threshold (a number between 0.0 and 1.0), then increase + the table size by multiplying by the growth factor (a number greater than + 1.0). The growth threshold defaults to 0.8, and the growth factor + defaults to 1.414, meaning that the table will have doubled its size + every second time 80% of the buckets get used. */ +#define DEFAULT_GROWTH_THRESHOLD 0.8f +#define DEFAULT_GROWTH_FACTOR 1.414f + +/* If a deletion empties a bucket and causes the ratio of used buckets to + table size to become smaller than the shrink threshold (a number between + 0.0 and 1.0), then shrink the table by multiplying by the shrink factor (a + number greater than the shrink threshold but smaller than 1.0). The shrink + threshold and factor default to 0.0 and 1.0, meaning that the table never + shrinks. */ +#define DEFAULT_SHRINK_THRESHOLD 0.0f +#define DEFAULT_SHRINK_FACTOR 1.0f + +/* Use this to initialize or reset a TUNING structure to + some sensible values. */ +static const Hash_tuning default_tuning = + { + DEFAULT_SHRINK_THRESHOLD, + DEFAULT_SHRINK_FACTOR, + DEFAULT_GROWTH_THRESHOLD, + DEFAULT_GROWTH_FACTOR, + false + }; + +/* Information and lookup. */ + +/* The following few functions provide information about the overall hash + table organization: the number of entries, number of buckets and maximum + length of buckets. */ + +/* Return the number of buckets in the hash table. The table size, the total + number of buckets (used plus unused), or the maximum number of slots, are + the same quantity. */ + +size_t +hash_get_n_buckets (const Hash_table *table) +{ + return table->n_buckets; +} + +/* Return the number of slots in use (non-empty buckets). */ + +size_t +hash_get_n_buckets_used (const Hash_table *table) +{ + return table->n_buckets_used; +} + +/* Return the number of active entries. */ + +size_t +hash_get_n_entries (const Hash_table *table) +{ + return table->n_entries; +} + +/* Return the length of the longest chain (bucket). */ + +size_t +hash_get_max_bucket_length (const Hash_table *table) +{ + struct hash_entry const *bucket; + size_t max_bucket_length = 0; + + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + struct hash_entry const *cursor = bucket; + size_t bucket_length = 1; + + while (cursor = cursor->next, cursor) + bucket_length++; + + if (bucket_length > max_bucket_length) + max_bucket_length = bucket_length; + } + } + + return max_bucket_length; +} + +/* Do a mild validation of a hash table, by traversing it and checking two + statistics. */ + +bool +hash_table_ok (const Hash_table *table) +{ + struct hash_entry const *bucket; + size_t n_buckets_used = 0; + size_t n_entries = 0; + + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + struct hash_entry const *cursor = bucket; + + /* Count bucket head. */ + n_buckets_used++; + n_entries++; + + /* Count bucket overflow. */ + while (cursor = cursor->next, cursor) + n_entries++; + } + } + + if (n_buckets_used == table->n_buckets_used && n_entries == table->n_entries) + return true; + + return false; +} + +void +hash_print_statistics (const Hash_table *table, FILE *stream) +{ + size_t n_entries = hash_get_n_entries (table); + size_t n_buckets = hash_get_n_buckets (table); + size_t n_buckets_used = hash_get_n_buckets_used (table); + size_t max_bucket_length = hash_get_max_bucket_length (table); + + fprintf (stream, "# entries: %lu\n", (unsigned long int) n_entries); + fprintf (stream, "# buckets: %lu\n", (unsigned long int) n_buckets); + fprintf (stream, "# buckets used: %lu (%.2f%%)\n", + (unsigned long int) n_buckets_used, + (100.0 * n_buckets_used) / n_buckets); + fprintf (stream, "max bucket length: %lu\n", + (unsigned long int) max_bucket_length); +} + +/* Hash KEY and return a pointer to the selected bucket. + If TABLE->hasher misbehaves, abort. */ +static struct hash_entry * +safe_hasher (const Hash_table *table, const void *key) +{ + size_t n = table->hasher (key, table->n_buckets); + if (! (n < table->n_buckets)) + abort (); + return table->bucket + n; +} + +/* If ENTRY matches an entry already in the hash table, return the + entry from the table. Otherwise, return NULL. */ + +void * +hash_lookup (const Hash_table *table, const void *entry) +{ + struct hash_entry const *bucket = safe_hasher (table, entry); + struct hash_entry const *cursor; + + if (bucket->data == NULL) + return NULL; + + for (cursor = bucket; cursor; cursor = cursor->next) + if (entry == cursor->data || table->comparator (entry, cursor->data)) + return cursor->data; + + return NULL; +} + +/* Walking. */ + +/* The functions in this page traverse the hash table and process the + contained entries. For the traversal to work properly, the hash table + should not be resized nor modified while any particular entry is being + processed. In particular, entries should not be added, and an entry + may be removed only if there is no shrink threshold and the entry being + removed has already been passed to hash_get_next. */ + +/* Return the first data in the table, or NULL if the table is empty. */ + +void * +hash_get_first (const Hash_table *table) +{ + struct hash_entry const *bucket; + + if (table->n_entries == 0) + return NULL; + + for (bucket = table->bucket; ; bucket++) + if (! (bucket < table->bucket_limit)) + abort (); + else if (bucket->data) + return bucket->data; +} + +/* Return the user data for the entry following ENTRY, where ENTRY has been + returned by a previous call to either 'hash_get_first' or 'hash_get_next'. + Return NULL if there are no more entries. */ + +void * +hash_get_next (const Hash_table *table, const void *entry) +{ + struct hash_entry const *bucket = safe_hasher (table, entry); + struct hash_entry const *cursor; + + /* Find next entry in the same bucket. */ + cursor = bucket; + do + { + if (cursor->data == entry && cursor->next) + return cursor->next->data; + cursor = cursor->next; + } + while (cursor != NULL); + + /* Find first entry in any subsequent bucket. */ + while (++bucket < table->bucket_limit) + if (bucket->data) + return bucket->data; + + /* None found. */ + return NULL; +} + +/* Fill BUFFER with pointers to active user entries in the hash table, then + return the number of pointers copied. Do not copy more than BUFFER_SIZE + pointers. */ + +size_t +hash_get_entries (const Hash_table *table, void **buffer, + size_t buffer_size) +{ + size_t counter = 0; + struct hash_entry const *bucket; + struct hash_entry const *cursor; + + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + for (cursor = bucket; cursor; cursor = cursor->next) + { + if (counter >= buffer_size) + return counter; + buffer[counter++] = cursor->data; + } + } + } + + return counter; +} + +/* Call a PROCESSOR function for each entry of a hash table, and return the + number of entries for which the processor function returned success. A + pointer to some PROCESSOR_DATA which will be made available to each call to + the processor function. The PROCESSOR accepts two arguments: the first is + the user entry being walked into, the second is the value of PROCESSOR_DATA + as received. The walking continue for as long as the PROCESSOR function + returns nonzero. When it returns zero, the walking is interrupted. */ + +size_t +hash_do_for_each (const Hash_table *table, Hash_processor processor, + void *processor_data) +{ + size_t counter = 0; + struct hash_entry const *bucket; + struct hash_entry const *cursor; + + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + for (cursor = bucket; cursor; cursor = cursor->next) + { + if (! processor (cursor->data, processor_data)) + return counter; + counter++; + } + } + } + + return counter; +} + +/* Allocation and clean-up. */ + +/* Return a hash index for a NUL-terminated STRING between 0 and N_BUCKETS-1. + This is a convenience routine for constructing other hashing functions. */ + +#if USE_DIFF_HASH + +/* About hashings, Paul Eggert writes to me (FP), on 1994-01-01: "Please see + B. J. McKenzie, R. Harries & T. Bell, Selecting a hashing algorithm, + Software--practice & experience 20, 2 (Feb 1990), 209-224. Good hash + algorithms tend to be domain-specific, so what's good for [diffutils'] io.c + may not be good for your application." */ + +size_t +hash_string (const char *string, size_t n_buckets) +{ +# define HASH_ONE_CHAR(Value, Byte) \ + ((Byte) + rotl_sz (Value, 7)) + + size_t value = 0; + unsigned char ch; + + for (; (ch = *string); string++) + value = HASH_ONE_CHAR (value, ch); + return value % n_buckets; + +# undef HASH_ONE_CHAR +} + +#else /* not USE_DIFF_HASH */ + +/* This one comes from 'recode', and performs a bit better than the above as + per a few experiments. It is inspired from a hashing routine found in the + very old Cyber 'snoop', itself written in typical Greg Mansfield style. + (By the way, what happened to this excellent man? Is he still alive?) */ + +size_t +hash_string (const char *string, size_t n_buckets) +{ + size_t value = 0; + unsigned char ch; + + for (; (ch = *string); string++) + value = (value * 31 + ch) % n_buckets; + return value; +} + +#endif /* not USE_DIFF_HASH */ + +/* Return true if CANDIDATE is a prime number. CANDIDATE should be an odd + number at least equal to 11. */ + +static bool _GL_ATTRIBUTE_CONST +is_prime (size_t candidate) +{ + size_t divisor = 3; + size_t square = divisor * divisor; + + while (square < candidate && (candidate % divisor)) + { + divisor++; + square += 4 * divisor; + divisor++; + } + + return (candidate % divisor ? true : false); +} + +/* Round a given CANDIDATE number up to the nearest prime, and return that + prime. Primes lower than 10 are merely skipped. */ + +static size_t _GL_ATTRIBUTE_CONST +next_prime (size_t candidate) +{ + /* Skip small primes. */ + if (candidate < 10) + candidate = 10; + + /* Make it definitely odd. */ + candidate |= 1; + + while (SIZE_MAX != candidate && !is_prime (candidate)) + candidate += 2; + + return candidate; +} + +void +hash_reset_tuning (Hash_tuning *tuning) +{ + *tuning = default_tuning; +} + +/* If the user passes a NULL hasher, we hash the raw pointer. */ +static size_t +raw_hasher (const void *data, size_t n) +{ + /* When hashing unique pointers, it is often the case that they were + generated by malloc and thus have the property that the low-order + bits are 0. As this tends to give poorer performance with small + tables, we rotate the pointer value before performing division, + in an attempt to improve hash quality. */ + size_t val = rotr_sz ((size_t) data, 3); + return val % n; +} + +/* If the user passes a NULL comparator, we use pointer comparison. */ +static bool +raw_comparator (const void *a, const void *b) +{ + return a == b; +} + + +/* For the given hash TABLE, check the user supplied tuning structure for + reasonable values, and return true if there is no gross error with it. + Otherwise, definitively reset the TUNING field to some acceptable default + in the hash table (that is, the user loses the right of further modifying + tuning arguments), and return false. */ + +static bool +check_tuning (Hash_table *table) +{ + const Hash_tuning *tuning = table->tuning; + float epsilon; + if (tuning == &default_tuning) + return true; + + /* Be a bit stricter than mathematics would require, so that + rounding errors in size calculations do not cause allocations to + fail to grow or shrink as they should. The smallest allocation + is 11 (due to next_prime's algorithm), so an epsilon of 0.1 + should be good enough. */ + epsilon = 0.1f; + + if (epsilon < tuning->growth_threshold + && tuning->growth_threshold < 1 - epsilon + && 1 + epsilon < tuning->growth_factor + && 0 <= tuning->shrink_threshold + && tuning->shrink_threshold + epsilon < tuning->shrink_factor + && tuning->shrink_factor <= 1 + && tuning->shrink_threshold + epsilon < tuning->growth_threshold) + return true; + + table->tuning = &default_tuning; + return false; +} + +/* Compute the size of the bucket array for the given CANDIDATE and + TUNING, or return 0 if there is no possible way to allocate that + many entries. */ + +static size_t _GL_ATTRIBUTE_PURE +compute_bucket_size (size_t candidate, const Hash_tuning *tuning) +{ + if (!tuning->is_n_buckets) + { + float new_candidate = candidate / tuning->growth_threshold; + if (SIZE_MAX <= new_candidate) + return 0; + candidate = new_candidate; + } + candidate = next_prime (candidate); + if (xalloc_oversized (candidate, sizeof (struct hash_entry *))) + return 0; + return candidate; +} + +/* Allocate and return a new hash table, or NULL upon failure. The initial + number of buckets is automatically selected so as to _guarantee_ that you + may insert at least CANDIDATE different user entries before any growth of + the hash table size occurs. So, if have a reasonably tight a-priori upper + bound on the number of entries you intend to insert in the hash table, you + may save some table memory and insertion time, by specifying it here. If + the IS_N_BUCKETS field of the TUNING structure is true, the CANDIDATE + argument has its meaning changed to the wanted number of buckets. + + TUNING points to a structure of user-supplied values, in case some fine + tuning is wanted over the default behavior of the hasher. If TUNING is + NULL, the default tuning parameters are used instead. If TUNING is + provided but the values requested are out of bounds or might cause + rounding errors, return NULL. + + The user-supplied HASHER function, when not NULL, accepts two + arguments ENTRY and TABLE_SIZE. It computes, by hashing ENTRY contents, a + slot number for that entry which should be in the range 0..TABLE_SIZE-1. + This slot number is then returned. + + The user-supplied COMPARATOR function, when not NULL, accepts two + arguments pointing to user data, it then returns true for a pair of entries + that compare equal, or false otherwise. This function is internally called + on entries which are already known to hash to the same bucket index, + but which are distinct pointers. + + The user-supplied DATA_FREER function, when not NULL, may be later called + with the user data as an argument, just before the entry containing the + data gets freed. This happens from within 'hash_free' or 'hash_clear'. + You should specify this function only if you want these functions to free + all of your 'data' data. This is typically the case when your data is + simply an auxiliary struct that you have malloc'd to aggregate several + values. */ + +Hash_table * +hash_initialize (size_t candidate, const Hash_tuning *tuning, + Hash_hasher hasher, Hash_comparator comparator, + Hash_data_freer data_freer) +{ + Hash_table *table; + + if (hasher == NULL) + hasher = raw_hasher; + if (comparator == NULL) + comparator = raw_comparator; + + table = malloc (sizeof *table); + if (table == NULL) + return NULL; + + if (!tuning) + tuning = &default_tuning; + table->tuning = tuning; + if (!check_tuning (table)) + { + /* Fail if the tuning options are invalid. This is the only occasion + when the user gets some feedback about it. Once the table is created, + if the user provides invalid tuning options, we silently revert to + using the defaults, and ignore further request to change the tuning + options. */ + goto fail; + } + + table->n_buckets = compute_bucket_size (candidate, tuning); + if (!table->n_buckets) + goto fail; + + table->bucket = calloc (table->n_buckets, sizeof *table->bucket); + if (table->bucket == NULL) + goto fail; + table->bucket_limit = table->bucket + table->n_buckets; + table->n_buckets_used = 0; + table->n_entries = 0; + + table->hasher = hasher; + table->comparator = comparator; + table->data_freer = data_freer; + + table->free_entry_list = NULL; +#if USE_OBSTACK + obstack_init (&table->entry_stack); +#endif + return table; + + fail: + free (table); + return NULL; +} + +/* Make all buckets empty, placing any chained entries on the free list. + Apply the user-specified function data_freer (if any) to the datas of any + affected entries. */ + +void +hash_clear (Hash_table *table) +{ + struct hash_entry *bucket; + + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + struct hash_entry *cursor; + struct hash_entry *next; + + /* Free the bucket overflow. */ + for (cursor = bucket->next; cursor; cursor = next) + { + if (table->data_freer) + table->data_freer (cursor->data); + cursor->data = NULL; + + next = cursor->next; + /* Relinking is done one entry at a time, as it is to be expected + that overflows are either rare or short. */ + cursor->next = table->free_entry_list; + table->free_entry_list = cursor; + } + + /* Free the bucket head. */ + if (table->data_freer) + table->data_freer (bucket->data); + bucket->data = NULL; + bucket->next = NULL; + } + } + + table->n_buckets_used = 0; + table->n_entries = 0; +} + +/* Reclaim all storage associated with a hash table. If a data_freer + function has been supplied by the user when the hash table was created, + this function applies it to the data of each entry before freeing that + entry. */ + +void +hash_free (Hash_table *table) +{ + struct hash_entry *bucket; + struct hash_entry *cursor; + struct hash_entry *next; + + /* Call the user data_freer function. */ + if (table->data_freer && table->n_entries) + { + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + if (bucket->data) + { + for (cursor = bucket; cursor; cursor = cursor->next) + table->data_freer (cursor->data); + } + } + } + +#if USE_OBSTACK + + obstack_free (&table->entry_stack, NULL); + +#else + + /* Free all bucket overflowed entries. */ + for (bucket = table->bucket; bucket < table->bucket_limit; bucket++) + { + for (cursor = bucket->next; cursor; cursor = next) + { + next = cursor->next; + free (cursor); + } + } + + /* Also reclaim the internal list of previously freed entries. */ + for (cursor = table->free_entry_list; cursor; cursor = next) + { + next = cursor->next; + free (cursor); + } + +#endif + + /* Free the remainder of the hash table structure. */ + free (table->bucket); + free (table); +} + +/* Insertion and deletion. */ + +/* Get a new hash entry for a bucket overflow, possibly by recycling a + previously freed one. If this is not possible, allocate a new one. */ + +static struct hash_entry * +allocate_entry (Hash_table *table) +{ + struct hash_entry *new; + + if (table->free_entry_list) + { + new = table->free_entry_list; + table->free_entry_list = new->next; + } + else + { +#if USE_OBSTACK + new = obstack_alloc (&table->entry_stack, sizeof *new); +#else + new = malloc (sizeof *new); +#endif + } + + return new; +} + +/* Free a hash entry which was part of some bucket overflow, + saving it for later recycling. */ + +static void +free_entry (Hash_table *table, struct hash_entry *entry) +{ + entry->data = NULL; + entry->next = table->free_entry_list; + table->free_entry_list = entry; +} + +/* This private function is used to help with insertion and deletion. When + ENTRY matches an entry in the table, return a pointer to the corresponding + user data and set *BUCKET_HEAD to the head of the selected bucket. + Otherwise, return NULL. When DELETE is true and ENTRY matches an entry in + the table, unlink the matching entry. */ + +static void * +hash_find_entry (Hash_table *table, const void *entry, + struct hash_entry **bucket_head, bool delete) +{ + struct hash_entry *bucket = safe_hasher (table, entry); + struct hash_entry *cursor; + + *bucket_head = bucket; + + /* Test for empty bucket. */ + if (bucket->data == NULL) + return NULL; + + /* See if the entry is the first in the bucket. */ + if (entry == bucket->data || table->comparator (entry, bucket->data)) + { + void *data = bucket->data; + + if (delete) + { + if (bucket->next) + { + struct hash_entry *next = bucket->next; + + /* Bump the first overflow entry into the bucket head, then save + the previous first overflow entry for later recycling. */ + *bucket = *next; + free_entry (table, next); + } + else + { + bucket->data = NULL; + } + } + + return data; + } + + /* Scan the bucket overflow. */ + for (cursor = bucket; cursor->next; cursor = cursor->next) + { + if (entry == cursor->next->data + || table->comparator (entry, cursor->next->data)) + { + void *data = cursor->next->data; + + if (delete) + { + struct hash_entry *next = cursor->next; + + /* Unlink the entry to delete, then save the freed entry for later + recycling. */ + cursor->next = next->next; + free_entry (table, next); + } + + return data; + } + } + + /* No entry found. */ + return NULL; +} + +/* Internal helper, to move entries from SRC to DST. Both tables must + share the same free entry list. If SAFE, only move overflow + entries, saving bucket heads for later, so that no allocations will + occur. Return false if the free entry list is exhausted and an + allocation fails. */ + +static bool +transfer_entries (Hash_table *dst, Hash_table *src, bool safe) +{ + struct hash_entry *bucket; + struct hash_entry *cursor; + struct hash_entry *next; + for (bucket = src->bucket; bucket < src->bucket_limit; bucket++) + if (bucket->data) + { + void *data; + struct hash_entry *new_bucket; + + /* Within each bucket, transfer overflow entries first and + then the bucket head, to minimize memory pressure. After + all, the only time we might allocate is when moving the + bucket head, but moving overflow entries first may create + free entries that can be recycled by the time we finally + get to the bucket head. */ + for (cursor = bucket->next; cursor; cursor = next) + { + data = cursor->data; + new_bucket = safe_hasher (dst, data); + + next = cursor->next; + + if (new_bucket->data) + { + /* Merely relink an existing entry, when moving from a + bucket overflow into a bucket overflow. */ + cursor->next = new_bucket->next; + new_bucket->next = cursor; + } + else + { + /* Free an existing entry, when moving from a bucket + overflow into a bucket header. */ + new_bucket->data = data; + dst->n_buckets_used++; + free_entry (dst, cursor); + } + } + /* Now move the bucket head. Be sure that if we fail due to + allocation failure that the src table is in a consistent + state. */ + data = bucket->data; + bucket->next = NULL; + if (safe) + continue; + new_bucket = safe_hasher (dst, data); + + if (new_bucket->data) + { + /* Allocate or recycle an entry, when moving from a bucket + header into a bucket overflow. */ + struct hash_entry *new_entry = allocate_entry (dst); + + if (new_entry == NULL) + return false; + + new_entry->data = data; + new_entry->next = new_bucket->next; + new_bucket->next = new_entry; + } + else + { + /* Move from one bucket header to another. */ + new_bucket->data = data; + dst->n_buckets_used++; + } + bucket->data = NULL; + src->n_buckets_used--; + } + return true; +} + +/* For an already existing hash table, change the number of buckets through + specifying CANDIDATE. The contents of the hash table are preserved. The + new number of buckets is automatically selected so as to _guarantee_ that + the table may receive at least CANDIDATE different user entries, including + those already in the table, before any other growth of the hash table size + occurs. If TUNING->IS_N_BUCKETS is true, then CANDIDATE specifies the + exact number of buckets desired. Return true iff the rehash succeeded. */ + +bool +hash_rehash (Hash_table *table, size_t candidate) +{ + Hash_table storage; + Hash_table *new_table; + size_t new_size = compute_bucket_size (candidate, table->tuning); + + if (!new_size) + return false; + if (new_size == table->n_buckets) + return true; + new_table = &storage; + new_table->bucket = calloc (new_size, sizeof *new_table->bucket); + if (new_table->bucket == NULL) + return false; + new_table->n_buckets = new_size; + new_table->bucket_limit = new_table->bucket + new_size; + new_table->n_buckets_used = 0; + new_table->n_entries = 0; + new_table->tuning = table->tuning; + new_table->hasher = table->hasher; + new_table->comparator = table->comparator; + new_table->data_freer = table->data_freer; + + /* In order for the transfer to successfully complete, we need + additional overflow entries when distinct buckets in the old + table collide into a common bucket in the new table. The worst + case possible is a hasher that gives a good spread with the old + size, but returns a constant with the new size; if we were to + guarantee table->n_buckets_used-1 free entries in advance, then + the transfer would be guaranteed to not allocate memory. + However, for large tables, a guarantee of no further allocation + introduces a lot of extra memory pressure, all for an unlikely + corner case (most rehashes reduce, rather than increase, the + number of overflow entries needed). So, we instead ensure that + the transfer process can be reversed if we hit a memory + allocation failure mid-transfer. */ + + /* Merely reuse the extra old space into the new table. */ +#if USE_OBSTACK + new_table->entry_stack = table->entry_stack; +#endif + new_table->free_entry_list = table->free_entry_list; + + if (transfer_entries (new_table, table, false)) + { + /* Entries transferred successfully; tie up the loose ends. */ + free (table->bucket); + table->bucket = new_table->bucket; + table->bucket_limit = new_table->bucket_limit; + table->n_buckets = new_table->n_buckets; + table->n_buckets_used = new_table->n_buckets_used; + table->free_entry_list = new_table->free_entry_list; + /* table->n_entries and table->entry_stack already hold their value. */ + return true; + } + + /* We've allocated new_table->bucket (and possibly some entries), + exhausted the free list, and moved some but not all entries into + new_table. We must undo the partial move before returning + failure. The only way to get into this situation is if new_table + uses fewer buckets than the old table, so we will reclaim some + free entries as overflows in the new table are put back into + distinct buckets in the old table. + + There are some pathological cases where a single pass through the + table requires more intermediate overflow entries than using two + passes. Two passes give worse cache performance and takes + longer, but at this point, we're already out of memory, so slow + and safe is better than failure. */ + table->free_entry_list = new_table->free_entry_list; + if (! (transfer_entries (table, new_table, true) + && transfer_entries (table, new_table, false))) + abort (); + /* table->n_entries already holds its value. */ + free (new_table->bucket); + return false; +} + +/* Insert ENTRY into hash TABLE if there is not already a matching entry. + + Return -1 upon memory allocation failure. + Return 1 if insertion succeeded. + Return 0 if there is already a matching entry in the table, + and in that case, if MATCHED_ENT is non-NULL, set *MATCHED_ENT + to that entry. + + This interface is easier to use than hash_insert when you must + distinguish between the latter two cases. More importantly, + hash_insert is unusable for some types of ENTRY values. When using + hash_insert, the only way to distinguish those cases is to compare + the return value and ENTRY. That works only when you can have two + different ENTRY values that point to data that compares "equal". Thus, + when the ENTRY value is a simple scalar, you must use + hash_insert_if_absent. ENTRY must not be NULL. */ +int +hash_insert_if_absent (Hash_table *table, void const *entry, + void const **matched_ent) +{ + void *data; + struct hash_entry *bucket; + + /* The caller cannot insert a NULL entry, since hash_lookup returns NULL + to indicate "not found", and hash_find_entry uses "bucket->data == NULL" + to indicate an empty bucket. */ + if (! entry) + abort (); + + /* If there's a matching entry already in the table, return that. */ + if ((data = hash_find_entry (table, entry, &bucket, false)) != NULL) + { + if (matched_ent) + *matched_ent = data; + return 0; + } + + /* If the growth threshold of the buckets in use has been reached, increase + the table size and rehash. There's no point in checking the number of + entries: if the hashing function is ill-conditioned, rehashing is not + likely to improve it. */ + + if (table->n_buckets_used + > table->tuning->growth_threshold * table->n_buckets) + { + /* Check more fully, before starting real work. If tuning arguments + became invalid, the second check will rely on proper defaults. */ + check_tuning (table); + if (table->n_buckets_used + > table->tuning->growth_threshold * table->n_buckets) + { + const Hash_tuning *tuning = table->tuning; + float candidate = + (tuning->is_n_buckets + ? (table->n_buckets * tuning->growth_factor) + : (table->n_buckets * tuning->growth_factor + * tuning->growth_threshold)); + + if (SIZE_MAX <= candidate) + return -1; + + /* If the rehash fails, arrange to return NULL. */ + if (!hash_rehash (table, candidate)) + return -1; + + /* Update the bucket we are interested in. */ + if (hash_find_entry (table, entry, &bucket, false) != NULL) + abort (); + } + } + + /* ENTRY is not matched, it should be inserted. */ + + if (bucket->data) + { + struct hash_entry *new_entry = allocate_entry (table); + + if (new_entry == NULL) + return -1; + + /* Add ENTRY in the overflow of the bucket. */ + + new_entry->data = (void *) entry; + new_entry->next = bucket->next; + bucket->next = new_entry; + table->n_entries++; + return 1; + } + + /* Add ENTRY right in the bucket head. */ + + bucket->data = (void *) entry; + table->n_entries++; + table->n_buckets_used++; + + return 1; +} + +/* If ENTRY matches an entry already in the hash table, return the pointer + to the entry from the table. Otherwise, insert ENTRY and return ENTRY. + Return NULL if the storage required for insertion cannot be allocated. + This implementation does not support duplicate entries or insertion of + NULL. */ + +void * +hash_insert (Hash_table *table, void const *entry) +{ + void const *matched_ent; + int err = hash_insert_if_absent (table, entry, &matched_ent); + return (err == -1 + ? NULL + : (void *) (err == 0 ? matched_ent : entry)); +} + +/* If ENTRY is already in the table, remove it and return the just-deleted + data (the user may want to deallocate its storage). If ENTRY is not in the + table, don't modify the table and return NULL. */ + +void * +hash_delete (Hash_table *table, const void *entry) +{ + void *data; + struct hash_entry *bucket; + + data = hash_find_entry (table, entry, &bucket, true); + if (!data) + return NULL; + + table->n_entries--; + if (!bucket->data) + { + table->n_buckets_used--; + + /* If the shrink threshold of the buckets in use has been reached, + rehash into a smaller table. */ + + if (table->n_buckets_used + < table->tuning->shrink_threshold * table->n_buckets) + { + /* Check more fully, before starting real work. If tuning arguments + became invalid, the second check will rely on proper defaults. */ + check_tuning (table); + if (table->n_buckets_used + < table->tuning->shrink_threshold * table->n_buckets) + { + const Hash_tuning *tuning = table->tuning; + size_t candidate = + (tuning->is_n_buckets + ? table->n_buckets * tuning->shrink_factor + : (table->n_buckets * tuning->shrink_factor + * tuning->growth_threshold)); + + if (!hash_rehash (table, candidate)) + { + /* Failure to allocate memory in an attempt to + shrink the table is not fatal. But since memory + is low, we can at least be kind and free any + spare entries, rather than keeping them tied up + in the free entry list. */ +#if ! USE_OBSTACK + struct hash_entry *cursor = table->free_entry_list; + struct hash_entry *next; + while (cursor) + { + next = cursor->next; + free (cursor); + cursor = next; + } + table->free_entry_list = NULL; +#endif + } + } + } + } + + return data; +} + +/* Testing. */ + +#if TESTING + +void +hash_print (const Hash_table *table) +{ + struct hash_entry *bucket = (struct hash_entry *) table->bucket; + + for ( ; bucket < table->bucket_limit; bucket++) + { + struct hash_entry *cursor; + + if (bucket) + printf ("%lu:\n", (unsigned long int) (bucket - table->bucket)); + + for (cursor = bucket; cursor; cursor = cursor->next) + { + char const *s = cursor->data; + /* FIXME */ + if (s) + printf (" %s\n", s); + } + } +} + +#endif /* TESTING */ diff --git a/lib/hash.h b/lib/hash.h new file mode 100644 index 0000000..cc5e2b4 --- /dev/null +++ b/lib/hash.h @@ -0,0 +1,103 @@ +/* hash - hashing table processing. + Copyright (C) 1998-1999, 2001, 2003, 2009-2017 Free Software Foundation, + Inc. + Written by Jim Meyering , 1998. + + This program is free software: 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* A generic hash table package. */ + +/* Make sure USE_OBSTACK is defined to 1 if you want the allocator to use + obstacks instead of malloc, and recompile 'hash.c' with same setting. */ + +#ifndef HASH_H_ +# define HASH_H_ + +# include +# include + +/* The __attribute__ feature is available in gcc versions 2.5 and later. + The warn_unused_result attribute appeared first in gcc-3.4.0. */ +# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# define _GL_ATTRIBUTE_WUR __attribute__ ((__warn_unused_result__)) +# else +# define _GL_ATTRIBUTE_WUR /* empty */ +# endif + +# ifndef _GL_ATTRIBUTE_DEPRECATED +/* The __attribute__((__deprecated__)) feature + is available in gcc versions 3.1 and newer. */ +# if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 1) +# define _GL_ATTRIBUTE_DEPRECATED /* empty */ +# else +# define _GL_ATTRIBUTE_DEPRECATED __attribute__ ((__deprecated__)) +# endif +# endif + +typedef size_t (*Hash_hasher) (const void *, size_t); +typedef bool (*Hash_comparator) (const void *, const void *); +typedef void (*Hash_data_freer) (void *); +typedef bool (*Hash_processor) (void *, void *); + +struct hash_tuning + { + /* This structure is mainly used for 'hash_initialize', see the block + documentation of 'hash_reset_tuning' for more complete comments. */ + + float shrink_threshold; /* ratio of used buckets to trigger a shrink */ + float shrink_factor; /* ratio of new smaller size to original size */ + float growth_threshold; /* ratio of used buckets to trigger a growth */ + float growth_factor; /* ratio of new bigger size to original size */ + bool is_n_buckets; /* if CANDIDATE really means table size */ + }; + +typedef struct hash_tuning Hash_tuning; + +struct hash_table; + +typedef struct hash_table Hash_table; + +/* Information and lookup. */ +size_t hash_get_n_buckets (const Hash_table *) _GL_ATTRIBUTE_PURE; +size_t hash_get_n_buckets_used (const Hash_table *) _GL_ATTRIBUTE_PURE; +size_t hash_get_n_entries (const Hash_table *) _GL_ATTRIBUTE_PURE; +size_t hash_get_max_bucket_length (const Hash_table *) _GL_ATTRIBUTE_PURE; +bool hash_table_ok (const Hash_table *) _GL_ATTRIBUTE_PURE; +void hash_print_statistics (const Hash_table *, FILE *); +void *hash_lookup (const Hash_table *, const void *); + +/* Walking. */ +void *hash_get_first (const Hash_table *) _GL_ATTRIBUTE_PURE; +void *hash_get_next (const Hash_table *, const void *); +size_t hash_get_entries (const Hash_table *, void **, size_t); +size_t hash_do_for_each (const Hash_table *, Hash_processor, void *); + +/* Allocation and clean-up. */ +size_t hash_string (const char *, size_t) _GL_ATTRIBUTE_PURE; +void hash_reset_tuning (Hash_tuning *); +Hash_table *hash_initialize (size_t, const Hash_tuning *, + Hash_hasher, Hash_comparator, + Hash_data_freer) _GL_ATTRIBUTE_WUR; +void hash_clear (Hash_table *); +void hash_free (Hash_table *); + +/* Insertion and deletion. */ +bool hash_rehash (Hash_table *, size_t) _GL_ATTRIBUTE_WUR; +void *hash_insert (Hash_table *, const void *) _GL_ATTRIBUTE_WUR; + +int hash_insert_if_absent (Hash_table *table, const void *entry, + const void **matched_ent); +void *hash_delete (Hash_table *, const void *); + +#endif diff --git a/lib/limits.h b/lib/limits.h new file mode 100644 index 0000000..1a27b22 --- /dev/null +++ b/lib/limits.h @@ -0,0 +1,75 @@ +/* DO NOT EDIT! GENERATED AUTOMATICALLY! */ +/* A GNU-like . + + Copyright 2016-2017 Free Software Foundation, Inc. + + This program is free software; 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, 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, see . */ + +#ifndef _GL_LIMITS_H + +#if __GNUC__ >= 3 +#pragma GCC system_header +#endif + + +/* The include_next requires a split double-inclusion guard. */ +#include_next + +#ifndef _GL_LIMITS_H +#define _GL_LIMITS_H + +/* For HP-UX 11.31. */ +#if defined LONG_LONG_MIN && !defined LLONG_MIN +# define LLONG_MIN LONG_LONG_MIN +#endif +#if defined LONG_LONG_MAX && !defined LLONG_MAX +# define LLONG_MAX LONG_LONG_MAX +#endif +#if defined ULONG_LONG_MAX && !defined ULLONG_MAX +# define ULLONG_MAX ULONG_LONG_MAX +#endif + +/* The number of usable bits in an unsigned or signed integer type + with minimum value MIN and maximum value MAX, as an int expression + suitable in #if. Cover all known practical hosts. This + implementation exploits the fact that MAX is 1 less than a power of + 2, and merely counts the number of 1 bits in MAX; "COBn" means + "count the number of 1 bits in the low-order n bits"). */ +#define _GL_INTEGER_WIDTH(min, max) (((min) < 0) + _GL_COB128 (max)) +#define _GL_COB128(n) (_GL_COB64 ((n) >> 31 >> 31 >> 2) + _GL_COB64 (n)) +#define _GL_COB64(n) (_GL_COB32 ((n) >> 31 >> 1) + _GL_COB32 (n)) +#define _GL_COB32(n) (_GL_COB16 ((n) >> 16) + _GL_COB16 (n)) +#define _GL_COB16(n) (_GL_COB8 ((n) >> 8) + _GL_COB8 (n)) +#define _GL_COB8(n) (_GL_COB4 ((n) >> 4) + _GL_COB4 (n)) +#define _GL_COB4(n) (!!((n) & 8) + !!((n) & 4) + !!((n) & 2) + !!((n) & 1)) + +/* Macros specified by ISO/IEC TS 18661-1:2014. */ + +#if (! defined ULLONG_WIDTH \ + && (defined _GNU_SOURCE || defined __STDC_WANT_IEC_60559_BFP_EXT__)) +# define CHAR_WIDTH _GL_INTEGER_WIDTH (CHAR_MIN, CHAR_MAX) +# define SCHAR_WIDTH _GL_INTEGER_WIDTH (SCHAR_MIN, SCHAR_MAX) +# define UCHAR_WIDTH _GL_INTEGER_WIDTH (0, UCHAR_MAX) +# define SHRT_WIDTH _GL_INTEGER_WIDTH (SHRT_MIN, SHRT_MAX) +# define USHRT_WIDTH _GL_INTEGER_WIDTH (0, USHRT_MAX) +# define INT_WIDTH _GL_INTEGER_WIDTH (INT_MIN, INT_MAX) +# define UINT_WIDTH _GL_INTEGER_WIDTH (0, UINT_MAX) +# define LONG_WIDTH _GL_INTEGER_WIDTH (LONG_MIN, LONG_MAX) +# define ULONG_WIDTH _GL_INTEGER_WIDTH (0, ULONG_MAX) +# define LLONG_WIDTH _GL_INTEGER_WIDTH (LLONG_MIN, LLONG_MAX) +# define ULLONG_WIDTH _GL_INTEGER_WIDTH (0, ULLONG_MAX) +#endif /* !ULLONG_WIDTH && (_GNU_SOURCE || __STDC_WANT_IEC_60559_BFP_EXT__) */ + +#endif /* _GL_LIMITS_H */ +#endif /* _GL_LIMITS_H */ diff --git a/lib/limits.in.h b/lib/limits.in.h new file mode 100644 index 0000000..08d3c32 --- /dev/null +++ b/lib/limits.in.h @@ -0,0 +1,74 @@ +/* A GNU-like . + + Copyright 2016-2017 Free Software Foundation, Inc. + + This program is free software; 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, 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, see . */ + +#ifndef _@GUARD_PREFIX@_LIMITS_H + +#if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +#endif +@PRAGMA_COLUMNS@ + +/* The include_next requires a split double-inclusion guard. */ +#@INCLUDE_NEXT@ @NEXT_LIMITS_H@ + +#ifndef _@GUARD_PREFIX@_LIMITS_H +#define _@GUARD_PREFIX@_LIMITS_H + +/* For HP-UX 11.31. */ +#if defined LONG_LONG_MIN && !defined LLONG_MIN +# define LLONG_MIN LONG_LONG_MIN +#endif +#if defined LONG_LONG_MAX && !defined LLONG_MAX +# define LLONG_MAX LONG_LONG_MAX +#endif +#if defined ULONG_LONG_MAX && !defined ULLONG_MAX +# define ULLONG_MAX ULONG_LONG_MAX +#endif + +/* The number of usable bits in an unsigned or signed integer type + with minimum value MIN and maximum value MAX, as an int expression + suitable in #if. Cover all known practical hosts. This + implementation exploits the fact that MAX is 1 less than a power of + 2, and merely counts the number of 1 bits in MAX; "COBn" means + "count the number of 1 bits in the low-order n bits"). */ +#define _GL_INTEGER_WIDTH(min, max) (((min) < 0) + _GL_COB128 (max)) +#define _GL_COB128(n) (_GL_COB64 ((n) >> 31 >> 31 >> 2) + _GL_COB64 (n)) +#define _GL_COB64(n) (_GL_COB32 ((n) >> 31 >> 1) + _GL_COB32 (n)) +#define _GL_COB32(n) (_GL_COB16 ((n) >> 16) + _GL_COB16 (n)) +#define _GL_COB16(n) (_GL_COB8 ((n) >> 8) + _GL_COB8 (n)) +#define _GL_COB8(n) (_GL_COB4 ((n) >> 4) + _GL_COB4 (n)) +#define _GL_COB4(n) (!!((n) & 8) + !!((n) & 4) + !!((n) & 2) + !!((n) & 1)) + +/* Macros specified by ISO/IEC TS 18661-1:2014. */ + +#if (! defined ULLONG_WIDTH \ + && (defined _GNU_SOURCE || defined __STDC_WANT_IEC_60559_BFP_EXT__)) +# define CHAR_WIDTH _GL_INTEGER_WIDTH (CHAR_MIN, CHAR_MAX) +# define SCHAR_WIDTH _GL_INTEGER_WIDTH (SCHAR_MIN, SCHAR_MAX) +# define UCHAR_WIDTH _GL_INTEGER_WIDTH (0, UCHAR_MAX) +# define SHRT_WIDTH _GL_INTEGER_WIDTH (SHRT_MIN, SHRT_MAX) +# define USHRT_WIDTH _GL_INTEGER_WIDTH (0, USHRT_MAX) +# define INT_WIDTH _GL_INTEGER_WIDTH (INT_MIN, INT_MAX) +# define UINT_WIDTH _GL_INTEGER_WIDTH (0, UINT_MAX) +# define LONG_WIDTH _GL_INTEGER_WIDTH (LONG_MIN, LONG_MAX) +# define ULONG_WIDTH _GL_INTEGER_WIDTH (0, ULONG_MAX) +# define LLONG_WIDTH _GL_INTEGER_WIDTH (LLONG_MIN, LLONG_MAX) +# define ULLONG_WIDTH _GL_INTEGER_WIDTH (0, ULLONG_MAX) +#endif /* !ULLONG_WIDTH && (_GNU_SOURCE || __STDC_WANT_IEC_60559_BFP_EXT__) */ + +#endif /* _@GUARD_PREFIX@_LIMITS_H */ +#endif /* _@GUARD_PREFIX@_LIMITS_H */ diff --git a/lib/stdbool.in.h b/lib/stdbool.in.h new file mode 100644 index 0000000..1067ed2 --- /dev/null +++ b/lib/stdbool.in.h @@ -0,0 +1,132 @@ +/* Copyright (C) 2001-2003, 2006-2017 Free Software Foundation, Inc. + Written by Bruno Haible , 2001. + + This program is free software; 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, 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, see . */ + +#ifndef _GL_STDBOOL_H +#define _GL_STDBOOL_H + +/* ISO C 99 for platforms that lack it. */ + +/* Usage suggestions: + + Programs that use should be aware of some limitations + and standards compliance issues. + + Standards compliance: + + - must be #included before 'bool', 'false', 'true' + can be used. + + - You cannot assume that sizeof (bool) == 1. + + - Programs should not undefine the macros bool, true, and false, + as C99 lists that as an "obsolescent feature". + + Limitations of this substitute, when used in a C89 environment: + + - must be #included before the '_Bool' type can be used. + + - You cannot assume that _Bool is a typedef; it might be a macro. + + - Bit-fields of type 'bool' are not supported. Portable code + should use 'unsigned int foo : 1;' rather than 'bool foo : 1;'. + + - In C99, casts and automatic conversions to '_Bool' or 'bool' are + performed in such a way that every nonzero value gets converted + to 'true', and zero gets converted to 'false'. This doesn't work + with this substitute. With this substitute, only the values 0 and 1 + give the expected result when converted to _Bool' or 'bool'. + + - C99 allows the use of (_Bool)0.0 in constant expressions, but + this substitute cannot always provide this property. + + Also, it is suggested that programs use 'bool' rather than '_Bool'; + this isn't required, but 'bool' is more common. */ + + +/* 7.16. Boolean type and values */ + +/* BeOS already #defines false 0, true 1. We use the same + definitions below, but temporarily we have to #undef them. */ +#if defined __BEOS__ && !defined __HAIKU__ +# include /* defines bool but not _Bool */ +# undef false +# undef true +#endif + +#ifdef __cplusplus +# define _Bool bool +# define bool bool +#else +# if defined __BEOS__ && !defined __HAIKU__ + /* A compiler known to have 'bool'. */ + /* If the compiler already has both 'bool' and '_Bool', we can assume they + are the same types. */ +# if !@HAVE__BOOL@ +typedef bool _Bool; +# endif +# else +# if !defined __GNUC__ + /* If @HAVE__BOOL@: + Some HP-UX cc and AIX IBM C compiler versions have compiler bugs when + the built-in _Bool type is used. See + http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-10/msg00086.html + Similar bugs are likely with other compilers as well; this file + wouldn't be used if was working. + So we override the _Bool type. + If !@HAVE__BOOL@: + Need to define _Bool ourselves. As 'signed char' or as an enum type? + Use of a typedef, with SunPRO C, leads to a stupid + "warning: _Bool is a keyword in ISO C99". + Use of an enum type, with IRIX cc, leads to a stupid + "warning(1185): enumerated type mixed with another type". + Even the existence of an enum type, without a typedef, + "Invalid enumerator. (badenum)" with HP-UX cc on Tru64. + The only benefit of the enum, debuggability, is not important + with these compilers. So use 'signed char' and no enum. */ +# define _Bool signed char +# else + /* With this compiler, trust the _Bool type if the compiler has it. */ +# if !@HAVE__BOOL@ + /* For the sake of symbolic names in gdb, define true and false as + enum constants, not only as macros. + It is tempting to write + typedef enum { false = 0, true = 1 } _Bool; + so that gdb prints values of type 'bool' symbolically. But then + values of type '_Bool' might promote to 'int' or 'unsigned int' + (see ISO C 99 6.7.2.2.(4)); however, '_Bool' must promote to 'int' + (see ISO C 99 6.3.1.1.(2)). So add a negative value to the + enum; this ensures that '_Bool' promotes to 'int'. */ +typedef enum { _Bool_must_promote_to_int = -1, false = 0, true = 1 } _Bool; +# endif +# endif +# endif +# define bool _Bool +#endif + +/* The other macros must be usable in preprocessor directives. */ +#ifdef __cplusplus +# define false false +# define true true +#else +# define false 0 +# define true 1 +#endif + +#define __bool_true_false_are_defined 1 + +#endif /* _GL_STDBOOL_H */ diff --git a/lib/stdint.in.h b/lib/stdint.in.h new file mode 100644 index 0000000..5fbec34 --- /dev/null +++ b/lib/stdint.in.h @@ -0,0 +1,726 @@ +/* Copyright (C) 2001-2002, 2004-2017 Free Software Foundation, Inc. + Written by Paul Eggert, Bruno Haible, Sam Steingold, Peter Burwood. + This file is part of gnulib. + + This program is free software; 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, 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, see . */ + +/* + * ISO C 99 for platforms that lack it. + * + */ + +#ifndef _@GUARD_PREFIX@_STDINT_H + +#if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +#endif +@PRAGMA_COLUMNS@ + +/* When including a system file that in turn includes , + use the system , not our substitute. This avoids + problems with (for example) VMS, whose includes + . */ +#define _GL_JUST_INCLUDE_SYSTEM_INTTYPES_H + +/* On Android (Bionic libc), includes this file before + having defined 'time_t'. Therefore in this case avoid including + other system header files; just include the system's . + Ideally we should test __BIONIC__ here, but it is only defined after + has been included; hence test __ANDROID__ instead. */ +#if defined __ANDROID__ && defined _GL_INCLUDING_SYS_TYPES_H +# @INCLUDE_NEXT@ @NEXT_STDINT_H@ +#else + +/* Get those types that are already defined in other system include + files, so that we can "#define int8_t signed char" below without + worrying about a later system include file containing a "typedef + signed char int8_t;" that will get messed up by our macro. Our + macros should all be consistent with the system versions, except + for the "fast" types and macros, which we recommend against using + in public interfaces due to compiler differences. */ + +#if @HAVE_STDINT_H@ +# if defined __sgi && ! defined __c99 + /* Bypass IRIX's if in C89 mode, since it merely annoys users + with "This header file is to be used only for c99 mode compilations" + diagnostics. */ +# define __STDINT_H__ +# endif + + /* Some pre-C++11 implementations need this. */ +# ifdef __cplusplus +# ifndef __STDC_CONSTANT_MACROS +# define __STDC_CONSTANT_MACROS 1 +# endif +# ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +# endif +# endif + + /* Other systems may have an incomplete or buggy . + Include it before , since any "#include " + in would reinclude us, skipping our contents because + _@GUARD_PREFIX@_STDINT_H is defined. + The include_next requires a split double-inclusion guard. */ +# @INCLUDE_NEXT@ @NEXT_STDINT_H@ +#endif + +#if ! defined _@GUARD_PREFIX@_STDINT_H && ! defined _GL_JUST_INCLUDE_SYSTEM_STDINT_H +#define _@GUARD_PREFIX@_STDINT_H + +/* Get SCHAR_MIN, SCHAR_MAX, UCHAR_MAX, INT_MIN, INT_MAX, + LONG_MIN, LONG_MAX, ULONG_MAX, _GL_INTEGER_WIDTH. */ +#include + +/* Override WINT_MIN and WINT_MAX if gnulib's or overrides + wint_t. */ +#if @GNULIB_OVERRIDES_WINT_T@ +# undef WINT_MIN +# undef WINT_MAX +# define WINT_MIN 0x0U +# define WINT_MAX 0xffffffffU +#endif + +#if ! @HAVE_C99_STDINT_H@ + +/* defines some of the stdint.h types as well, on glibc, + IRIX 6.5, and OpenBSD 3.8 (via ). + AIX 5.2 isn't needed and causes troubles. + Mac OS X 10.4.6 includes (which is us), but + relies on the system definitions, so include + after @NEXT_STDINT_H@. */ +# if @HAVE_SYS_TYPES_H@ && ! defined _AIX +# include +# endif + +# if @HAVE_INTTYPES_H@ + /* In OpenBSD 3.8, includes , which defines + int{8,16,32,64}_t, uint{8,16,32,64}_t and __BIT_TYPES_DEFINED__. + also defines intptr_t and uintptr_t. */ +# include +# elif @HAVE_SYS_INTTYPES_H@ + /* Solaris 7 has the types except the *_fast*_t types, and + the macros except for *_FAST*_*, INTPTR_MIN, PTRDIFF_MIN, PTRDIFF_MAX. */ +# include +# endif + +# if @HAVE_SYS_BITYPES_H@ && ! defined __BIT_TYPES_DEFINED__ + /* Linux libc4 >= 4.6.7 and libc5 have a that defines + int{8,16,32,64}_t and __BIT_TYPES_DEFINED__. In libc5 >= 5.2.2 it is + included by . */ +# include +# endif + +# undef _GL_JUST_INCLUDE_SYSTEM_INTTYPES_H + +/* Minimum and maximum values for an integer type under the usual assumption. + Return an unspecified value if BITS == 0, adding a check to pacify + picky compilers. */ + +/* These are separate macros, because if you try to merge these macros into + a single one, HP-UX cc rejects the resulting expression in constant + expressions. */ +# define _STDINT_UNSIGNED_MIN(bits, zero) \ + (zero) +# define _STDINT_SIGNED_MIN(bits, zero) \ + (~ _STDINT_MAX (1, bits, zero)) + +# define _STDINT_MAX(signed, bits, zero) \ + (((((zero) + 1) << ((bits) ? (bits) - 1 - (signed) : 0)) - 1) * 2 + 1) + +#if !GNULIB_defined_stdint_types + +/* 7.18.1.1. Exact-width integer types */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. */ + +# undef int8_t +# undef uint8_t +typedef signed char gl_int8_t; +typedef unsigned char gl_uint8_t; +# define int8_t gl_int8_t +# define uint8_t gl_uint8_t + +# undef int16_t +# undef uint16_t +typedef short int gl_int16_t; +typedef unsigned short int gl_uint16_t; +# define int16_t gl_int16_t +# define uint16_t gl_uint16_t + +# undef int32_t +# undef uint32_t +typedef int gl_int32_t; +typedef unsigned int gl_uint32_t; +# define int32_t gl_int32_t +# define uint32_t gl_uint32_t + +/* If the system defines INT64_MAX, assume int64_t works. That way, + if the underlying platform defines int64_t to be a 64-bit long long + int, the code below won't mistakenly define it to be a 64-bit long + int, which would mess up C++ name mangling. We must use #ifdef + rather than #if, to avoid an error with HP-UX 10.20 cc. */ + +# ifdef INT64_MAX +# define GL_INT64_T +# else +/* Do not undefine int64_t if gnulib is not being used with 64-bit + types, since otherwise it breaks platforms like Tandem/NSK. */ +# if LONG_MAX >> 31 >> 31 == 1 +# undef int64_t +typedef long int gl_int64_t; +# define int64_t gl_int64_t +# define GL_INT64_T +# elif defined _MSC_VER +# undef int64_t +typedef __int64 gl_int64_t; +# define int64_t gl_int64_t +# define GL_INT64_T +# elif @HAVE_LONG_LONG_INT@ +# undef int64_t +typedef long long int gl_int64_t; +# define int64_t gl_int64_t +# define GL_INT64_T +# endif +# endif + +# ifdef UINT64_MAX +# define GL_UINT64_T +# else +# if ULONG_MAX >> 31 >> 31 >> 1 == 1 +# undef uint64_t +typedef unsigned long int gl_uint64_t; +# define uint64_t gl_uint64_t +# define GL_UINT64_T +# elif defined _MSC_VER +# undef uint64_t +typedef unsigned __int64 gl_uint64_t; +# define uint64_t gl_uint64_t +# define GL_UINT64_T +# elif @HAVE_UNSIGNED_LONG_LONG_INT@ +# undef uint64_t +typedef unsigned long long int gl_uint64_t; +# define uint64_t gl_uint64_t +# define GL_UINT64_T +# endif +# endif + +/* Avoid collision with Solaris 2.5.1 etc. */ +# define _UINT8_T +# define _UINT32_T +# define _UINT64_T + + +/* 7.18.1.2. Minimum-width integer types */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. Therefore the leastN_t types + are the same as the corresponding N_t types. */ + +# undef int_least8_t +# undef uint_least8_t +# undef int_least16_t +# undef uint_least16_t +# undef int_least32_t +# undef uint_least32_t +# undef int_least64_t +# undef uint_least64_t +# define int_least8_t int8_t +# define uint_least8_t uint8_t +# define int_least16_t int16_t +# define uint_least16_t uint16_t +# define int_least32_t int32_t +# define uint_least32_t uint32_t +# ifdef GL_INT64_T +# define int_least64_t int64_t +# endif +# ifdef GL_UINT64_T +# define uint_least64_t uint64_t +# endif + +/* 7.18.1.3. Fastest minimum-width integer types */ + +/* Note: Other substitutes may define these types differently. + It is not recommended to use these types in public header files. */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. Therefore the fastN_t types + are taken from the same list of types. The following code normally + uses types consistent with glibc, as that lessens the chance of + incompatibility with older GNU hosts. */ + +# undef int_fast8_t +# undef uint_fast8_t +# undef int_fast16_t +# undef uint_fast16_t +# undef int_fast32_t +# undef uint_fast32_t +# undef int_fast64_t +# undef uint_fast64_t +typedef signed char gl_int_fast8_t; +typedef unsigned char gl_uint_fast8_t; + +# ifdef __sun +/* Define types compatible with SunOS 5.10, so that code compiled under + earlier SunOS versions works with code compiled under SunOS 5.10. */ +typedef int gl_int_fast32_t; +typedef unsigned int gl_uint_fast32_t; +# else +typedef long int gl_int_fast32_t; +typedef unsigned long int gl_uint_fast32_t; +# endif +typedef gl_int_fast32_t gl_int_fast16_t; +typedef gl_uint_fast32_t gl_uint_fast16_t; + +# define int_fast8_t gl_int_fast8_t +# define uint_fast8_t gl_uint_fast8_t +# define int_fast16_t gl_int_fast16_t +# define uint_fast16_t gl_uint_fast16_t +# define int_fast32_t gl_int_fast32_t +# define uint_fast32_t gl_uint_fast32_t +# ifdef GL_INT64_T +# define int_fast64_t int64_t +# endif +# ifdef GL_UINT64_T +# define uint_fast64_t uint64_t +# endif + +/* 7.18.1.4. Integer types capable of holding object pointers */ + +/* kLIBC's stdint.h defines _INTPTR_T_DECLARED and needs its own + definitions of intptr_t and uintptr_t (which use int and unsigned) + to avoid clashes with declarations of system functions like sbrk. */ +# ifndef _INTPTR_T_DECLARED +# undef intptr_t +# undef uintptr_t +typedef long int gl_intptr_t; +typedef unsigned long int gl_uintptr_t; +# define intptr_t gl_intptr_t +# define uintptr_t gl_uintptr_t +# endif + +/* 7.18.1.5. Greatest-width integer types */ + +/* Note: These types are compiler dependent. It may be unwise to use them in + public header files. */ + +/* If the system defines INTMAX_MAX, assume that intmax_t works, and + similarly for UINTMAX_MAX and uintmax_t. This avoids problems with + assuming one type where another is used by the system. */ + +# ifndef INTMAX_MAX +# undef INTMAX_C +# undef intmax_t +# if @HAVE_LONG_LONG_INT@ && LONG_MAX >> 30 == 1 +typedef long long int gl_intmax_t; +# define intmax_t gl_intmax_t +# elif defined GL_INT64_T +# define intmax_t int64_t +# else +typedef long int gl_intmax_t; +# define intmax_t gl_intmax_t +# endif +# endif + +# ifndef UINTMAX_MAX +# undef UINTMAX_C +# undef uintmax_t +# if @HAVE_UNSIGNED_LONG_LONG_INT@ && ULONG_MAX >> 31 == 1 +typedef unsigned long long int gl_uintmax_t; +# define uintmax_t gl_uintmax_t +# elif defined GL_UINT64_T +# define uintmax_t uint64_t +# else +typedef unsigned long int gl_uintmax_t; +# define uintmax_t gl_uintmax_t +# endif +# endif + +/* Verify that intmax_t and uintmax_t have the same size. Too much code + breaks if this is not the case. If this check fails, the reason is likely + to be found in the autoconf macros. */ +typedef int _verify_intmax_size[sizeof (intmax_t) == sizeof (uintmax_t) + ? 1 : -1]; + +# define GNULIB_defined_stdint_types 1 +# endif /* !GNULIB_defined_stdint_types */ + +/* 7.18.2. Limits of specified-width integer types */ + +/* 7.18.2.1. Limits of exact-width integer types */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. */ + +# undef INT8_MIN +# undef INT8_MAX +# undef UINT8_MAX +# define INT8_MIN (~ INT8_MAX) +# define INT8_MAX 127 +# define UINT8_MAX 255 + +# undef INT16_MIN +# undef INT16_MAX +# undef UINT16_MAX +# define INT16_MIN (~ INT16_MAX) +# define INT16_MAX 32767 +# define UINT16_MAX 65535 + +# undef INT32_MIN +# undef INT32_MAX +# undef UINT32_MAX +# define INT32_MIN (~ INT32_MAX) +# define INT32_MAX 2147483647 +# define UINT32_MAX 4294967295U + +# if defined GL_INT64_T && ! defined INT64_MAX +/* Prefer (- INTMAX_C (1) << 63) over (~ INT64_MAX) because SunPRO C 5.0 + evaluates the latter incorrectly in preprocessor expressions. */ +# define INT64_MIN (- INTMAX_C (1) << 63) +# define INT64_MAX INTMAX_C (9223372036854775807) +# endif + +# if defined GL_UINT64_T && ! defined UINT64_MAX +# define UINT64_MAX UINTMAX_C (18446744073709551615) +# endif + +/* 7.18.2.2. Limits of minimum-width integer types */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. Therefore the leastN_t types + are the same as the corresponding N_t types. */ + +# undef INT_LEAST8_MIN +# undef INT_LEAST8_MAX +# undef UINT_LEAST8_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST8_MAX UINT8_MAX + +# undef INT_LEAST16_MIN +# undef INT_LEAST16_MAX +# undef UINT_LEAST16_MAX +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST16_MAX UINT16_MAX + +# undef INT_LEAST32_MIN +# undef INT_LEAST32_MAX +# undef UINT_LEAST32_MAX +# define INT_LEAST32_MIN INT32_MIN +# define INT_LEAST32_MAX INT32_MAX +# define UINT_LEAST32_MAX UINT32_MAX + +# undef INT_LEAST64_MIN +# undef INT_LEAST64_MAX +# ifdef GL_INT64_T +# define INT_LEAST64_MIN INT64_MIN +# define INT_LEAST64_MAX INT64_MAX +# endif + +# undef UINT_LEAST64_MAX +# ifdef GL_UINT64_T +# define UINT_LEAST64_MAX UINT64_MAX +# endif + +/* 7.18.2.3. Limits of fastest minimum-width integer types */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits. Therefore the fastN_t types + are taken from the same list of types. */ + +# undef INT_FAST8_MIN +# undef INT_FAST8_MAX +# undef UINT_FAST8_MAX +# define INT_FAST8_MIN SCHAR_MIN +# define INT_FAST8_MAX SCHAR_MAX +# define UINT_FAST8_MAX UCHAR_MAX + +# undef INT_FAST16_MIN +# undef INT_FAST16_MAX +# undef UINT_FAST16_MAX +# define INT_FAST16_MIN INT_FAST32_MIN +# define INT_FAST16_MAX INT_FAST32_MAX +# define UINT_FAST16_MAX UINT_FAST32_MAX + +# undef INT_FAST32_MIN +# undef INT_FAST32_MAX +# undef UINT_FAST32_MAX +# ifdef __sun +# define INT_FAST32_MIN INT_MIN +# define INT_FAST32_MAX INT_MAX +# define UINT_FAST32_MAX UINT_MAX +# else +# define INT_FAST32_MIN LONG_MIN +# define INT_FAST32_MAX LONG_MAX +# define UINT_FAST32_MAX ULONG_MAX +# endif + +# undef INT_FAST64_MIN +# undef INT_FAST64_MAX +# ifdef GL_INT64_T +# define INT_FAST64_MIN INT64_MIN +# define INT_FAST64_MAX INT64_MAX +# endif + +# undef UINT_FAST64_MAX +# ifdef GL_UINT64_T +# define UINT_FAST64_MAX UINT64_MAX +# endif + +/* 7.18.2.4. Limits of integer types capable of holding object pointers */ + +# undef INTPTR_MIN +# undef INTPTR_MAX +# undef UINTPTR_MAX +# define INTPTR_MIN LONG_MIN +# define INTPTR_MAX LONG_MAX +# define UINTPTR_MAX ULONG_MAX + +/* 7.18.2.5. Limits of greatest-width integer types */ + +# ifndef INTMAX_MAX +# undef INTMAX_MIN +# ifdef INT64_MAX +# define INTMAX_MIN INT64_MIN +# define INTMAX_MAX INT64_MAX +# else +# define INTMAX_MIN INT32_MIN +# define INTMAX_MAX INT32_MAX +# endif +# endif + +# ifndef UINTMAX_MAX +# ifdef UINT64_MAX +# define UINTMAX_MAX UINT64_MAX +# else +# define UINTMAX_MAX UINT32_MAX +# endif +# endif + +/* 7.18.3. Limits of other integer types */ + +/* ptrdiff_t limits */ +# undef PTRDIFF_MIN +# undef PTRDIFF_MAX +# if @APPLE_UNIVERSAL_BUILD@ +# ifdef _LP64 +# define PTRDIFF_MIN _STDINT_SIGNED_MIN (64, 0l) +# define PTRDIFF_MAX _STDINT_MAX (1, 64, 0l) +# else +# define PTRDIFF_MIN _STDINT_SIGNED_MIN (32, 0) +# define PTRDIFF_MAX _STDINT_MAX (1, 32, 0) +# endif +# else +# define PTRDIFF_MIN \ + _STDINT_SIGNED_MIN (@BITSIZEOF_PTRDIFF_T@, 0@PTRDIFF_T_SUFFIX@) +# define PTRDIFF_MAX \ + _STDINT_MAX (1, @BITSIZEOF_PTRDIFF_T@, 0@PTRDIFF_T_SUFFIX@) +# endif + +/* sig_atomic_t limits */ +# undef SIG_ATOMIC_MIN +# undef SIG_ATOMIC_MAX +# if @HAVE_SIGNED_SIG_ATOMIC_T@ +# define SIG_ATOMIC_MIN \ + _STDINT_SIGNED_MIN (@BITSIZEOF_SIG_ATOMIC_T@, 0@SIG_ATOMIC_T_SUFFIX@) +# else +# define SIG_ATOMIC_MIN \ + _STDINT_UNSIGNED_MIN (@BITSIZEOF_SIG_ATOMIC_T@, 0@SIG_ATOMIC_T_SUFFIX@) +# endif +# define SIG_ATOMIC_MAX \ + _STDINT_MAX (@HAVE_SIGNED_SIG_ATOMIC_T@, @BITSIZEOF_SIG_ATOMIC_T@, \ + 0@SIG_ATOMIC_T_SUFFIX@) + + +/* size_t limit */ +# undef SIZE_MAX +# if @APPLE_UNIVERSAL_BUILD@ +# ifdef _LP64 +# define SIZE_MAX _STDINT_MAX (0, 64, 0ul) +# else +# define SIZE_MAX _STDINT_MAX (0, 32, 0ul) +# endif +# else +# define SIZE_MAX _STDINT_MAX (0, @BITSIZEOF_SIZE_T@, 0@SIZE_T_SUFFIX@) +# endif + +/* wchar_t limits */ +/* Get WCHAR_MIN, WCHAR_MAX. + This include is not on the top, above, because on OSF/1 4.0 we have a + sequence of nested includes + -> -> -> , and the latter includes + and assumes its types are already defined. */ +# if @HAVE_WCHAR_H@ && ! (defined WCHAR_MIN && defined WCHAR_MAX) + /* BSD/OS 4.0.1 has a bug: , and must be + included before . */ +# include +# include +# include +# define _GL_JUST_INCLUDE_SYSTEM_WCHAR_H +# include +# undef _GL_JUST_INCLUDE_SYSTEM_WCHAR_H +# endif +# undef WCHAR_MIN +# undef WCHAR_MAX +# if @HAVE_SIGNED_WCHAR_T@ +# define WCHAR_MIN \ + _STDINT_SIGNED_MIN (@BITSIZEOF_WCHAR_T@, 0@WCHAR_T_SUFFIX@) +# else +# define WCHAR_MIN \ + _STDINT_UNSIGNED_MIN (@BITSIZEOF_WCHAR_T@, 0@WCHAR_T_SUFFIX@) +# endif +# define WCHAR_MAX \ + _STDINT_MAX (@HAVE_SIGNED_WCHAR_T@, @BITSIZEOF_WCHAR_T@, 0@WCHAR_T_SUFFIX@) + +/* wint_t limits */ +# undef WINT_MIN +# undef WINT_MAX +# if @HAVE_SIGNED_WINT_T@ +# define WINT_MIN \ + _STDINT_SIGNED_MIN (@BITSIZEOF_WINT_T@, 0@WINT_T_SUFFIX@) +# else +# define WINT_MIN \ + _STDINT_UNSIGNED_MIN (@BITSIZEOF_WINT_T@, 0@WINT_T_SUFFIX@) +# endif +# define WINT_MAX \ + _STDINT_MAX (@HAVE_SIGNED_WINT_T@, @BITSIZEOF_WINT_T@, 0@WINT_T_SUFFIX@) + +/* 7.18.4. Macros for integer constants */ + +/* 7.18.4.1. Macros for minimum-width integer constants */ +/* According to ISO C 99 Technical Corrigendum 1 */ + +/* Here we assume a standard architecture where the hardware integer + types have 8, 16, 32, optionally 64 bits, and int is 32 bits. */ + +# undef INT8_C +# undef UINT8_C +# define INT8_C(x) x +# define UINT8_C(x) x + +# undef INT16_C +# undef UINT16_C +# define INT16_C(x) x +# define UINT16_C(x) x + +# undef INT32_C +# undef UINT32_C +# define INT32_C(x) x +# define UINT32_C(x) x ## U + +# undef INT64_C +# undef UINT64_C +# if LONG_MAX >> 31 >> 31 == 1 +# define INT64_C(x) x##L +# elif defined _MSC_VER +# define INT64_C(x) x##i64 +# elif @HAVE_LONG_LONG_INT@ +# define INT64_C(x) x##LL +# endif +# if ULONG_MAX >> 31 >> 31 >> 1 == 1 +# define UINT64_C(x) x##UL +# elif defined _MSC_VER +# define UINT64_C(x) x##ui64 +# elif @HAVE_UNSIGNED_LONG_LONG_INT@ +# define UINT64_C(x) x##ULL +# endif + +/* 7.18.4.2. Macros for greatest-width integer constants */ + +# ifndef INTMAX_C +# if @HAVE_LONG_LONG_INT@ && LONG_MAX >> 30 == 1 +# define INTMAX_C(x) x##LL +# elif defined GL_INT64_T +# define INTMAX_C(x) INT64_C(x) +# else +# define INTMAX_C(x) x##L +# endif +# endif + +# ifndef UINTMAX_C +# if @HAVE_UNSIGNED_LONG_LONG_INT@ && ULONG_MAX >> 31 == 1 +# define UINTMAX_C(x) x##ULL +# elif defined GL_UINT64_T +# define UINTMAX_C(x) UINT64_C(x) +# else +# define UINTMAX_C(x) x##UL +# endif +# endif + +#endif /* !@HAVE_C99_STDINT_H@ */ + +/* Macros specified by ISO/IEC TS 18661-1:2014. */ + +#if (!defined UINTMAX_WIDTH \ + && (defined _GNU_SOURCE || defined __STDC_WANT_IEC_60559_BFP_EXT__)) +# ifdef INT8_MAX +# define INT8_WIDTH _GL_INTEGER_WIDTH (INT8_MIN, INT8_MAX) +# endif +# ifdef UINT8_MAX +# define UINT8_WIDTH _GL_INTEGER_WIDTH (0, UINT8_MAX) +# endif +# ifdef INT16_MAX +# define INT16_WIDTH _GL_INTEGER_WIDTH (INT16_MIN, INT16_MAX) +# endif +# ifdef UINT16_MAX +# define UINT16_WIDTH _GL_INTEGER_WIDTH (0, UINT16_MAX) +# endif +# ifdef INT32_MAX +# define INT32_WIDTH _GL_INTEGER_WIDTH (INT32_MIN, INT32_MAX) +# endif +# ifdef UINT32_MAX +# define UINT32_WIDTH _GL_INTEGER_WIDTH (0, UINT32_MAX) +# endif +# ifdef INT64_MAX +# define INT64_WIDTH _GL_INTEGER_WIDTH (INT64_MIN, INT64_MAX) +# endif +# ifdef UINT64_MAX +# define UINT64_WIDTH _GL_INTEGER_WIDTH (0, UINT64_MAX) +# endif +# define INT_LEAST8_WIDTH _GL_INTEGER_WIDTH (INT_LEAST8_MIN, INT_LEAST8_MAX) +# define UINT_LEAST8_WIDTH _GL_INTEGER_WIDTH (0, UINT_LEAST8_MAX) +# define INT_LEAST16_WIDTH _GL_INTEGER_WIDTH (INT_LEAST16_MIN, INT_LEAST16_MAX) +# define UINT_LEAST16_WIDTH _GL_INTEGER_WIDTH (0, UINT_LEAST16_MAX) +# define INT_LEAST32_WIDTH _GL_INTEGER_WIDTH (INT_LEAST32_MIN, INT_LEAST32_MAX) +# define UINT_LEAST32_WIDTH _GL_INTEGER_WIDTH (0, UINT_LEAST32_MAX) +# define INT_LEAST64_WIDTH _GL_INTEGER_WIDTH (INT_LEAST64_MIN, INT_LEAST64_MAX) +# define UINT_LEAST64_WIDTH _GL_INTEGER_WIDTH (0, UINT_LEAST64_MAX) +# define INT_FAST8_WIDTH _GL_INTEGER_WIDTH (INT_FAST8_MIN, INT_FAST8_MAX) +# define UINT_FAST8_WIDTH _GL_INTEGER_WIDTH (0, UINT_FAST8_MAX) +# define INT_FAST16_WIDTH _GL_INTEGER_WIDTH (INT_FAST16_MIN, INT_FAST16_MAX) +# define UINT_FAST16_WIDTH _GL_INTEGER_WIDTH (0, UINT_FAST16_MAX) +# define INT_FAST32_WIDTH _GL_INTEGER_WIDTH (INT_FAST32_MIN, INT_FAST32_MAX) +# define UINT_FAST32_WIDTH _GL_INTEGER_WIDTH (0, UINT_FAST32_MAX) +# define INT_FAST64_WIDTH _GL_INTEGER_WIDTH (INT_FAST64_MIN, INT_FAST64_MAX) +# define UINT_FAST64_WIDTH _GL_INTEGER_WIDTH (0, UINT_FAST64_MAX) +# define INTPTR_WIDTH _GL_INTEGER_WIDTH (INTPTR_MIN, INTPTR_MAX) +# define UINTPTR_WIDTH _GL_INTEGER_WIDTH (0, UINTPTR_MAX) +# define INTMAX_WIDTH _GL_INTEGER_WIDTH (INTMAX_MIN, INTMAX_MAX) +# define UINTMAX_WIDTH _GL_INTEGER_WIDTH (0, UINTMAX_MAX) +# define PTRDIFF_WIDTH _GL_INTEGER_WIDTH (PTRDIFF_MIN, PTRDIFF_MAX) +# define SIZE_WIDTH _GL_INTEGER_WIDTH (0, SIZE_MAX) +# define WCHAR_WIDTH _GL_INTEGER_WIDTH (WCHAR_MIN, WCHAR_MAX) +# ifdef WINT_MAX +# define WINT_WIDTH _GL_INTEGER_WIDTH (WINT_MIN, WINT_MAX) +# endif +# ifdef SIG_ATOMIC_MAX +# define SIG_ATOMIC_WIDTH _GL_INTEGER_WIDTH (SIG_ATOMIC_MIN, SIG_ATOMIC_MAX) +# endif +#endif /* !WINT_WIDTH && (_GNU_SOURCE || __STDC_WANT_IEC_60559_BFP_EXT__) */ + +#endif /* _@GUARD_PREFIX@_STDINT_H */ +#endif /* !(defined __ANDROID__ && ...) */ +#endif /* !defined _@GUARD_PREFIX@_STDINT_H && !defined _GL_JUST_INCLUDE_SYSTEM_STDINT_H */ diff --git a/lib/sys_types.in.h b/lib/sys_types.in.h new file mode 100644 index 0000000..3cea448 --- /dev/null +++ b/lib/sys_types.in.h @@ -0,0 +1,95 @@ +/* Provide a more complete sys/types.h. + + Copyright (C) 2011-2017 Free Software Foundation, Inc. + + This program is free software; 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, 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, see . */ + +#if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +#endif +@PRAGMA_COLUMNS@ + +#ifndef _@GUARD_PREFIX@_SYS_TYPES_H + +/* The include_next requires a split double-inclusion guard. */ +# define _GL_INCLUDING_SYS_TYPES_H +#@INCLUDE_NEXT@ @NEXT_SYS_TYPES_H@ +# undef _GL_INCLUDING_SYS_TYPES_H + +#ifndef _@GUARD_PREFIX@_SYS_TYPES_H +#define _@GUARD_PREFIX@_SYS_TYPES_H + +/* Override off_t if Large File Support is requested on native Windows. */ +#if @WINDOWS_64_BIT_OFF_T@ +/* Same as int64_t in . */ +# if defined _MSC_VER +# define off_t __int64 +# else +# define off_t long long int +# endif +/* Indicator, for gnulib internal purposes. */ +# define _GL_WINDOWS_64_BIT_OFF_T 1 +#endif + +/* Override dev_t and ino_t if distinguishable inodes support is requested + on native Windows. */ +#if @WINDOWS_STAT_INODES@ + +# if @WINDOWS_STAT_INODES@ == 2 +/* Experimental, not useful in Windows 10. */ + +/* Define dev_t to a 64-bit type. */ +# if !defined GNULIB_defined_dev_t +typedef unsigned long long int rpl_dev_t; +# undef dev_t +# define dev_t rpl_dev_t +# define GNULIB_defined_dev_t 1 +# endif + +/* Define ino_t to a 128-bit type. */ +# if !defined GNULIB_defined_ino_t +/* MSVC does not have a 128-bit integer type. + GCC has a 128-bit integer type __int128, but only on 64-bit targets. */ +typedef struct { unsigned long long int _gl_ino[2]; } rpl_ino_t; +# undef ino_t +# define ino_t rpl_ino_t +# define GNULIB_defined_ino_t 1 +# endif + +# else /* @WINDOWS_STAT_INODES@ == 1 */ + +/* Define ino_t to a 64-bit type. */ +# if !defined GNULIB_defined_ino_t +typedef unsigned long long int rpl_ino_t; +# undef ino_t +# define ino_t rpl_ino_t +# define GNULIB_defined_ino_t 1 +# endif + +# endif + +/* Indicator, for gnulib internal purposes. */ +# define _GL_WINDOWS_STAT_INODES @WINDOWS_STAT_INODES@ + +#endif + +/* MSVC 9 defines size_t in , not in . */ +/* But avoid namespace pollution on glibc systems. */ +#if ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__) \ + && ! defined __GLIBC__ +# include +#endif + +#endif /* _@GUARD_PREFIX@_SYS_TYPES_H */ +#endif /* _@GUARD_PREFIX@_SYS_TYPES_H */ diff --git a/lib/xalloc-oversized.h b/lib/xalloc-oversized.h new file mode 100644 index 0000000..2e09bab --- /dev/null +++ b/lib/xalloc-oversized.h @@ -0,0 +1,60 @@ +/* xalloc-oversized.h -- memory allocation size checking + + Copyright (C) 1990-2000, 2003-2004, 2006-2017 Free Software Foundation, Inc. + + This program is free software: 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef XALLOC_OVERSIZED_H_ +#define XALLOC_OVERSIZED_H_ + +#include +#include + +/* True if N * S would overflow in a size_t calculation, + or would generate a value larger than PTRDIFF_MAX. + This expands to a constant expression if N and S are both constants. + By gnulib convention, SIZE_MAX represents overflow in size + calculations, so the conservative size_t-based dividend to use here + is SIZE_MAX - 1. */ +#define __xalloc_oversized(n, s) \ + ((size_t) (PTRDIFF_MAX < SIZE_MAX ? PTRDIFF_MAX : SIZE_MAX - 1) / (s) < (n)) + +#if PTRDIFF_MAX < SIZE_MAX +typedef ptrdiff_t __xalloc_count_type; +#else +typedef size_t __xalloc_count_type; +#endif + +/* Return 1 if an array of N objects, each of size S, cannot exist + reliably due to size or ptrdiff_t arithmetic overflow. S must be + positive and N must be nonnegative. This is a macro, not a + function, so that it works correctly even when SIZE_MAX < N. */ + +#if 7 <= __GNUC__ +# define xalloc_oversized(n, s) \ + __builtin_mul_overflow_p (n, s, (__xalloc_count_type) 1) +#elif 5 <= __GNUC__ && !defined __ICC && !__STRICT_ANSI__ +# define xalloc_oversized(n, s) \ + (__builtin_constant_p (n) && __builtin_constant_p (s) \ + ? __xalloc_oversized (n, s) \ + : ({ __xalloc_count_type __xalloc_count; \ + __builtin_mul_overflow (n, s, &__xalloc_count); })) + +/* Other compilers use integer division; this may be slower but is + more portable. */ +#else +# define xalloc_oversized(n, s) __xalloc_oversized (n, s) +#endif + +#endif /* !XALLOC_OVERSIZED_H_ */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..a74e5a2 --- /dev/null +++ b/main.c @@ -0,0 +1,2856 @@ +/* containers: Overlay Filesystem in Userspace + + Copyright (C) 2018 Giuseppe Scrivano + Copyright (C) 2001-2007 Miklos Szeredi + + This program is free software: 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define _GNU_SOURCE +#define FUSE_USE_VERSION 31 +#define _FILE_OFFSET_BITS 64 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#ifndef RENAME_EXCHANGE +# define RENAME_EXCHANGE (1 << 1) +# define RENAME_NOREPLACE (1 << 2) +#endif + +#define ATTR_TIMEOUT 1000000000.0 +#define ENTRY_TIMEOUT 1000000000.0 + +#define NODE_TO_INODE(x) ((fuse_ino_t) x) + +#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus +_Static_assert (sizeof (fuse_ino_t) >= sizeof (uintptr_t), + "fuse_ino_t too small to hold uintptr_t values!"); +#else +struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct +{ + unsigned _uintptr_to_must_hold_fuse_ino_t: + ((sizeof (fuse_ino_t) >= sizeof (uintptr_t)) ? 1 : -1); +}; +#endif + +static size_t +str_hasher (const void *p, size_t s) +{ + const char *str = (const char *) p; + + return hash_string (str, s); +} + +static bool +str_compare (const void *n1, const void *n2) +{ + const char *s1 = (const char *) n1; + const char *s2 = (const char *) n2; + + return strcmp (s1, s2) == 0 ? true : false; +} + +struct lo_dir +{ + struct lo_dir *next; + struct lo_dir *prev; + char *path; + int fd; +}; + +struct lo_node +{ + struct lo_node *parent; + struct lo_node *lowerdir; + Hash_table *children; + char *path; + char *name; + int lookups; + unsigned int dirty : 1; + unsigned int low : 1; + unsigned int do_unlink : 1; + unsigned int do_rmdir : 1; + unsigned int hidden : 1; + unsigned int not_exists : 1; +}; + +struct lo_data +{ + struct fuse_session *se; + int debug; + int uid; + int gid; + char *lowerdir; + char *context; + char *upperdir; + char *workdir; + struct lo_node *root_lower; + struct lo_node *root_upper; +}; + +static const struct fuse_opt lo_opts[] = { + {"context=%s", + offsetof (struct lo_data, context), 0}, + {"lowerdir=%s", + offsetof (struct lo_data, lowerdir), 0}, + {"upperdir=%s", + offsetof (struct lo_data, upperdir), 0}, + {"workdir=%s", + offsetof (struct lo_data, workdir), 0}, + {"uid=%i", + offsetof (struct lo_data, uid), -1}, + {"gid=%i", + offsetof (struct lo_data, gid), -1}, + FUSE_OPT_END +}; + +static struct lo_data * +lo_data (fuse_req_t req) +{ + return (struct lo_data *) fuse_req_userdata (req); +} + +/* Useful in a gdb session. */ +static void +dump_directory (struct lo_node *node) +{ + struct lo_node *it; + + if (node->children == NULL) + return; + + for (it = hash_get_first (node->children); it; it = hash_get_next (node->children, it)) + printf ("ENTRY: %s (%s)\n", it->name, it->path); +} + +static bool +lo_debug (fuse_req_t req) +{ + return lo_data (req)->debug != 0; +} + +static void +lo_init (void *userdata, struct fuse_conn_info *conn) +{ + conn->want |= FUSE_CAP_DONT_MASK | FUSE_CAP_SPLICE_READ | FUSE_CAP_SPLICE_MOVE; + conn->want &= ~FUSE_CAP_PARALLEL_DIROPS; +} + +/* FIXME: support proper ID ranges. */ +static uid_t +get_uid (struct lo_data *lo, uid_t id) +{ + if (lo->uid == id) + return geteuid (); + return id; +} + +static uid_t +get_gid (struct lo_data *lo, gid_t id) +{ + if (lo->gid == id) + return getegid (); + return id; +} + +static inline bool +node_dirp (struct lo_node *n) +{ + return n->children != NULL; +} + +static char * +get_node_path (struct lo_node *node) +{ + if (node->not_exists) + return node->lowerdir->path; + return node->path; +} + +static void +hide_node (struct lo_data *lo, struct lo_node *node) +{ + char *src = node->path; + + node->hidden = 1; + node->parent = NULL; + + node->path = tempnam (lo->workdir, NULL); + if (node->path == NULL) + { + free (src); + return; + } + if (rename (src, node->path) < 0) + { + free (src); + return; + } + node->do_unlink = 1; +} + +static int +rpl_stat (fuse_req_t req, struct lo_node *node, struct stat *st) +{ + int ret; + struct lo_data *data = lo_data (req); + + if (! node->not_exists) + ret = lstat (node->path, st); + else + ret = lstat (node->lowerdir->path, st); + + if (ret < 0) + return ret; + + if (data->uid >= 0) + st->st_uid = data->uid; + if (data->gid >= 0) + st->st_gid = data->gid; + + + st->st_ino = NODE_TO_INODE (node); + + if (ret == 0 && node_dirp (node)) + { + struct lo_node *it; + + st->st_nlink = 2; + + for (it = hash_get_first (node->children); it; it = hash_get_next (node->children, it)) + { + if (node_dirp (it)) + st->st_nlink++; + } + if (node->lowerdir) + { + for (it = hash_get_first (node->lowerdir->children); it; it = hash_get_next (node->lowerdir->children, it)) + { + if (node_dirp (it) && it->lowerdir == NULL) + st->st_nlink++; + } + } + } + + return ret; +} + +static void +node_mark_all_free (void *p) +{ + struct lo_node *it, *n = (struct lo_node *) p; + + n->lookups = 0; + + if (n->children) + { + for (it = hash_get_first (n->children); it; it = hash_get_next (n->children, it)) + node_mark_all_free (it); + } +} + +static void +node_free (void *p) +{ + struct lo_node *n = (struct lo_node *) p; + if (n->parent) + { + if (hash_lookup (n->parent->children, n) == n) + hash_delete (n->parent->children, n); + n->parent->dirty = 1; + n->parent = NULL; + } + + if (n->lookups > 0) + return; + + if (n->children) + { + struct lo_node *it; + + for (it = hash_get_first (n->children); it; it = hash_get_next (n->children, it)) + it->parent = NULL; + hash_free (n->children); + n->children = NULL; + } + + if (! n->not_exists) + { + if (n->do_unlink) + unlink (n->path); + if (n->do_rmdir) + rmdir (n->path); + } + + free (n->name); + n->name = NULL; + free (n->path); + n->path = NULL; + free (n); +} + +static void +do_forget (fuse_ino_t ino, uint64_t nlookup) +{ + struct lo_node *n; + + if (ino == FUSE_ROOT_ID) + return; + + n = (struct lo_node *) ino; + if (n->low) + return; + + n->lookups -= nlookup; + if (n->lookups <= 0) + node_free (n); +} + +static void +lo_forget (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) +{ + if (lo_debug (req)) + fprintf (stderr, "lo_forget(ino=%" PRIu64 ", nlookup=%lu)\n", + ino, nlookup); + do_forget (ino, nlookup); + fuse_reply_none (req); +} + +static size_t +node_hasher (const void *p, size_t s) +{ + struct lo_node *n = (struct lo_node *) p; + return hash_string (n->name, s); +} + +static bool +file_exists_p (const char *path) +{ + return access (path, R_OK) == F_OK; +} + +static bool +node_compare (const void *n1, const void *n2) +{ + struct lo_node *node1 = (struct lo_node *) n1; + struct lo_node *node2 = (struct lo_node *) n2; + + return strcmp (node1->name, node2->name) == 0 ? true : false; +} + +static struct lo_node * +make_lo_node (const char *path, const char *name, bool dir_p) +{ + struct lo_node *ret = malloc (sizeof (*ret)); + if (ret == NULL) + { + errno = ENOMEM; + return NULL; + } + + ret->lowerdir = NULL; + ret->parent = NULL; + ret->dirty = 0; + ret->low = 0; + ret->lookups = 0; + ret->do_unlink = 0; + ret->hidden = 0; + ret->do_rmdir = 0; + ret->not_exists = 0; + + ret->name = strdup (name); + if (ret->name == NULL) + { + free (ret); + errno = ENOMEM; + return NULL; + } + + ret->path = strdup (path); + if (ret->path == NULL) + { + free (ret->name); + free (ret); + errno = ENOMEM; + return NULL; + } + + if (!dir_p) + ret->children = NULL; + else + { + ret->children = hash_initialize (10, NULL, node_hasher, node_compare, node_free); + if (ret->children == NULL) + { + free (ret->path); + free (ret->name); + free (ret); + errno = ENOMEM; + return NULL; + } + } + + return ret; +} + +static struct lo_node * +insert_node (struct lo_node *parent, struct lo_node *item, bool replace) +{ + struct lo_node *old = NULL, *prev_parent = item->parent; + int ret; + + if (prev_parent) + hash_delete (prev_parent->children, item); + + if (replace) + hash_delete (parent->children, item); + + ret = hash_insert_if_absent (parent->children, item, (const void **) &old); + if (ret < 0) + { + errno = ENOMEM; + return NULL; + } + if (ret == 0) + { + node_free (item); + return old; + } + + item->parent = parent; + + if (parent->lowerdir && item->lowerdir == NULL) + item->lowerdir = hash_lookup (parent->lowerdir->children, item); + + return item; +} + +static struct lo_node * +traverse_dir (char * const dir, struct lo_node *lower, bool low) +{ + struct lo_node *root, *n, *parent; + int ret = -1; + char *const dirs[] = {dir, NULL}; + FTS *fts = fts_open (dirs, FTS_NOSTAT | FTS_COMFOLLOW, NULL); + if (fts == NULL) + return NULL; + + root = NULL; + + while (1) + { + FTSENT *ent = fts_read (fts); + if (ent == NULL) + { + if (errno) + goto err; + break; + } + + switch (ent->fts_info) + { + case FTS_D: + if (root == NULL) + { + root = make_lo_node (dir, "/", true); + root->lowerdir = lower; + root->low = low ? 1 : 0; + ent->fts_pointer = root; + } + else + { + n = make_lo_node (ent->fts_path, ent->fts_name, true); + ent->fts_pointer = n; + if (n == NULL) + goto err; + parent = (struct lo_node *) ent->fts_parent->fts_pointer; + if (insert_node (parent, n, false) == NULL) + goto err; + n->low = low ? 1 : 0; + } + break; + + case FTS_DP: + break; + + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + case FTS_DEFAULT: + n = make_lo_node (ent->fts_path, ent->fts_name, false); + if (n == NULL) + goto err; + n->low = low ? 1 : 0; + parent = (struct lo_node *) ent->fts_parent->fts_pointer; + if (insert_node (parent, n, true) == NULL) + goto err; + break; + } + } + + ret = 0; + + err: + if (ret) + { + node_mark_all_free (root); + node_free (root); + root = NULL; + } + fts_close (fts); + return root; +} + +static bool +has_prefix (const char *str, const char *pref) +{ + while (1) + { + if (*pref == '\0') + return true; + if (*str == '\0') + return false; + if (*pref != *str) + return false; + str++; + pref++; + } + return false; +} + +static struct lo_node * +merge_trees (struct lo_node *origin, struct lo_node *new) +{ + struct lo_node *it; + struct lo_node **children; + size_t i, s; + + if (!node_dirp (origin) || !node_dirp (new)) + { + if (insert_node (origin->parent, new, true) == NULL) + return NULL; + return origin; + } + + s = sizeof (*children) * hash_get_n_entries (new->children); + children = malloc (s); + if (children == NULL) + return NULL; + + s = hash_get_entries (new->children, (void **) children, s); + for (i = s; i > 0; i--) + { + struct lo_node *prev; + + it = children[i - 1]; + prev = hash_lookup (origin->children, it); + + if (has_prefix (it->name, ".wh.")) + { + struct lo_node *rm; + char *name = it->name; + + it->name = it->name + 4; + rm = hash_delete (origin->children, it); + it->name = name; + if (rm) + node_free (rm); + continue; + } + + if (prev != NULL && node_dirp (origin) && node_dirp (it)) + { + hash_delete (new->children, it); + if (merge_trees (prev, it) == NULL) + { + free (children); + return NULL; + } + } + else + { + hash_delete (new->children, it); + if (insert_node (origin, it, true) == NULL) + { + free (children); + return NULL; + } + } + } + + node_free (new); + + free (children); + return origin; +} + +static struct lo_node * +reload_dir (struct lo_node *n, char *path, char *name, struct lo_node *lowerdir) +{ + DIR *dp; + struct dirent *dent; + char *it; + int fd; + struct stat st; + struct lo_node *created = NULL; + Hash_table *whiteouts = NULL; + + if (n) + { + n->path = path; + if (n->not_exists) + return n; + } + else + { + n = created = make_lo_node (path, name, true); + if (n == NULL) + return NULL; + } + + n->lowerdir = lowerdir; + dp = opendir (path); + if (dp == NULL) + return NULL; + + whiteouts = hash_initialize (10, NULL, str_hasher, str_compare, free); + if (whiteouts == NULL) + { + closedir (dp); + errno = ENOMEM; + return NULL; + } + + fd = dirfd (dp); + while (dp && ((dent = readdir (dp)) != NULL)) + { + bool dirp; + struct lo_node *child; + char b[PATH_MAX + 1]; + struct lo_node key; + + key.name = dent->d_name; + if (hash_lookup (n->children, &key)) + continue; + + if ((strcmp (dent->d_name, ".") == 0) || strcmp (dent->d_name, "..") == 0) + continue; + + if (fstatat (fd, dent->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) + goto err; + + sprintf (b, "%s/%s", path, dent->d_name); + dirp = st.st_mode & S_IFDIR; + + if (has_prefix (dent->d_name, ".wh.")) + { + char *tmp, *name = strdup (dent->d_name + 4); + if (name == NULL) + { + errno = ENOMEM; + closedir (dp); + hash_free (whiteouts); + return NULL; + } + tmp = hash_insert (whiteouts, name); + if (tmp == NULL) + { + free (name); + errno = ENOMEM; + closedir (dp); + hash_free (whiteouts); + return NULL; + } + continue; + } + + child = make_lo_node (b, dent->d_name, dirp); + if (!child) + goto err; + + if (lowerdir) + child->lowerdir = hash_lookup (lowerdir->children, &key); + + if (dirp) + child->dirty = 1; + + if (insert_node (n, child, false) == NULL) + { + node_free (child); + goto err; + } + } + closedir (dp); + + for (it = hash_get_first (whiteouts); it; it = hash_get_next (whiteouts, it)) + { + struct lo_node key, *tmp; + + key.name = it; + tmp = (struct lo_node *) hash_delete (n->children, &key); + if (tmp) + node_free (tmp); + } + hash_free (whiteouts); + whiteouts = NULL; + + n->dirty = 0; + return n; + + err: + if (created) + node_free (created); + closedir (dp); + return NULL; +} + +static struct lo_node * +read_dirs (char *path, struct lo_node *lower, bool low) +{ + char *buf = NULL, *saveptr = NULL, *it; + struct lo_node *root = NULL; + + if (path == NULL) + return NULL; + + buf = strdup (path); + if (buf == NULL) + return NULL; + + for (it = strtok_r (path, ":", &saveptr); it; it = strtok_r (NULL, ":", &saveptr)) + { + char full_path[PATH_MAX + 1]; + struct lo_node *node; + + if (realpath (it, full_path) < 0) + return NULL; + + node = traverse_dir (full_path, lower, low); + if (node == NULL) + { + free (buf); + return NULL; + } + + if (root == NULL) + root = node; + else + { + node = merge_trees (root, node); + if (node == NULL) + { + free (buf); + node_free (root); + return NULL; + } + root = node; + } + } + free (buf); + return root; +} +static void +make_path (struct lo_node *node, const char *prefix, const char *suffix, char *b) +{ + struct lo_node *it; + struct + { + size_t l; + const char *p; + } + parts[PATH_MAX + 1]; + size_t i = 0; + + if (suffix) + { + parts[i].l = strlen (suffix); + parts[i].p = suffix; + i++; + } + for (it = node; it->parent; it = it->parent) + { + parts[i].l = strlen (it->name); + parts[i].p = it->name; + i++; + } + if (prefix) + { + if (*prefix == '/') + prefix++; + parts[i].l = strlen (prefix); + parts[i].p = prefix; + i++; + } + for (; i > 0; i--) + { + *b++ = '/'; + memcpy (b, parts[i - 1].p, parts[i - 1].l); + b += parts[i - 1].l; + } + *b = '\0'; +} + +static struct lo_node * +do_lookup_file (struct lo_data *lo, fuse_ino_t parent, const char *path) +{ + char *saveptr = NULL, *it; + char *b; + struct lo_node *node; + struct lo_node *lowerdir; + + if (parent == FUSE_ROOT_ID) + node = lo->root_upper; + else + node = (struct lo_node *) parent; + + if (path == NULL) + return node; + + lowerdir = node->lowerdir; + if (*path == '\0') + return node; + + b = alloca (strlen (path)); + strcpy (b, path); + + for (it = strtok_r (b, "/", &saveptr); it; it = strtok_r (NULL, "/", &saveptr)) + { + struct lo_node *next; + struct lo_node tmp; + + if (node->dirty) + { + node = reload_dir (node, node->path, node->name, node->lowerdir); + if (node == NULL) + return node; + } + + tmp.name = it; + + if (lowerdir && lowerdir->children) + lowerdir = hash_lookup (lowerdir->children, &tmp); + else + lowerdir = NULL; + + if (node->children == NULL && lowerdir == NULL) + return NULL; + + next = hash_lookup (node->children, &tmp); + if (next != NULL) + node = next; + else + { + char b[PATH_MAX + 1]; + + if (lowerdir == NULL) + return NULL; + + sprintf (b, "%s/.wh.%s", node->path, lowerdir->name); + if (file_exists_p (b)) + return NULL; + + sprintf (b, "%s/%s", node->path, lowerdir->name); + next = make_lo_node (b, lowerdir->name, lowerdir->children != NULL); + if (!next) + return NULL; + + next->not_exists = 1; + + next = insert_node (node, next, false); + if (next == NULL) + return NULL; + } + + node = next; + + if (lowerdir) + assert (strcmp (node->name, lowerdir->name) == 0); + + node->lowerdir = lowerdir; + } + + return node; +} + +static void +lo_lookup (fuse_req_t req, fuse_ino_t parent, const char *name) +{ + struct fuse_entry_param e; + int err = 0; + struct lo_data *lo = lo_data (req); + struct lo_node *node; + + if (lo_debug (req)) + fprintf (stderr, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", + parent, name); + + memset (&e, 0, sizeof (e)); + + node = do_lookup_file (lo, parent, name); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + if (node->dirty && !node->not_exists) + { + node = reload_dir (node, get_node_path (node), node->name, node->lowerdir); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + } + + err = rpl_stat (req, node, &e.attr); + if (err) + { + fuse_reply_err (req, errno); + return; + } + + e.ino = (fuse_ino_t) node; + node->lookups++; + e.attr_timeout = ATTR_TIMEOUT; + e.entry_timeout = ENTRY_TIMEOUT; + fuse_reply_entry (req, &e); +} + +struct lo_dirp +{ + struct lo_data *lo; + fuse_ino_t parent; + Hash_table *elements; + char **tbl; + size_t tbl_size; + size_t offset; +}; + +static struct lo_dirp * +lo_dirp (struct fuse_file_info *fi) +{ + return (struct lo_dirp *) (uintptr_t) fi->fh; +} + +static void +lo_opendir (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + int error; + DIR *dp = NULL; + char *it; + struct lo_node *node; + struct lo_node *low = NULL; + struct lo_data *lo = lo_data (req); + struct lo_dirp *d = calloc (1, sizeof (struct lo_dirp)); + struct dirent *dent; + Hash_table *whiteouts = NULL; + + node = do_lookup_file (lo, ino, NULL); + if (node == NULL) + { + errno = ENOENT; + goto out_errno; + } + + if (node->dirty) + { + node = reload_dir (node, node->path, node->name, node->lowerdir); + if (node == NULL) + goto out_errno; + } + + d->parent = ino; + d->offset = 0; + d->elements = hash_initialize (10, NULL, str_hasher, str_compare, free); + if (d->elements == NULL) + { + errno = ENOMEM; + goto out_errno; + } + + if (node->lowerdir && node->lowerdir->children) + low = node->lowerdir; + else if (node->low && node->children) + low = node; + + if (low) + { + struct lo_node *it; + + for (it = hash_get_first (low->children); it; it = hash_get_next (low->children, it)) + { + char *i; + char *el; + struct lo_node *n; + + n = do_lookup_file (lo, ino, it->name); + if (n == NULL) + continue; + + el = strdup (it->name); + if (el == NULL) + { + errno = ENOMEM; + goto out_errno; + } + + i = hash_insert (d->elements, el); + if (i == NULL) + { + errno = ENOMEM; + goto out_errno; + } + if (i != el) + free (el); + } + } + + if (!node->low) + { + dp = opendir (node->path); + if (dp == NULL && errno != ENOENT) + goto out_errno; + whiteouts = hash_initialize (10, NULL, str_hasher, str_compare, free); + if (whiteouts == NULL) + { + errno = ENOMEM; + goto out_errno; + } + + while (dp && ((dent = readdir (dp)) != NULL)) + { + char *el = NULL; + char *prev; + struct lo_node *l; + + if (strcmp (dent->d_name, ".") == 0) + l = node; + else if (strcmp (dent->d_name, "..") == 0) + { + if (node->parent) + l = node->parent; + else + continue; + } + else + { + l = do_lookup_file (lo, NODE_TO_INODE (node), dent->d_name); + } + + if (has_prefix (dent->d_name, ".wh.")) + { + char *tmp, *name = strdup (dent->d_name + 4); + if (name == NULL) + { + errno = ENOMEM; + goto out_errno; + } + tmp = hash_insert (whiteouts, name); + if (tmp == NULL) + { + free (name); + errno = ENOMEM; + goto out_errno; + } + if (tmp != name) + free (name); + continue; + } + + if (l == NULL) + continue; + + el = strdup (dent->d_name); + if (el == NULL) + { + errno = ENOMEM; + goto out_errno; + } + + prev = hash_insert (d->elements, el); + if (prev == NULL) + { + errno = ENOMEM; + goto out_errno; + } + if (prev != el) + free (el); + } + if (dp) + { + closedir (dp); + dp = NULL; + } + + for (it = hash_get_first (whiteouts); it; it = hash_get_next (whiteouts, it)) + { + char *tmp = hash_delete (d->elements, it); + if (tmp) + free (tmp); + } + hash_free (whiteouts); + whiteouts = NULL; + } + + d->tbl_size = hash_get_n_entries (d->elements); + d->tbl = malloc (sizeof (char *) * d->tbl_size); + if (d->tbl == NULL) + { + errno = ENOMEM; + goto out_errno; + } + + hash_get_entries (d->elements, (void **) d->tbl, d->tbl_size); + + fi->fh = (uintptr_t) d; + + fuse_reply_open (req, fi); + return; + +out_errno: + error = errno; + if (whiteouts) + hash_free (whiteouts); + if (dp) + closedir (dp); + if (d) + { + if (d->elements) + hash_free (d->elements); + if (d->tbl) + free (d->tbl); + free (d); + } + fuse_reply_err (req, error); +} + +static void +lo_do_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi, int plus) +{ + struct lo_dirp *d = lo_dirp (fi); + size_t remaining = size; + char *p, *buffer = calloc (size, 1); + struct lo_data *lo = lo_data (req); + + if (buffer == NULL) + { + fuse_reply_err (req, ENOMEM); + return; + } + p = buffer; + while (remaining > 0 && offset < d->tbl_size) + { + int ret; + size_t entsize; + struct stat st; + char *name = d->tbl[offset]; + struct lo_node *node; + + if (strcmp (name, ".") == 0) + { + node = do_lookup_file (lo, ino, NULL); + } + else if (strcmp (name, "..") == 0) + { + node = do_lookup_file (lo, ino, NULL); + if (node->parent) + node = node->parent; + else + { + offset++; + continue; + } + } + else + { + node = do_lookup_file (lo, ino, name); + if (node == NULL) + { + offset++; + continue; + } + } + + ret = rpl_stat (req, node, &st); + if (ret < 0) + { + fuse_reply_err (req, errno); + goto exit; + } + + if (!plus) + entsize = fuse_add_direntry (req, p, remaining, name, &st, offset + 1); + else + { + struct fuse_entry_param e; + + memset (&e, 0, sizeof (e)); + e.attr_timeout = ATTR_TIMEOUT; + e.entry_timeout = ENTRY_TIMEOUT; + e.ino = NODE_TO_INODE (node); + if ((strcmp (name, ".") != 0) && (strcmp (name, "..") != 0)) + node->lookups++; + memcpy (&e.attr, &st, sizeof (st)); + + entsize = fuse_add_direntry_plus (req, p, remaining, name, &e, offset + 1); + } + + p += entsize; + offset++; + + if (entsize > remaining) + break; + + remaining -= entsize; + } + fuse_reply_buf (req, buffer, size - remaining); + exit: + free (buffer); +} + +static void +lo_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + lo_do_readdir (req, ino, size, offset, fi, 0); +} + +static void +lo_readdirplus (fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + lo_do_readdir (req, ino, size, offset, fi, 1); +} + +static void +lo_releasedir (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + struct lo_dirp *d = lo_dirp (fi); + if (d->elements) + hash_free (d->elements); + free (d->tbl); + free (d); + fuse_reply_err (req, 0); +} + +static void +lo_listxattr (fuse_req_t req, fuse_ino_t ino, size_t size) +{ + ssize_t len; + struct lo_node *node; + struct lo_data *lo = lo_data (req); + char buf[1024]; + + if (lo_debug (req)) + fprintf (stderr, "lo_listxattr(ino=%" PRIu64 ", size=%zu)\n", ino, size); + + node = do_lookup_file (lo, ino, NULL); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + len = llistxattr (get_node_path (node), buf, sizeof (buf)); + if (len < 0) + fuse_reply_err (req, errno); + else if (size == 0) + fuse_reply_xattr (req, len); + else if (len <= size) + fuse_reply_buf (req, buf, len); + +} + +static void +lo_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) +{ + ssize_t len; + struct lo_node *node; + struct lo_data *lo = lo_data (req); + char buf[1024]; + + if (lo_debug (req)) + fprintf (stderr, "lo_getxattr(ino=%" PRIu64 ", name=%s, size=%zu)\n", ino, name, size); + + node = do_lookup_file (lo, ino, NULL); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + len = lgetxattr (get_node_path (node), name, buf, sizeof (buf)); + if (len < 0) + fuse_reply_err (req, errno); + else if (size == 0) + fuse_reply_xattr (req, len); + else if (len <= size) + fuse_reply_buf (req, buf, len); +} + +static void +lo_access (fuse_req_t req, fuse_ino_t ino, int mask) +{ + int ret; + struct lo_data *lo = lo_data (req); + struct lo_node *n = do_lookup_file (lo, ino, NULL); + + if (lo_debug (req)) + fprintf (stderr, "lo_access(ino=%" PRIu64 ", mask=%d)\n", + ino, mask); + + ret = access (get_node_path (n), mask); + fuse_reply_err (req, ret < 0 ? errno : 0); +} + +static int +create_directory (struct lo_data *lo, struct lo_node *src) +{ + int ret; + struct stat st; + char *dest_path; + + if (src == NULL) + return 0; + + if (! src->not_exists) + return 0; + + dest_path = src->path; + + ret = lstat (src->lowerdir->path, &st); + if (ret < 0) + goto out; + + ret = mkdir (dest_path, st.st_mode); + if (ret < 0 && errno == EEXIST) + { + ret = 0; + goto out; + } + else if (ret < 0 && errno == ENOENT) + { + ret = create_directory (lo, src->parent); + if (ret != 0) + goto out; + + ret = mkdir (dest_path, st.st_mode); + if (ret < 0) + goto out; + + ret = chown (dest_path, st.st_uid, st.st_gid); + } + +out: + if (ret == 0) + { + src->not_exists = 0; + + if (src->parent) + { + char wh[PATH_MAX]; + sprintf (wh, "%s/.wh.%s", src->parent->path, src->name); + unlink (wh); + } + } + + return ret; +} + +static int +copyup (struct lo_data *lo, struct lo_node *node) +{ + int saved_errno; + int ret = -1; + int dfd = -1, sfd = -1; + struct stat st; + int r; + const char *src = node->lowerdir->path; + const char *dst = node->path; + struct timespec times[2]; + + if (node->parent) + { + r = create_directory (lo, node->parent); + if (r < 0) + return r; + } + + if (lstat (src, &st) < 0) + goto exit; + + if (node->parent) + { + char whpath[PATH_MAX + 10]; + sprintf (whpath, "%s/.wh.%s", node->parent->path, node->name); + if (unlink (whpath) < 0 && errno != ENOENT) + goto exit; + } + + if ((st.st_mode & S_IFMT) == S_IFDIR) + { + ret = create_directory (lo, node); + if (ret < 0) + goto exit; + goto success; + } + + if ((st.st_mode & S_IFMT) == S_IFLNK) + { + char p[PATH_MAX + 1]; + ret = readlink (src, p, sizeof (p) - 1); + if (ret < 0) + goto exit; + p[ret] = '\0'; + ret = symlink (p, dst); + if (ret < 0) + goto exit; + goto success; + } + + sfd = open (src, O_RDONLY); + if (sfd < 0) + goto exit; + + dfd = open (dst, O_WRONLY|O_CREAT, st.st_mode); + if (dfd < 0) + goto exit; + + ret = fchown (dfd, st.st_uid, st.st_gid); + if (ret < 0) + goto exit; + + for (;;) + { + int written; + int nread; + char buf[4096]; + + nread = TEMP_FAILURE_RETRY (read (sfd, buf, sizeof (buf))); + if (nread < 0) + goto exit; + + if (nread == 0) + break; + + written = 0; + { + r = TEMP_FAILURE_RETRY (write (dfd, buf + written, nread)); + if (r < 0) + goto exit; + + written += r; + nread -= r; + } + while (nread); + } + + times[0] = st.st_atim; + times[1] = st.st_mtim; + if (futimens (dfd, times) < 0) + goto exit; + + success: + ret = 0; + + node->not_exists = 0; + + exit: + saved_errno = errno; + if (sfd >= 0) + close (sfd); + if (dfd >= 0) + close (dfd); + errno = saved_errno; + + return ret; +} + +static struct lo_node * +get_node_up (struct lo_data *lo, struct lo_node *node) +{ + int ret; + + if (!node->lowerdir || !node->not_exists) + return node; + + ret = copyup (lo, node); + if (ret < 0) + return NULL; + + assert (has_prefix (node->path, lo->upperdir)); + + return node; +} + +static size_t +count_dir_entries (struct lo_node *node) +{ + size_t c = 0; + struct lo_node *it; + + for (it = hash_get_first (node->children); it; it = hash_get_next (node->children, it)) + { + if (strcmp (it->name, ".") == 0) + continue; + if (strcmp (it->name, "..") == 0) + continue; + c++; + } + return c; +} + +static int +update_paths (struct lo_node *node) +{ + struct lo_node *it; + + if (node == NULL) + return 0; + + if (node->parent) + { + free (node->path); + node->path = NULL; + if (asprintf (&node->path, "%s/%s", node->parent->path, node->name) < 0) + return -1; + } + + if (node->children) + { + for (it = hash_get_first (node->children); it; it = hash_get_next (node->children, it)) + { + if (update_paths (it) < 0) + return -1; + } + } + + return 0; +} + +static void +do_rm (fuse_req_t req, fuse_ino_t parent, const char *name, bool dirp) +{ + struct lo_node *node; + struct lo_data *lo = lo_data (req); + struct lo_node *pnode; + int fd; + int ret = 0; + char whiteout_path[PATH_MAX + 10]; + struct lo_node key, *rm; + + node = do_lookup_file (lo, parent, name); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + if (! node->low) + { + if (! dirp) + node->do_unlink = 1; + else + { + DIR *dp; + size_t c = 0; + + if (node->dirty) + { + node = reload_dir (node, node->path, node->name, node->lowerdir); + if (node == NULL) + { + fuse_reply_err (req, errno); + return; + } + } + + if (node->children) + c = count_dir_entries (node); + if (c == 0 && node->lowerdir && node->lowerdir->children) + c = count_dir_entries (node->lowerdir); + if (c) + { + fuse_reply_err (req, ENOTEMPTY); + return; + } + + dp = opendir (node->path); + if (dp) + { + struct dirent *dent; + + while (dp && ((dent = readdir (dp)) != NULL)) + { + if (strcmp (dent->d_name, ".") == 0) + continue; + if (strcmp (dent->d_name, "..") == 0) + continue; + if (unlinkat (dirfd (dp), dent->d_name, 0) < 0) + unlinkat (dirfd (dp), dent->d_name, AT_REMOVEDIR); + } + + closedir (dp); + } + + node->do_rmdir = 1; + } + } + + pnode = do_lookup_file (lo, parent, NULL); + if (pnode == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + pnode = get_node_up (lo, pnode); + if (pnode == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + sprintf (whiteout_path, "%s/.wh.%s", get_node_path (pnode), name); + fd = creat (whiteout_path, 0700); + if (fd < 0) + { + fuse_reply_err (req, errno); + return; + } + close (fd); + + ret = 0; + + key.name = (char *) name; + rm = hash_delete (pnode->children, &key); + if (rm) + { + hide_node (lo, rm); + node_free (rm); + } + + fuse_reply_err (req, ret); +} + +static void +lo_unlink (fuse_req_t req, fuse_ino_t parent, const char *name) +{ + if (lo_debug (req)) + fprintf (stderr, "lo_unlink(parent=%" PRIu64 ", name=%s)\n", + parent, name); + do_rm (req, parent, name, false); +} + +static void +lo_rmdir (fuse_req_t req, fuse_ino_t parent, const char *name) +{ + if (lo_debug (req)) + fprintf (stderr, "lo_rmdir(parent=%" PRIu64 ", name=%s)\n", + parent, name); + do_rm (req, parent, name, true); +} + +static void +lo_setxattr (fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags) +{ + struct lo_data *lo = lo_data (req); + struct lo_node *node; + + if (lo_debug (req)) + fprintf (stderr, "lo_setxattr(ino=%" PRIu64 "s, name=%s, value=%s, size=%zu, flags=%d)\n", ino, name, + value, size, flags); + + node = do_lookup_file (lo, ino, NULL); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + node = get_node_up (lo, node); + if (node == NULL) + { + fuse_reply_err (req, errno); + return; + } + + if (setxattr (node->path, name, value, size, flags) < 0) + { + fuse_reply_err (req, errno); + return; + } + + fuse_reply_err (req, 0); +} + +static void +lo_removexattr (fuse_req_t req, fuse_ino_t ino, const char *name) +{ + struct lo_node *node; + struct lo_data *lo = lo_data (req); + + if (lo_debug (req)) + fprintf (stderr, "lo_removexattr(ino=%" PRIu64 "s, name=%s)\n", ino, name); + + node = do_lookup_file (lo, ino, NULL); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + node = get_node_up (lo, node); + if (node == NULL) + { + fuse_reply_err (req, errno); + return; + } + + if (removexattr (node->path, name) < 0) + { + fuse_reply_err (req, errno); + return; + } + + fuse_reply_err (req, 0); +} + +static int +lo_do_open (fuse_req_t req, fuse_ino_t parent, const char *name, int flags, mode_t mode) +{ + struct lo_data *lo = lo_data (req); + struct lo_node *n; + bool readonly = (flags & (O_APPEND | O_RDWR | O_WRONLY | O_CREAT | O_TRUNC)) == 0; + char path[PATH_MAX + 10]; + int fd, ret; + + flags |= O_NOFOLLOW; + + if (name && has_prefix (name, ".wh.")) + { + errno = EINVAL; + return - 1; + } + + n = do_lookup_file (lo, parent, name); + if (n && n->hidden) + { + n = NULL; + } + if (n && (flags & O_CREAT)) + { + errno = EEXIST; + return -1; + } + + if (!n) + { + struct lo_node *p; + + if ((flags & O_CREAT) == 0) + { + errno = ENOENT; + return -1; + } + + p = do_lookup_file (lo, parent, NULL); + if (p == NULL) + { + errno = ENOENT; + return -1; + } + + p = get_node_up (lo, p); + if (p == NULL) + { + return -1; + } + + sprintf (path, "%s/.wh.%s", p->path, name); + unlink (path); + + sprintf (path, "%s/%s", p->path, name); + unlink (path); + fd = open (path, flags, mode); + if (fd < 0) + { + return -1; + } + + n = make_lo_node (path, name, false); + if (n == NULL) + { + p->dirty = 1; + errno = ENOMEM; + return -1; + } + if (insert_node (p, n, true) == NULL) + { + p->dirty = 1; + node_free (n); + errno = ENOMEM; + return -1; + } + + return fd; + } + + /* readonly, we can use both lowerdir and upperdir. */ + if (readonly) + { + ret = open (get_node_path (n), flags, mode); + if (ret < 0) + return ret; + + return ret; + } + else + { + n = get_node_up (lo, n); + if (n == NULL) + return -1; + + fd = open (n->path, flags, mode); + if (fd < 0) + return fd; + + return fd; + } +} + +static void +lo_read (fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + struct fuse_bufvec buf = FUSE_BUFVEC_INIT (size); + if (lo_debug (req)) + fprintf (stderr, "lo_read(ino=%" PRIu64 ", size=%zd, " + "off=%lu)\n", ino, size, (unsigned long) offset); + buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; + buf.buf[0].fd = fi->fh; + buf.buf[0].pos = offset; + fuse_reply_data (req, &buf, FUSE_BUF_SPLICE_MOVE); +} + +static void +lo_write_buf (fuse_req_t req, fuse_ino_t ino, + struct fuse_bufvec *in_buf, off_t off, + struct fuse_file_info *fi) +{ + (void) ino; + ssize_t res; + struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT (fuse_buf_size (in_buf)); + out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; + out_buf.buf[0].fd = fi->fh; + out_buf.buf[0].pos = off; + + if (lo_debug (req)) + fprintf (stderr, "lo_write_buf(ino=%" PRIu64 ", size=%zd, off=%lu, fd=%d)\n", + ino, out_buf.buf[0].size, (unsigned long) off, (int) fi->fh); + + errno = 0; + res = fuse_buf_copy (&out_buf, in_buf, 0); + if (res < 0) + fuse_reply_err (req, errno); + else + fuse_reply_write (req, (size_t) res); +} + +static void +lo_release (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + (void) ino; + close (fi->fh); + fuse_reply_err (req, 0); +} + +static int +do_getattr (fuse_req_t req, struct fuse_entry_param *e, struct lo_node *node) +{ + int err = 0; + + memset (e, 0, sizeof (*e)); + + err = rpl_stat (req, node, &e->attr); + if (err < 0) + return err; + + e->ino = (fuse_ino_t) node; + e->attr_timeout = ATTR_TIMEOUT; + e->entry_timeout = ENTRY_TIMEOUT; + + return 0; +} + +static void +lo_create (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi) +{ + int fd; + struct fuse_entry_param e; + struct lo_data *lo = lo_data (req); + struct lo_node *node; + + if (lo_debug (req)) + fprintf (stderr, "lo_create(parent=%" PRIu64 ", name=%s)\n", + parent, name); + + fi->flags = fi->flags | O_CREAT; + + fd = lo_do_open (req, parent, name, fi->flags, mode); + if (fd < 0) + { + fuse_reply_err (req, errno); + return; + } + + node = do_lookup_file (lo, parent, name); + if (node == NULL || do_getattr (req, &e, node) < 0) + { + close (fd); + fuse_reply_err (req, errno); + return; + } + fi->fh = fd; + + node->lookups++; + fuse_reply_create (req, &e, fi); +} + +static void +lo_open (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + int fd; + + if (lo_debug (req)) + fprintf (stderr, "lo_open(ino=%" PRIu64 "s)\n", ino); + + fd = lo_do_open (req, ino, NULL, fi->flags, 0700); + if (fd < 0) + { + fuse_reply_err (req, errno); + return; + } + fi->fh = fd; + fuse_reply_open (req, fi); +} + +static void +lo_getattr (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) +{ + struct lo_data *lo = lo_data (req); + struct lo_node *node; + struct fuse_entry_param e; + + if (lo_debug (req)) + fprintf (stderr, "lo_getattr(ino=%" PRIu64 "s)\n", ino); + + node = do_lookup_file (lo, ino, NULL); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + if (do_getattr (req, &e, node) < 0) + { + fuse_reply_err (req, errno); + return; + } + + fuse_reply_attr (req, &e.attr, ENTRY_TIMEOUT); +} + +static void +lo_setattr (fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi) +{ + struct lo_data *lo = lo_data (req); + struct lo_node *node; + struct fuse_entry_param e; + struct stat old_st; + struct timespec times[2]; + uid_t uid; + gid_t gid; + + if (lo_debug (req)) + fprintf (stderr, "lo_setattr(ino=%" PRIu64 "s, to_set=%d)\n", ino, to_set); + + node = do_lookup_file (lo, ino, NULL); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + node = get_node_up (lo, node); + if (node == NULL) + { + fuse_reply_err (req, errno); + return; + } + + if (lstat (node->path, &old_st) < 0) + { + fuse_reply_err (req, errno); + return; + } + + if (to_set & FUSE_SET_ATTR_CTIME) + { + fuse_reply_err (req, EPERM); + return; + } + + times[0].tv_sec = UTIME_OMIT; + times[1].tv_sec = UTIME_OMIT; + if (to_set & FUSE_SET_ATTR_ATIME) + times[0] = attr->st_atim; + else if (to_set & FUSE_SET_ATTR_ATIME_NOW) + times[0].tv_sec = UTIME_NOW; + + if (to_set & FUSE_SET_ATTR_MTIME) + times[1] = attr->st_mtim; + else if (to_set & FUSE_SET_ATTR_MTIME_NOW) + times[1].tv_sec = UTIME_NOW; + + if (times[0].tv_sec != UTIME_OMIT || times[1].tv_sec != UTIME_OMIT) + { + if ((utimensat (AT_FDCWD, node->path, times, AT_SYMLINK_NOFOLLOW) < 0)) + { + fuse_reply_err (req, errno); + return; + } + } + + if ((to_set & FUSE_SET_ATTR_MODE) && chmod (node->path, attr->st_mode) < 0) + { + fuse_reply_err (req, errno); + return; + } + if ((to_set & FUSE_SET_ATTR_SIZE) && truncate (node->path, attr->st_size) < 0) + { + fuse_reply_err (req, errno); + return; + } + + uid = old_st.st_uid; + gid = old_st.st_gid; + if (to_set & FUSE_SET_ATTR_UID) + uid = get_uid (lo, attr->st_uid); + if (to_set & FUSE_SET_ATTR_GID) + gid = get_gid (lo, attr->st_gid); + + if (uid != old_st.st_uid || gid != old_st.st_gid) + { + if (lchown (node->path, uid, gid) < 0) + { + fuse_reply_err (req, errno); + return; + } + } + + if (do_getattr (req, &e, node) < 0) + { + fuse_reply_err (req, errno); + return; + } + + fuse_reply_attr (req, &e.attr, ENTRY_TIMEOUT); +} + +static void +lo_link (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname) +{ + struct lo_data *lo = lo_data (req); + struct lo_node *node, *newparentnode, *destnode; + char path[PATH_MAX + 10]; + int ret; + struct fuse_entry_param e; + + if (lo_debug (req)) + fprintf (stderr, "lo_link(ino=%" PRIu64 "s, newparent=%" PRIu64 "s, newname=%s)\n", ino, newparent, newname); + + node = do_lookup_file (lo, newparent, newname); + if (node != NULL) + { + fuse_reply_err (req, EEXIST); + return; + } + + node = do_lookup_file (lo, ino, NULL); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + node = get_node_up (lo, node); + if (node == NULL) + { + fuse_reply_err (req, errno); + return; + } + + newparentnode = do_lookup_file (lo, newparent, NULL); + if (newparentnode == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + destnode = do_lookup_file (lo, newparent, newname); + if (destnode) + { + fuse_reply_err (req, EEXIST); + return; + } + + newparentnode = get_node_up (lo, newparentnode); + if (newparentnode == NULL) + { + fuse_reply_err (req, errno); + return; + } + + sprintf (path, "%s/.wh.%s", newparentnode->path, newname); + if (unlink (path) < 0 && errno != ENOENT) + { + fuse_reply_err (req, errno); + return; + } + + sprintf (path, "%s/%s", newparentnode->path, newname); + if (link (get_node_path (node), path) < 0) + { + fuse_reply_err (req, errno); + return; + } + + node = make_lo_node (path, newname, false); + if (node == NULL) + { + fuse_reply_err (req, ENOMEM); + return; + } + if (insert_node (newparentnode, node, true) == NULL) + { + node_free (node); + fuse_reply_err (req, ENOMEM); + return; + } + + memset (&e, 0, sizeof (e)); + + ret = rpl_stat (req, node, &e.attr); + if (ret) + { + fuse_reply_err (req, errno); + return; + } + + e.ino = (fuse_ino_t) node; + node->lookups++; + e.attr_timeout = ATTR_TIMEOUT; + e.entry_timeout = ENTRY_TIMEOUT; + fuse_reply_entry (req, &e); +} + +static void +lo_symlink (fuse_req_t req, const char *link, fuse_ino_t parent, const char *name) +{ + struct lo_data *lo = lo_data (req); + struct lo_node *pnode, *node; + char path[PATH_MAX + 10]; + int ret; + struct fuse_entry_param e; + + if (lo_debug (req)) + fprintf (stderr, "lo_symlink(link=%s, ino=%" PRIu64 "s, name=%s)\n", link, parent, name); + + pnode = do_lookup_file (lo, parent, NULL); + if (pnode == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + pnode = get_node_up (lo, pnode); + if (pnode == NULL) + { + fuse_reply_err (req, errno); + return; + } + + node = do_lookup_file (lo, parent, name); + if (node != NULL) + { + fuse_reply_err (req, EEXIST); + return; + } + + sprintf (path, "%s/.wh.%s", pnode->path, name); + if (unlink (path) < 0 && errno != ENOENT) + { + fuse_reply_err (req, errno); + return; + } + + sprintf (path, "%s/%s", pnode->path, name); + ret = symlink (link, path); + if (ret < 0) + { + fuse_reply_err (req, errno); + return; + } + + node = make_lo_node (path, name, false); + if (node == NULL) + { + fuse_reply_err (req, ENOMEM); + return; + } + if (insert_node (pnode, node, true) == NULL) + { + node_free (node); + fuse_reply_err (req, ENOMEM); + return; + } + + memset (&e, 0, sizeof (e)); + + ret = rpl_stat (req, node, &e.attr); + if (ret) + { + fuse_reply_err (req, errno); + return; + } + + e.ino = (fuse_ino_t) node; + node->lookups++; + e.attr_timeout = ATTR_TIMEOUT; + e.entry_timeout = ENTRY_TIMEOUT; + fuse_reply_entry (req, &e); +} + +static +void lo_flock (fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi, int op) +{ + int ret, fd; + + if (lo_debug (req)) + fprintf (stderr, "lo_flock(ino=%" PRIu64 "s, op=%d)\n", ino, op); + + fd = fi->fh; + + ret = flock (fd, op); + + fuse_reply_err (req, ret == 0 ? 0 : errno); +} + +/* used to recover a failed lo_rename. */ +static void +unhide_node (struct lo_node *node, struct lo_node *parent) +{ + char b[PATH_MAX]; + + if (node->path == NULL) + return; + + sprintf (b, "%s/%s", parent->path, node->name); + rename (node->path, b); + free (node->path); + node->path = strdup (b); + insert_node (parent, node, true); +} + +static struct lo_node * +get_node_up_rec (struct lo_data *lo, struct lo_node *node) +{ + int ret; + struct lo_node *it; + struct lo_node *l; + + if (node->children) + { + for (it = hash_get_first (node->children); it; it = hash_get_next (node->children, it)) + { + l = get_node_up_rec (lo, it); + if (l == NULL) + return NULL; + } + } + if (node->lowerdir && node->lowerdir->children) + { + for (it = hash_get_first (node->lowerdir->children); it; it = hash_get_next (node->lowerdir->children, it)) + { + l = do_lookup_file (lo, NODE_TO_INODE (node), it->name); + if (l) + { + l = get_node_up_rec (lo, l); + if (l == NULL) + return NULL; + } + } + } + + return get_node_up (lo, node); +} + +static void +lo_rename (fuse_req_t req, fuse_ino_t parent, const char *name, + fuse_ino_t newparent, const char *newname, + unsigned int flags) +{ + struct lo_node *pnode, *node, *destnode, *destpnode; + struct lo_data *lo = lo_data (req); + int ret; + int saved_errno; + char path[PATH_MAX + 1]; + int srcfd = -1; + int destfd = -1; + struct lo_node key, *rm = NULL; + + if (lo_debug (req)) + fprintf (stderr, "lo_rename(ino=%" PRIu64 "s, name=%s , ino=%" PRIu64 "s, name=%s)\n", parent, name, newparent, newname); + + node = do_lookup_file (lo, parent, name); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + pnode = node->parent; + + destpnode = do_lookup_file (lo, newparent, NULL); + destnode = NULL; + + pnode = get_node_up (lo, pnode); + if (pnode == NULL) + goto error; + + ret = open (pnode->path, O_DIRECTORY); + if (ret < 0) + goto error; + srcfd = ret; + + destpnode = get_node_up (lo, destpnode); + if (destpnode == NULL) + goto error; + + ret = open (destpnode->path, O_DIRECTORY); + if (ret < 0) + goto error; + destfd = ret; + + destnode = do_lookup_file (lo, newparent, newname); + + node = get_node_up_rec (lo, node); + if (node == NULL) + goto error; + + if (flags & RENAME_EXCHANGE) + { + if (destnode == NULL) + { + errno = ENOENT; + goto error; + } + destnode = get_node_up_rec (lo, destnode); + if (destnode == NULL) + goto error; + } + else + { + key.name = (char *) newname; + if (flags & RENAME_NOREPLACE) + { + rm = hash_lookup (destpnode->children, &key); + if (rm) + { + errno = EEXIST; + goto error; + } + + } + if (destnode != NULL && node_dirp (destnode)) + { + errno = EISDIR; + goto error; + } + + rm = hash_delete (destpnode->children, &key); + if (rm) + { + hide_node (lo, rm); + node_free (rm); + } + } + + ret = syscall (SYS_renameat2, srcfd, name, destfd, newname, flags); + if (ret < 0) + { + if (rm) + unhide_node (rm, destpnode); + + pnode->dirty = destpnode->dirty = 1; + goto error; + } + + if (flags & RENAME_EXCHANGE) + { + struct lo_node *rm1, *rm2; + char *tmp; + + rm1 = hash_delete (destpnode->children, destnode); + rm2 = hash_delete (pnode->children, node); + + tmp = node->path; + node->path = destnode->path; + destnode->path = tmp; + + tmp = node->name; + node->name = destnode->name; + destnode->name = tmp; + + if (insert_node (destpnode, node, true) == NULL) + { + node_free (rm1); + node_free (rm2); + goto error; + } + if (insert_node (pnode, destnode, true) == NULL) + { + node_free (rm1); + node_free (rm2); + goto error; + } + if ((update_paths (node) < 0) || (update_paths (destnode) < 0)) + goto error; + } + else + { + int fd; + struct lo_node *rm; + + sprintf (path, ".wh.%s", name); + fd = openat (srcfd, path, O_CREAT, 0700); + if (fd < 0) + goto error; + close (fd); + + key.name = (char *) name; + hash_delete (pnode->children, &key); + + free (node->name); + node->name = strdup (newname); + if (node->name == NULL) + goto error; + + if (insert_node (destpnode, node, true) == NULL) + goto error; + if (update_paths (node) < 0) + goto error; + } + + sprintf (path, ".wh.%s", newname); + if (unlinkat (destfd, path, 0) < 0 && errno != ENOENT) + goto error; + + error: + saved_errno = errno; + if (srcfd >= 0) + close (srcfd); + if (destfd >= 0) + close (destfd); + errno = saved_errno; + + fuse_reply_err (req, ret == 0 ? 0 : errno); +} + +static void +lo_statfs (fuse_req_t req, fuse_ino_t ino) +{ + int ret; + struct statvfs sfs; + struct lo_data *lo = lo_data (req); + + if (lo_debug (req)) + fprintf (stderr, "lo_statfs(ino=%" PRIu64 "s)\n", ino); + + ret = statvfs (lo->upperdir, &sfs); + if (ret < 0) + { + fuse_reply_err (req, errno); + return; + } + fuse_reply_statfs (req, &sfs); +} + +static void +lo_readlink (fuse_req_t req, fuse_ino_t ino) +{ + struct lo_node *node; + struct lo_data *lo = lo_data (req); + int ret = 0; + char buf[PATH_MAX + 1]; + + if (lo_debug (req)) + fprintf (stderr, "lo_readlink(ino=%" PRIu64 "s)\n", ino); + + node = do_lookup_file (lo, ino, NULL); + if (node == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + ret = readlink (get_node_path (node), buf, sizeof (buf)); + if (ret == -1) + { + fuse_reply_err (req, errno); + return; + } + if (ret == sizeof (buf)) + { + fuse_reply_err (req, ENAMETOOLONG); + return; + } + + buf[ret] = '\0'; + fuse_reply_readlink (req, buf); +} + +static int +hide_all (struct lo_node *node) +{ + char b[PATH_MAX]; + struct lo_node *it; + + if (node->lowerdir == NULL || node->lowerdir->children == NULL) + return 0; + + for (it = hash_get_first (node->lowerdir->children); it; it = hash_get_next (node->lowerdir->children, it)) + { + int fd; + + sprintf (b, "%s/.wh.%s", node->path, it->name); + + fd = open (b, O_CREAT, 0700); + if (fd < 0 && errno != EEXIST) + return fd; + close (fd); + } + return 0; +} + +static void +lo_mkdir (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) +{ + struct lo_node *node; + struct lo_data *lo = lo_data (req); + struct lo_node *pnode; + int ret = 0; + char path[PATH_MAX + 10]; + char whiteout_path[PATH_MAX + 16]; + struct fuse_entry_param e; + + if (lo_debug (req)) + fprintf (stderr, "lo_mkdir(ino=%" PRIu64 ", name=%s, mode=%d)\n", + parent, name, mode); + + node = do_lookup_file (lo, parent, name); + if (node != NULL) + { + fuse_reply_err (req, EEXIST); + return; + } + + pnode = do_lookup_file (lo, parent, NULL); + if (pnode == NULL) + { + fuse_reply_err (req, ENOENT); + return; + } + + pnode = get_node_up (lo, pnode); + if (pnode == NULL) + { + fuse_reply_err (req, errno); + return; + } + sprintf (path, "%s/%s", pnode->path, name); + + rmdir (path); + ret = mkdir (path, mode); + if (ret < 0) + { + fuse_reply_err (req, errno); + return; + } + + node = make_lo_node (path, name, true); + if (node == NULL) + { + fuse_reply_err (req, ENOMEM); + return; + } + if (insert_node (pnode, node, true) == NULL) + { + node_free (node); + fuse_reply_err (req, ENOMEM); + return; + } + ret = hide_all (node); + if (ret < 0) + { + fuse_reply_err (req, errno); + return; + } + + sprintf (whiteout_path, "%s/.wh.%s", pnode->path, name); + ret = unlink (whiteout_path); + if (ret < 0 && errno != ENOENT) + { + fuse_reply_err (req, errno); + return; + } + + memset (&e, 0, sizeof (e)); + + ret = rpl_stat (req, node, &e.attr); + if (ret) + { + fuse_reply_err (req, errno); + return; + } + + e.ino = (fuse_ino_t) node; + e.attr_timeout = ATTR_TIMEOUT; + e.entry_timeout = ENTRY_TIMEOUT; + node->lookups++; + fuse_reply_entry (req, &e); +} + +static void +lo_fsync (fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) +{ + int ret, fd; + + if (lo_debug (req)) + fprintf (stderr, "lo_fsync(ino=%" PRIu64 ", datasync=%d, fi=%p)\n", + ino, datasync, fi); + + fd = fi->fh; + ret = datasync ? fdatasync (fd) : fsync (fd); + fuse_reply_err (req, ret == 0 ? 0 : errno); +} + +static struct fuse_lowlevel_ops lo_oper = { + .statfs = lo_statfs, + .access = lo_access, + .getxattr = lo_getxattr, + .removexattr = lo_removexattr, + .setxattr = lo_setxattr, + .listxattr = lo_listxattr, + .init = lo_init, + .lookup = lo_lookup, + .forget = lo_forget, + .getattr = lo_getattr, + .readlink = lo_readlink, + .opendir = lo_opendir, + .readdir = lo_readdir, + .readdirplus = lo_readdirplus, + .releasedir = lo_releasedir, + .create = lo_create, + .open = lo_open, + .release = lo_release, + .read = lo_read, + .write_buf = lo_write_buf, + .unlink = lo_unlink, + .rmdir = lo_rmdir, + .setattr = lo_setattr, + .symlink = lo_symlink, + .rename = lo_rename, + .mkdir = lo_mkdir, + .link = lo_link, + .fsync = lo_fsync, + .flock = lo_flock, +}; + +static int +fuse_opt_proc (void *data, const char *arg, int key, struct fuse_args *outargs) +{ + if (strcmp (arg, "-f") == 0) + return 1; + if (strcmp (arg, "--debug") == 0) + return 1; + + /* Ignore unknown arguments. */ + if (key == -1) + return 0; + + return 1; +} + +int +main (int argc, char *argv[]) +{ + struct fuse_args args = FUSE_ARGS_INIT (argc, argv); + struct fuse_session *se; + struct fuse_cmdline_opts opts; + struct lo_data lo = {.debug = 0, + .uid = -1, + .gid = -1, + .root_lower = NULL, + .root_upper = NULL, + .lowerdir = NULL, + }; + int ret = -1; + + unsetenv ("TMPDIR"); + + if (fuse_opt_parse (&args, &lo, lo_opts, fuse_opt_proc) == -1) + return 1; + if (fuse_parse_cmdline (&args, &opts) != 0) + return 1; + if (opts.show_help) + { + printf ("usage: %s [options] \n\n", argv[0]); + fuse_cmdline_help (); + fuse_lowlevel_help (); + ret = 0; + goto err_out1; + } + else if (opts.show_version) + { + printf ("FUSE library version %s\n", fuse_pkgversion ()); + fuse_lowlevel_version (); + ret = 0; + goto err_out1; + } + + lo.debug = opts.debug; + + if (lo.upperdir != NULL) + { + char full_path[PATH_MAX + 1]; + + if (realpath (lo.upperdir, full_path) < 0) + goto err_out1; + + lo.upperdir = strdup (full_path); + if (lo.upperdir == NULL) + goto err_out1; + } + else + { + char *up = strdup ("/tmp/containerfs.XXXXXX"); + int ret = mkstemp (up); + if (ret < 0) + goto err_out1; + + close (ret); + lo.upperdir = up; + } + + printf ("UID=%i\n", lo.uid); + printf ("GID=%i\n", lo.gid); + printf ("UPPERDIR=%s\n", lo.upperdir); + printf ("WORKDIR=%s\n", lo.workdir); + printf ("LOWERDIR=%s\n", lo.lowerdir); + printf ("MOUNTPOINT=%s\n", opts.mountpoint); + + lo.root_lower = read_dirs (lo.lowerdir, NULL, true); + if (lo.root_lower == NULL) + error (EXIT_FAILURE, errno, "cannot read lower dirs"); + + lo.root_upper = reload_dir (NULL, lo.upperdir, "/", lo.root_lower); + if (lo.root_upper == NULL) + error (EXIT_FAILURE, errno, "cannot read upper dir"); + lo.root_upper->lookups = 2; + lo.root_upper->lowerdir = lo.root_lower; + + if (lo.workdir) + { + char path[PATH_MAX + 1]; + + if (realpath (lo.workdir, path) < 0) + goto err_out1; + mkdir (path, 0700); + strcat (path, "/work"); + mkdir (path, 0700); + free (lo.workdir); + lo.workdir = strdup (path); + } + + se = fuse_session_new (&args, &lo_oper, sizeof (lo_oper), &lo); + lo.se = se; + if (se == NULL) + goto err_out1; + if (fuse_set_signal_handlers (se) != 0) + goto err_out2; + if (fuse_session_mount (se, opts.mountpoint) != 0) + goto err_out3; + fuse_daemonize (opts.foreground); + ret = fuse_session_loop (se); + fuse_session_unmount (se); +err_out3: + fuse_remove_signal_handlers (se); +err_out2: + fuse_session_destroy (se); +err_out1: + + node_mark_all_free (lo.root_lower); + node_mark_all_free (lo.root_upper); + + node_free (lo.root_lower); + node_free (lo.root_upper); + + free (opts.mountpoint); + fuse_opt_free_args (&args); + + return ret ? 1 : 0; +}