summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRadek Podgorny <radek@podgorny.cz>2012-02-03 10:26:44 +0100
committerRadek Podgorny <radek@podgorny.cz>2012-02-03 10:26:44 +0100
commita41c6ff888754416becd3df07b7fd4dd73c71c4c (patch)
tree6bd4a06d45543832535f50bc6206b9b928272088
parentae08a93bdb680a2b19666c6f244cd49d2b0c9e3d (diff)
parentf9c6f75d12b80d5b1f38d49c57f39b97abdff9f2 (diff)
downloadunionfs-fuse-a41c6ff888754416becd3df07b7fd4dd73c71c4c.tar.gz
merge with Bernd
-rw-r--r--.hgignore4
-rw-r--r--CMakeLists.txt16
-rw-r--r--NEWS17
-rw-r--r--debian/README.source2
-rw-r--r--debian/changelog199
-rw-r--r--debian/compat1
-rw-r--r--debian/control13
-rw-r--r--debian/copyright102
-rw-r--r--debian/dirs1
-rw-r--r--debian/patches/binary_name19
-rw-r--r--debian/patches/series1
-rwxr-xr-xdebian/rules94
-rw-r--r--debian/watch3
-rw-r--r--man/unionfs-fuse.857
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/Makefile7
-rw-r--r--src/cow.c67
-rw-r--r--src/cow_utils.c81
-rw-r--r--src/debug.c23
-rw-r--r--src/debug.h48
-rw-r--r--src/findbranch.c81
-rw-r--r--src/findbranch.h1
-rw-r--r--src/general.c91
-rw-r--r--src/general.h16
-rw-r--r--src/hashtable.c24
-rw-r--r--src/opts.c71
-rw-r--r--src/opts.h21
-rw-r--r--src/readdir.c137
-rw-r--r--src/readdir.h1
-rw-r--r--src/rmdir.c14
-rw-r--r--src/string.c89
-rw-r--r--src/string.h4
-rw-r--r--src/unionfs.c337
-rw-r--r--src/unionfs.h9
-rw-r--r--src/unlink.c23
-rw-r--r--src/usyslog.c296
-rw-r--r--src/usyslog.h31
-rwxr-xr-xtest.sh102
38 files changed, 1631 insertions, 477 deletions
diff --git a/.hgignore b/.hgignore
index 54a370c..39fd4af 100644
--- a/.hgignore
+++ b/.hgignore
@@ -6,6 +6,10 @@ unionfs
# build directory
build/*
+BUILD/*
+
+# codelite project files
+*.project
# vi tmp files
*.swp
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4991462..54b13d0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,7 @@
project(unionfs-fuse)
cmake_minimum_required(VERSION 2.0)
+INCLUDE (CheckIncludeFiles)
# Set a default build type for single-configuration
# CMake generators if no build type is set.
@@ -16,10 +17,17 @@ SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -DDEBUG")
add_definitions(-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26)
-option(WITH_XATTR "Enable support for extended attributes")
-if(WITH_XATTR)
- add_definitions(-DHAVE_SETXATTR)
-endif(WITH_XATTR)
+option(WITH_XATTR "Enable support for extended attributes" OFF)
+
+# .h include files
+if (WITH_XATTR)
+ CHECK_INCLUDE_FILES("sys/xattr.h" HAVE_XATTR)
+ IF (HAVE_XATTR)
+ add_definitions(-DHAVE_XATTR)
+ ENDIF(HAVE_XATTR)
+ENDIF (WITH_XATTR)
+
+
add_subdirectory(src)
add_subdirectory(man)
diff --git a/NEWS b/NEWS
index 62d7aee..c148a9f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,19 @@
+?.?
+- use BUILD_PATH instead of snprintf, which will
+ return -ENAMETOOLONG if the path is too long instead of simply
+ using a wrong path
+- new options "-o relaxed_permissions" and "-o hide_meta_dir"
+- included the debian directory
+- Properly check on rmdir() if sub-branches are also empty
+- Always compile with debug code, but only enable debugging on request
+- New option -o debug_file
+- Lots of bugs fixed in BUILD_PATH()
+- Improved search of white-out files
+- Properly fix Debian Bug#509516.
+- More debug output.
+- Add syslog support without the risk of possible dead locks
+- Use fuse big-writes (and reads) if available
+
0.25
- Alternate way to specify branches
- Minor fixes
@@ -6,6 +22,7 @@
- Support for liveCDs / live USB sticks (-o chroot)
- Build-in support to change the maximum number of open files
- Added recursive directory COW.
+- relative paths: Critical bug fix, had been completely broken in 0.23
0.23
- remove to_user() and to_root() calls for autorization and use fuse build-in
diff --git a/debian/README.source b/debian/README.source
new file mode 100644
index 0000000..e77187c
--- /dev/null
+++ b/debian/README.source
@@ -0,0 +1,2 @@
+This package make use of quilt to sligtly modify it for Debian.
+See more detail read /usr/share/doc/quilt/README.source
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..152e3cd
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,199 @@
+unionfs-fuse (0.25~hg.20110220-1) unstable; urgency=low
+
+ * new hg version
+ * Update debian/copyright to directly include the original BSD 3-Clause
+ License
+
+ -- Bernd Schubert <bernd.schubert@fastmail.fm> Mon, 21 Feb 2011 00:03:28 +0100
+
+unionfs-fuse (0.25~hg.20100602-2) unstable; urgency=low
+
+ * Set upload to unstable
+
+ -- Bernd Schubert <bernd.schubert@fastmail.fm> Sun, 06 Jun 2010 16:49:24 +0200
+
+unionfs-fuse (0.25~hg.20100602-1) karmic; urgency=low
+
+ * Pull from my 0.25 branch http://podgorny.cz/~bernd/hg/hgwebdir.cgi/0.25
+ * Check the NEWS file for details
+ * Improve the fix for Debian bug 509516
+
+ -- Bernd Schubert <bernd.schubert@fastmail.fm> Wed, 02 Jun 2010 00:25:51 +0200
+
+
+unionfs-fuse (0.25~hg.20100315-1) unstable; urgency=low
+
+ * new hg version
+
+ -- Bernd Schubert <bernd.schubert@fastmail.fm> Sun, 16 May 2010 21:35:29 +0200
+
+unionfs-fuse (0.24-1) unstable; urgency=low
+
+ * New upstream release
+ * Add utimens.patch
+ * Also close 509516, although the solution is not perfect yet
+ (closes: #509516)
+ * Switch to dpkg-source 3.0 (quilt) format
+
+ -- Bernd Schubert <bernd.schubert@fastmail.fm> Wed, 26 May 2010 16:42:13 +0200
+
+unionfs-fuse (0.23.hg.20100315-1) unstable; urgency=low
+
+ * Another update from my branch to merge with Radek and to
+ update the test script
+
+ -- Bernd Schubert <bernd.schubert@fastmail.fm> Mon, 15 Mar 2010 00:02:08 +0100
+
+unionfs-fuse (0.23.hg.20090831-1) unstable; urgency=low
+
+ * another pull from my branch
+ * fix relative pathes (closes: 541614)
+ * inlcude patch from Goswin (closes: 533403)
+
+ -- Bernd Schubert <bernd.schubert@fastmail.fm> Mon, 31 Aug 2009 00:29:46 +0200
+
+unionfs-fuse (0.23.hg.20090611-1) unstable; urgency=low
+
+ * another pull from my branch
+ * change my maintainer mail address
+
+ -- Bernd Schubert <bernd.schubert@fastmail.fm> Thu, 11 Jun 2009 20:42:41 +0200
+
+unionfs-fuse (0.23.hg.20090601-2) unstable; urgency=low
+
+ * set DH_COMPAT to 7
+ * fix several lintian packaging errors
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Tue, 02 Jun 2009 00:18:25 +0200
+
+unionfs-fuse (0.23.hg.20090601-1) unstable; urgency=low
+
+ * new upstream release
+ * pull from my branch to include everything that is supposed to be in 0.24
+ (http://podgorny.cz/~bernd/hg/hgwebdir.cgi/radek-trunk-bernd-merge)
+ * (closes: #496794)
+ * (closes: #530214)
+ * (closes: #511157)
+ * new option "-o statfs_omit_ro" (closes: #511446)
+ * man page updated if "-o cow" (is not specified closes: #511047)
+ * partly fixes bug #509516, I leave that bug open, since only fixed for one
+ rw branch
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Mon, 01 Jun 2009 23:12:42 +0200
+
+unionfs-fuse (0.21-3) unstable; urgency=high
+
+ * fixes critical buffer overflow on using relativ pathes
+ * slightly improve the man page to tell people without -ocow
+ not everything might work as expected
+ * change binary path from /usr/sbin to /usr/bin
+ * (closes: #511995)
+ * (closes: #511158)
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Sat, 24 Jan 2009 00:10:44 +0100
+
+unionfs-fuse (0.21-2) unstable; urgency=high
+
+ * fix a critical bug: creating new files in directories existing only
+ in read-only branches failed, since the directory path wasn't copied
+ to the rw-branch
+ * (closes: #495380)
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Mon, 11 Aug 2008 09:22:42 +0000
+
+unionfs-fuse (0.21-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Sat, 26 Jul 2008 18:28:39 +0200
+
+unionfs-fuse (0.20-5) unstable; urgency=low
+
+ * Another pull from my branch, fixes sereveral bugs, among it a critical
+ deadlock
+ * Installing new NEWS file and examples/ directory with dh_docs
+ * This is actually the 0.21 release (also to be released this weekend).
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Sat, 26 Jul 2008 14:03:50 +0200
+
+unionfs-fuse (0.20-4) unstable; urgency=low
+
+ * as by suggestion of Kapil:
+ - remove debian/copyright.in
+ - install the CREDITS file with dh_installdocs
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Thu, 26 Jun 2008 21:02:11 +0200
+
+unionfs-fuse (0.20-3) unstable; urgency=low
+
+ * another hg pull from http://hg.podgorny.cz/unionfs-fuse
+ (fixes the elfhash CPL incompatibility problem)
+ * Changes to the debian/copyright files as suggested by Kapil Hari Paranjape
+ and Richard Laager
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Thu, 26 Jun 2008 13:22:14 +0200
+
+unionfs-fuse (0.20-2) unstable; urgency=low
+
+ * add debian/watch file
+ * fix the copyright file generation
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Mon, 23 Jun 2008 22:50:03 +0200
+
+unionfs-fuse (0.20-1) unstable; urgency=low
+
+ * Ouch, already so many internal releases and I never noticed the version
+ string was wrong.
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Fri, 20 Jun 2008 21:13:18 +0000
+
+unionfs-fuse (0.9.20-2) unstable; urgency=low
+
+ * correct the section to misc
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Fri, 20 Jun 2008 12:25:54 +0000
+
+unionfs-fuse (0.9.20-1) unstable; urgency=low
+
+ * new upstream release
+ * additional hg pull from
+ http://podgorny.cz/~bernd/hg/hgwebdir.cgi/radek-trunk-bernd-merge
+ * preparing official debian upload
+ * switch to cmake based build system
+ * (closes: #481490)
+
+ -- Bernd Schubert <bernd-schubert@gmx.de> Thu, 19 Jun 2008 17:37:15 +0200
+
+unionfs-fuse (0.9.19-hg20080403-ql2) unstable; urgency=low
+
+ * convert to debian quilt series
+ * add fd.patch
+
+ -- Bernd Schubert <bs@q-leap.de> Tue, 29 Apr 2008 13:56:06 +0200
+
+unionfs-fuse (0.9.19-hg20080403-ql1) unstable; urgency=low
+
+ * rename() bugfixes
+ * slight rmdir() and readdir() bugfixes
+ * Update the version to 0.9.19-hg
+
+ -- Bernd Schubert <bs@q-leap.de> Thu, 3 Apr 2008 17:32:29 +0200
+
+unionfs-fuse (0.9.18hg.ql2) unstable; urgency=low
+
+ * fix dependency of unionfs-fuse and build-dependency
+
+ -- Bernd Schubert <bs@q-leap.de> Thu, 13 Mar 2008 13:49:45 +0100
+
+unionfs-fuse (0.9.18hg-1) unstable; urgency=low
+
+ * update to hg version + fixes
+
+ -- Bernd Schubert <bs@q-leap.de> Wed, 12 Mar 2008 16:54:57 +0100
+
+unionfs-fuse (0.9.18-1) unstable; urgency=low
+
+ * Initial Release.
+
+ -- Bernd Schubert <bs@q-leap.de> Wed, 12 Sep 2007 19:22:07 +0200
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..7b4aa04
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,13 @@
+Source: unionfs-fuse
+Section: misc
+Priority: optional
+Maintainer: Bernd Schubert <bernd.schubert@fastmail.fm>
+Build-Depends: debhelper (>= 7.0.0), libfuse-dev, quilt (>= 0.40), cmake
+Standards-Version: 3.8.4
+
+Package: unionfs-fuse
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, fuse-utils
+Description: Fuse implementation of unionfs
+ This is another unionfs implementation using filesystem in
+ userspace (fuse).
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..85254bd
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,102 @@
+This package was debianized by Bernd Schubert <bs@q-leap.de> on
+Wed, 12 Sep 2007 19:22:07 +0200.
+
+It was downloaded from http://podgorny.cz/moin/UnionFsFuse
+
+The brief summary of the copyright information for the files in this
+package is given below. The summary is followed by the detailed
+license texts.
+
+Files: *
+Copyright: Copyright 2008, Radek Podgorny, Bernd Schubert
+License: other
+ The complete text of this license can be found at the end of this file
+ in the paragraph "Modified BSD 3-Clause"
+
+Files: debian/*
+Copyright: Copyright 2008, Bernd Schubert <bs@q-leap.de>
+License: other
+ The complete text of this license can be found at the end of this file
+ in the paragraph "Modified BSD 3-Clause"
+
+Files: hashtable*.[ch]
+Copyright: Copyright 2002, 2004, Christopher Clark
+License: other
+ The complete text of this license can be found at the end of this file
+ in the paragraph "Modified BSD 3-Clause"
+
+Files: cow_utils.[ch]
+Copyright: Copyright 1991, 1993, 1994, The Regents of the University of California.
+License: BSD-3
+ The complete text of this license can be found at the end of this file
+ in the paragraph "Original BSD 3-Clause License:"
+
+
+
+The detailed License Texts
+==========================
+
+Modified BSD 3-Clause:
+----------------------
+(taken from LICENSE file in source package)
+
+Copyright <author_names specific to each file as detailed above>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the original author; nor the names of any contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Original BSD 3-Clause License:
+------------------------------
+
+Copyright (c) The Regents of the University of California.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/debian/dirs b/debian/dirs
new file mode 100644
index 0000000..236670a
--- /dev/null
+++ b/debian/dirs
@@ -0,0 +1 @@
+usr/sbin
diff --git a/debian/patches/binary_name b/debian/patches/binary_name
new file mode 100644
index 0000000..d030ef3
--- /dev/null
+++ b/debian/patches/binary_name
@@ -0,0 +1,19 @@
+---
+ src/CMakeLists.txt | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+Index: unionfs.0.25/src/CMakeLists.txt
+===================================================================
+--- unionfs.0.25.orig/src/CMakeLists.txt
++++ unionfs.0.25/src/CMakeLists.txt
+@@ -2,7 +2,7 @@ set(HASHTABLE_SRCS hashtable.c hashtable
+ set(UNIONFS_SRCS unionfs.c stats.c opts.c debug.c findbranch.c readdir.c
+ general.c unlink.c cow.c cow_utils.c string.c rmdir.c usyslog.c)
+
+-add_executable(unionfs ${UNIONFS_SRCS} ${HASHTABLE_SRCS})
+-target_link_libraries(unionfs fuse pthread rt)
++add_executable(unionfs-fuse ${UNIONFS_SRCS} ${HASHTABLE_SRCS})
++target_link_libraries(unionfs-fuse fuse pthread rt)
+
+-INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/unionfs DESTINATION bin)
++INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/unionfs-fuse DESTINATION bin)
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..2fd96b3
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1 @@
+binary_name
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..5c3425c
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,94 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+export DH_VERBOSE=1
+
+QUILT_STAMPFN := patch-stamp
+include /usr/share/quilt/quilt.make
+
+
+CFLAGS = -pipe -W -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+ CFLAGS += -O0
+else
+ CFLAGS += -O2
+endif
+
+configure: configure-stamp
+configure-stamp:
+ dh_testdir
+ # Add here commands to configure the package.
+ mkdir -p debian/build
+ cd debian/build \
+ && cmake ../../ -DCMAKE_INSTALL_PREFIX=/usr \
+ -DCMAKE_C_FLAGS="$(CFLAGS)" \
+ -DCMAKE_SKIP_RPATH=TRUE
+
+ touch configure-stamp
+
+
+build: build-stamp
+
+build-stamp: $(QUILT_STAMPFN) configure-stamp
+ dh_testdir
+
+ # Add here commands to compile the package.
+ cd debian/build && make
+
+ touch build-stamp
+
+clean: unpatch
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp configure-stamp $(QUILT_STAMPFN)
+
+ # Add here commands to clean up after the build process.
+ rm -fr debian/build
+ rm -f debian/files
+
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_prep
+ dh_installdirs
+
+ # Add here commands to install the package into debian/unionfsfuse.
+ cd debian/build && make DESTDIR=../../debian/unionfs-fuse install
+
+# Build architecture-independent files here.
+binary-indep:
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs
+ dh_installdocs CREDITS NEWS examples/
+ dh_installexamples
+
+
+# dh_install
+ dh_installman
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms
+ dh_installdeb
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
+
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..0583681
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,3 @@
+version=3
+
+http://podgorny.cz/unionfs-fuse/releases/unionfs-fuse-(.*)\.tar\.bz2
diff --git a/man/unionfs-fuse.8 b/man/unionfs-fuse.8
index d4d679c..91ae8fc 100644
--- a/man/unionfs-fuse.8
+++ b/man/unionfs-fuse.8
@@ -26,22 +26,6 @@ the file is copied to to a higher level read\-write branch if the
.SH "OPTIONS"
Below is a summary of unionfs\-fuse options
.TP
-\fB\-o cow
-Enable copy\-on\-write
-.TP
-\fB\-o stats
-Show statistics in the file 'stats' under the mountpoint.
-.TP
-\fB\-o statfs_omit_ro
-By default blocks of all branches are counted in statfs() calls
-(e.g. by 'df'). On setting this option read-only branches will be omitted
-for the summary of blocks. This may sound weird but it actually fixes
-"wrong" percentage of free space.
-.TP
-\fB\-o noinitgroups
-Since version 0.23 without any effect, just left over for compatibility.
-Might be removed in future versions.
-.TP
\fB\-o chroot=path
Path to chroot into. By using this option unionfs-fuse
may be used for live CDs or live USB sticks, etc. So it can serve
@@ -51,12 +35,53 @@ If you do set this option, you also need to specify the branches relativly
to the given chroot directory. See examples/S01a-unionfs-fuse-live-cd.sh
for an example.
.TP
+\fB\-o cow
+Enable copy\-on\-write
+.TP
+\fB\-o hide_meta_files
+In our unionfs root path we have a .unionfs directory that includes
+metadata, such as hidden (deleted) files. This options make this
+directory invisible from readdir(), so for example "ls -la /union_root/"
+will not show it. However, this directory is still there and "cd .unionfs"
+or "ls -l .unionfs" still work. Also, libfuse will create .fuse_hidden*
+files, if a file is open, but will be deleted. Those fuse meta files also
+will be invisble. This option is especially usufull for
+package builders.
+.TP
+\fB\-d
+Enable debugging for unionfs and libfuse. Useful for developers if the code
+if the code does not behave as expected. Debug information will be written
+to stderr and a debug file (./unionfs_debug.log by default).
+.TP
+\fB\-o debug_file=file
+Write unionfs debug information into that file.
+.TP
\fB\-o max_files=number
Maximum number of open files. Most system have a default of 1024 open
files per process. For example if unionfs-fuse servs "/" applications like
KDE or GNOME might have much more open files, which will make the unionfs-fuse
process to exceed this limit. Suggested for "/" is >16000 or even >32000 files.
If this limit exceeds unionfs-fuse will not be able to open further files.
+.TP
+\fB\-o noinitgroups
+Since version 0.23 without any effect, just left over for compatibility.
+Might be removed in future versions.
+.TP
+\fB\-o relaxed_permissions
+Usually we automatically add the libfuse option "-odefault_permissions"
+so that libfuse takes over permission checks. However, if running not
+as root (so as uid =! 0 and gid != 0), permissions of the underlying
+filesystem are already sufficient. In order to prevent from severe
+security issues, this option is not allowed if running as root.
+.TP
+\fB\-o statfs_omit_ro
+By default blocks of all branches are counted in statfs() calls
+(e.g. by 'df'). On setting this option read-only branches will be omitted
+for the summary of blocks. This may sound weird but it actually fixes
+"wrong" percentage of free space.
+.TP
+\fB\-o stats
+Show statistics in the file 'stats' under the mountpoint.
.SH "Options to libfuse"
There are several further options available, which don't directly apply to
unionfs, but to libfuse. Please run "unionfs-fuse --help" to see these.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 708ac18..d58d4f4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,7 +1,8 @@
set(HASHTABLE_SRCS hashtable.c hashtable_itr.c)
-set(UNIONFS_SRCS unionfs.c stats.c opts.c debug.c findbranch.c readdir.c general.c unlink.c cow.c cow_utils.c string.c rmdir.c)
+set(UNIONFS_SRCS unionfs.c stats.c opts.c debug.c findbranch.c readdir.c
+ general.c unlink.c cow.c cow_utils.c string.c rmdir.c usyslog.c)
add_executable(unionfs ${UNIONFS_SRCS} ${HASHTABLE_SRCS})
-target_link_libraries(unionfs fuse pthread m rt)
+target_link_libraries(unionfs fuse pthread rt)
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/unionfs DESTINATION bin)
diff --git a/src/Makefile b/src/Makefile
index 71c9318..82f7a1c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,15 +1,16 @@
CFLAGS += -Wall
CPPFLAGS += $(shell pkg-config --cflags fuse)
CPPFLAGS += -DFUSE_USE_VERSION=26
-CPPFLAGS += -DHAVE_SETXATTR
+CPPFLAGS += -DHAVE_XATTR
LDFLAGS +=
LIB = $(shell pkg-config --libs fuse)
-LIB += -lm # For ceil(3)
HASHTABLE_OBJ = hashtable.o hashtable_itr.o
-UNIONFS_OBJ = unionfs.o stats.o opts.o debug.o findbranch.o readdir.o general.o unlink.o rmdir.o cow.o cow_utils.o string.o
+UNIONFS_OBJ = unionfs.o stats.o opts.o debug.o findbranch.o readdir.o \
+ general.o unlink.o rmdir.o cow.o cow_utils.o string.o \
+ usyslog.o
unionfs: $(UNIONFS_OBJ) $(HASHTABLE_OBJ) version.h
diff --git a/src/cow.c b/src/cow.c
index da55304..21d0869 100644
--- a/src/cow.c
+++ b/src/cow.c
@@ -24,20 +24,21 @@
#include "cow_utils.h"
#include "string.h"
#include "debug.h"
+#include "usyslog.h"
/**
* Actually create the directory here.
*/
static int do_create(const char *path, int nbranch_ro, int nbranch_rw) {
- DBG_IN();
+ DBG("%s\n", path);
char dirp[PATHLEN_MAX]; // dir path to create
sprintf(dirp, "%s%s", uopt.branches[nbranch_rw].path, path);
struct stat buf;
int res = stat(dirp, &buf);
- if (res != -1) return 0; // already exists
+ if (res != -1) RETURN(0); // already exists
if (nbranch_ro == nbranch_rw) {
// special case nbranch_ro = nbranch_rw, this is if we a create
@@ -48,22 +49,22 @@ static int do_create(const char *path, int nbranch_ro, int nbranch_rw) {
char o_dirp[PATHLEN_MAX]; // the pathname we want to copy
sprintf(o_dirp, "%s%s", uopt.branches[nbranch_ro].path, path);
res = stat(o_dirp, &buf);
- if (res == -1) return 1; // lower level branch removed in the mean time?
+ if (res == -1) RETURN(1); // lower level branch removed in the mean time?
}
res = mkdir(dirp, buf.st_mode);
if (res == -1) {
- usyslog(LOG_DAEMON, "Creating %s failed: \n", dirp);
- return 1;
+ USYSLOG(LOG_DAEMON, "Creating %s failed: \n", dirp);
+ RETURN(1);
}
- if (nbranch_ro == nbranch_rw) return 0; // the special case again
+ if (nbranch_ro == nbranch_rw) RETURN(0); // the special case again
- if (setfile(dirp, &buf)) return 1; // directory already removed by another process?
+ if (setfile(dirp, &buf)) RETURN(1); // directory already removed by another process?
// TODO: time, but its values are modified by the next dir/file creation steps?
- return 0;
+ RETURN(0);
}
/**
@@ -71,23 +72,17 @@ static int do_create(const char *path, int nbranch_ro, int nbranch_rw) {
* nbranch for an other COW operation.
*/
int path_create(const char *path, int nbranch_ro, int nbranch_rw) {
- DBG_IN();
-
- if (!uopt.cow_enabled) return 0;
-
- if (strlen(path) + strlen(uopt.branches[nbranch_rw].path) > PATHLEN_MAX
- || strlen(path) + strlen(uopt.branches[nbranch_ro].path) > PATHLEN_MAX) {
- // TODO: how to handle that?
- return 1;
- }
+ DBG("%s\n", path);
+ if (!uopt.cow_enabled) RETURN(0);
+
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[nbranch_rw].path, path);
+ if (BUILD_PATH(p, uopt.branches[nbranch_rw].path, path)) RETURN(-ENAMETOOLONG);
struct stat st;
if (!stat(p, &st)) {
// path does already exists, no need to create it
- return 0;
+ RETURN(0);
}
char *walk = (char *)path;
@@ -102,13 +97,13 @@ int path_create(const char *path, int nbranch_ro, int nbranch_rw) {
// +1 due to \0, which gets added automatically
snprintf(p, (walk - path) + 1, "%s", path); // walk - path = strlen(/dir1)
int res = do_create(p, nbranch_ro, nbranch_rw);
- if (res) return res; // creating the directory failed
+ if (res) RETURN(res); // creating the directory failed
// as above the do loop, walk over the next slashes, walk = dir2/
while (*walk != '\0' && *walk == '/') walk++;
} while (*walk != '\0');
- return 0;
+ RETURN(0);
}
/**
@@ -116,29 +111,31 @@ int path_create(const char *path, int nbranch_ro, int nbranch_rw) {
* i.e. it might be a filename.
*/
int path_create_cutlast(const char *path, int nbranch_ro, int nbranch_rw) {
- DBG_IN();
+ DBG("%s\n", path);
char *dname = u_dirname(path);
if (dname == NULL)
- return -ENOMEM;
+ RETURN(-ENOMEM);
int ret = path_create(dname, nbranch_ro, nbranch_rw);
free(dname);
- return ret;
+ RETURN(ret);
}
/**
* initiate the cow-copy action
*/
int cow_cp(const char *path, int branch_ro, int branch_rw) {
- DBG_IN();
+ DBG("%s\n", path);
// create the path to the file
path_create_cutlast(path, branch_ro, branch_rw);
char from[PATHLEN_MAX], to[PATHLEN_MAX];
- snprintf(from, PATHLEN_MAX, "%s%s", uopt.branches[branch_ro].path, path);
- snprintf(to, PATHLEN_MAX, "%s%s", uopt.branches[branch_rw].path, path);
+ if (BUILD_PATH(from, uopt.branches[branch_ro].path, path))
+ RETURN(-ENAMETOOLONG);
+ if (BUILD_PATH(to, uopt.branches[branch_rw].path, path))
+ RETURN(-ENAMETOOLONG);
setlocale(LC_ALL, "");
@@ -173,33 +170,33 @@ int cow_cp(const char *path, int branch_ro, int branch_rw) {
res = copy_fifo(&cow);
break;
case S_IFSOCK:
- usyslog(LOG_WARNING, "COW of sockets not supported: %s\n", cow.from_path);
- return 1;
+ USYSLOG(LOG_WARNING, "COW of sockets not supported: %s\n", cow.from_path);
+ RETURN(1);
default:
res = copy_file(&cow);
}
- return res;
+ RETURN(res);
}
/**
* copy a directory between branches (includes all contents of the directory)
*/
int copy_directory(const char *path, int branch_ro, int branch_rw) {
- DBG_IN();
+ DBG("%s\n", path);
/* create the directory on the destination branch */
int res = path_create(path, branch_ro, branch_rw);
if (res != 0) {
- return res;
+ RETURN(res);
}
/* determine path to source directory on read-only branch */
char from[PATHLEN_MAX];
- if (BUILD_PATH(from, uopt.branches[branch_ro].path, path)) return 1;
+ if (BUILD_PATH(from, uopt.branches[branch_ro].path, path)) RETURN(1);
DIR *dp = opendir(from);
- if (dp == NULL) return 1;
+ if (dp == NULL) RETURN(1);
struct dirent *de;
while ((de = readdir(dp)) != NULL) {
@@ -215,6 +212,6 @@ int copy_directory(const char *path, int branch_ro, int branch_rw) {
}
closedir(dp);
- return res;
+ RETURN(res);
}
diff --git a/src/cow_utils.c b/src/cow_utils.c
index c2ecdb1..52e824a 100644
--- a/src/cow_utils.c
+++ b/src/cow_utils.c
@@ -46,6 +46,7 @@
#include "cow_utils.h"
#include "debug.h"
#include "general.h"
+#include "usyslog.h"
// BSD seems to know S_ISTXT itself
#ifndef S_ISTXT
@@ -57,7 +58,7 @@
**/
int setfile(const char *path, struct stat *fs)
{
- DBG_IN();
+ DBG("%s\n", path);
struct utimbuf ut;
int rval;
@@ -68,7 +69,7 @@ int setfile(const char *path, struct stat *fs)
ut.actime = fs->st_atime;
ut.modtime = fs->st_mtime;
if (utime(path, &ut)) {
- usyslog(LOG_WARNING, "utimes: %s", path);
+ USYSLOG(LOG_WARNING, "utimes: %s", path);
rval = 1;
}
/*
@@ -79,14 +80,14 @@ int setfile(const char *path, struct stat *fs)
*/
if (chown(path, fs->st_uid, fs->st_gid)) {
if (errno != EPERM) {
- usyslog(LOG_WARNING, "chown: %s", path);
+ USYSLOG(LOG_WARNING, "chown: %s", path);
rval = 1;
}
fs->st_mode &= ~(S_ISTXT | S_ISUID | S_ISGID);
}
if (chmod(path, fs->st_mode)) {
- usyslog(LOG_WARNING, "chown: %s", path);
+ USYSLOG(LOG_WARNING, "chown: %s", path);
rval = 1;
}
@@ -101,13 +102,13 @@ int setfile(const char *path, struct stat *fs)
errno = 0;
if (chflags(path, fs->st_flags)) {
if (errno != EOPNOTSUPP || fs->st_flags != 0) {
- usyslog(LOG_WARNING, "chflags: %s", path);
+ USYSLOG(LOG_WARNING, "chflags: %s", path);
rval = 1;
}
- return (rval);
+ RETURN(rval);
}
#endif
- return 0;
+ RETURN(0);
}
/**
@@ -115,15 +116,15 @@ int setfile(const char *path, struct stat *fs)
**/
static int setlink(const char *path, struct stat *fs)
{
- DBG_IN();
+ DBG("%s\n", path);
if (lchown(path, fs->st_uid, fs->st_gid)) {
if (errno != EPERM) {
- usyslog(LOG_WARNING, "lchown: %s", path);
- return (1);
+ USYSLOG(LOG_WARNING, "lchown: %s", path);
+ RETURN(1);
}
}
- return (0);
+ RETURN(0);
}
@@ -132,7 +133,7 @@ static int setlink(const char *path, struct stat *fs)
**/
int copy_file(struct cow *cow)
{
- DBG_IN();
+ DBG("from %s to %s\n", cow->from_path, cow->to_path);
static char buf[MAXBSIZE];
struct stat to_stat, *fs;
@@ -143,8 +144,8 @@ int copy_file(struct cow *cow)
#endif
if ((from_fd = open(cow->from_path, O_RDONLY, 0)) == -1) {
- usyslog(LOG_WARNING, "%s", cow->from_path);
- return (1);
+ USYSLOG(LOG_WARNING, "%s", cow->from_path);
+ RETURN(1);
}
fs = cow->stat;
@@ -153,9 +154,9 @@ int copy_file(struct cow *cow)
fs->st_mode & ~(S_ISTXT | S_ISUID | S_ISGID));
if (to_fd == -1) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
(void)close(from_fd);
- return (1);
+ RETURN(1);
}
/*
@@ -167,17 +168,17 @@ int copy_file(struct cow *cow)
if (fs->st_size > 0 && fs->st_size <= 8 * 1048576) {
if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
- usyslog(LOG_WARNING, "mmap: %s", cow->from_path);
+ USYSLOG(LOG_WARNING, "mmap: %s", cow->from_path);
rval = 1;
} else {
madvise(p, fs->st_size, MADV_SEQUENTIAL);
if (write(to_fd, p, fs->st_size) != fs->st_size) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
rval = 1;
}
/* Some systems don't unmap on close(2). */
if (munmap(p, fs->st_size) < 0) {
- usyslog(LOG_WARNING, "%s", cow->from_path);
+ USYSLOG(LOG_WARNING, "%s", cow->from_path);
rval = 1;
}
}
@@ -187,13 +188,13 @@ int copy_file(struct cow *cow)
while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
wcount = write(to_fd, buf, rcount);
if (rcount != wcount || wcount == -1) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
rval = 1;
break;
}
}
if (rcount < 0) {
- usyslog(LOG_WARNING, "copy failed: %s", cow->from_path);
+ USYSLOG(LOG_WARNING, "copy failed: %s", cow->from_path);
rval = 1;
}
}
@@ -201,7 +202,7 @@ int copy_file(struct cow *cow)
if (rval == 1) {
(void)close(from_fd);
(void)close(to_fd);
- return (1);
+ RETURN(1);
}
if (setfile(cow->to_path, cow->stat))
@@ -214,21 +215,21 @@ int copy_file(struct cow *cow)
(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == cow->uid) {
if (fstat(to_fd, &to_stat)) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
rval = 1;
} else if (fs->st_gid == to_stat.st_gid &&
fchmod(to_fd, fs->st_mode & RETAINBITS & ~cow->umask)) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
rval = 1;
}
}
(void)close(from_fd);
if (close(to_fd)) {
- usyslog(LOG_WARNING, "%s", cow->to_path);
+ USYSLOG(LOG_WARNING, "%s", cow->to_path);
rval = 1;
}
- return (rval);
+ RETURN(rval);
}
/**
@@ -236,24 +237,24 @@ int copy_file(struct cow *cow)
*/
int copy_link(struct cow *cow)
{
- DBG_IN();
+ DBG("from %s to %s\n", cow->from_path, cow->to_path);
int len;
char link[PATHLEN_MAX];
if ((len = readlink(cow->from_path, link, sizeof(link)-1)) == -1) {
- usyslog(LOG_WARNING, "readlink: %s", cow->from_path);
- return (1);
+ USYSLOG(LOG_WARNING, "readlink: %s", cow->from_path);
+ RETURN(1);
}
link[len] = '\0';
if (symlink(link, cow->to_path)) {
- usyslog(LOG_WARNING, "symlink: %s", link);
- return (1);
+ USYSLOG(LOG_WARNING, "symlink: %s", link);
+ RETURN(1);
}
- return setlink(cow->to_path, cow->stat);
+ RETURN(setlink(cow->to_path, cow->stat));
}
/**
@@ -262,13 +263,13 @@ int copy_link(struct cow *cow)
**/
int copy_fifo(struct cow *cow)
{
- DBG_IN();
+ DBG("from %s to %s\n", cow->from_path, cow->to_path);
if (mkfifo(cow->to_path, cow->stat->st_mode)) {
- usyslog(LOG_WARNING, "mkfifo: %s", cow->to_path);
- return (1);
+ USYSLOG(LOG_WARNING, "mkfifo: %s", cow->to_path);
+ RETURN(1);
}
- return setfile(cow->to_path, cow->stat);
+ RETURN(setfile(cow->to_path, cow->stat));
}
/**
@@ -277,11 +278,11 @@ int copy_fifo(struct cow *cow)
*/
int copy_special(struct cow *cow)
{
- DBG_IN();
+ DBG("from %s to %s\n", cow->from_path, cow->to_path);
if (mknod(cow->to_path, cow->stat->st_mode, cow->stat->st_rdev)) {
- usyslog(LOG_WARNING, "mknod: %s", cow->to_path);
- return (1);
+ USYSLOG(LOG_WARNING, "mknod: %s", cow->to_path);
+ RETURN(1);
}
- return setfile(cow->to_path, cow->stat);
+ RETURN(setfile(cow->to_path, cow->stat));
}
diff --git a/src/debug.c b/src/debug.c
index 34918cd..632ff1f 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -1,24 +1,31 @@
/*
* License: BSD-style license
-* Copyright: Radek Podgorny <radek@podgorny.cz>,
+* Copyright: Radek Podgorny <radek@podgorny.cz>,
+* Bernd Schubert <bernd.schubert@fastmail.fm>
*/
#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "opts.h"
#include "debug.h"
+static char default_debug_path[] = "./unionfs_debug.log";
FILE* dbgfile = NULL;
-int debug_init() {
-#ifdef DEBUG
- char *dbgpath = "./unionfs_debug.log";
+int debug_init(void) {
+ char *dbgpath = uopt.dbgpath;
+
+ if (!dbgpath) dbgpath = default_debug_path;
+
printf("Debug mode, log will be written to %s\n", dbgpath);
dbgfile = fopen(dbgpath, "w");
if (!dbgfile) {
- printf("Failed to open %s for writing, exitting\n", dbgpath);
- return 2;
+ printf("Failed to open %s for writing: %s.\nAborting!\n",
+ dbgpath, strerror(errno));
+ RETURN(2);
}
-#endif
- return 0;
+ RETURN(0);
}
diff --git a/src/debug.h b/src/debug.h
index 4455d5b..64626eb 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -1,28 +1,42 @@
/*
-* License: BSD-style license
-* Copyright: Radek Podgorny <radek@podgorny.cz>,
-*/
+ * License: BSD-style license
+ * Copyright: Radek Podgorny <radek@podgorny.cz>,
+ * Bernd Schubert <bernd.schubert@fastmail.fm
+ */
#ifndef DEBUG_H
#define DEBUG_H
+#include "opts.h"
-#ifdef DEBUG
- extern FILE* dbgfile;
- //#define DBG(x) fprintf(dbgfile, "%s\n", x);
- #define DBG_IN() printf("In %s()\n", __func__);
+extern FILE* dbgfile;
- #define DBG(...) \
- do { \
- printf("%s(): %d: ", __func__, __LINE__); \
- printf(__VA_ARGS__); \
- } while (0);
-#else
- #define DBG_IN();
- #define DBG(...);
-#endif
+#define DBG_IN() DBG("\n");
+#define DBG(format, ...) \
+ do { \
+ if (!uopt.debug) break; \
+ fprintf(stderr, "%s(): %d: ", __func__, __LINE__); \
+ fprintf(dbgfile, "%s(): %d: ", __func__, __LINE__); \
+ fprintf(stderr, format, ##__VA_ARGS__); \
+ fprintf(dbgfile, format, ##__VA_ARGS__); \
+ fflush(stderr); \
+ fflush(stdout); \
+ } while (0)
+
+#define RETURN(returncode) \
+ do { \
+ if (uopt.debug) DBG("return %d\n", returncode); \
+ return returncode; \
+ } while (0)
+
+
+/* In order to prevent useless function calls and to make the compiler
+ * to optimize those out, debug.c will only have definitions if DEBUG
+ * is defined. So if DEBUG is NOT defined, we define empty functions here */
int debug_init();
+void dbg_in(const char *function);
+
+#endif // DEBUG_H
-#endif
diff --git a/src/findbranch.c b/src/findbranch.c
index 16943c4..8c8d703 100644
--- a/src/findbranch.c
+++ b/src/findbranch.c
@@ -49,17 +49,18 @@
#include "findbranch.h"
#include "string.h"
#include "debug.h"
+#include "usyslog.h"
/**
* Find a branch that has "path". Return the branch number.
*/
static int find_branch(const char *path, searchflag_t flag) {
- DBG_IN();
+ DBG("%s\n", path);
int i = 0;
for (i = 0; i < uopt.nbranches; i++) {
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
struct stat stbuf;
int res = lstat(p, &stbuf);
@@ -70,46 +71,51 @@ static int find_branch(const char *path, searchflag_t flag) {
switch (flag) {
case RWRO:
// any path we found is fine
- return i;
+ RETURN(i);
case RWONLY:
// we need a rw-branch
- if (uopt.branches[i].rw) return i;
+ if (uopt.branches[i].rw) RETURN(i);
break;
default:
- usyslog(LOG_ERR, "%s: Unknown flag %d\n", __func__, flag);
+ USYSLOG(LOG_ERR, "%s: Unknown flag %d\n", __func__, flag);
}
}
// check check for a hide file, checking first here is the magic to hide files *below* this level
- if (path_hidden(path, i)) {
+ res = path_hidden(path, i);
+ if (res > 0) {
// So no path, but whiteout found. No need to search in further branches
errno = ENOENT;
- return -1;
+ RETURN(-1);
+ } else if (res < 0) {
+ errno = res; // error
+ RETURN(-1);
}
}
errno = ENOENT;
- return -1;
+ RETURN(-1);
}
/**
* Find a ro or rw branch.
*/
int find_rorw_branch(const char *path) {
- DBG_IN();
- return find_branch(path, RWRO);
+ DBG("%s\n", path);
+ RETURN(find_branch(path, RWRO));
}
/**
* Find a writable branch. If file does not exist, we check for
* the parent directory.
+ * @path - the path to find or to copy (with last element cut off)
+ * @ rw_hint - the rw branch to copy to, set to -1 to autodetect it
*/
-int find_rw_branch_cutlast(const char *path) {
- DBG_IN();
-
+int __find_rw_branch_cutlast(const char *path, int rw_hint) {
int branch = find_rw_branch_cow(path);
+ DBG("branch = %d\n", branch);
- if (branch >= 0 || (branch < 0 && errno != ENOENT)) return branch;
+ if (branch >= 0 || (branch < 0 && errno != ENOENT)) RETURN(branch);
DBG("Check for parent directory\n");
@@ -119,11 +125,18 @@ int find_rw_branch_cutlast(const char *path) {
char *dname = u_dirname(path);
if (dname == NULL) {
errno = ENOMEM;
- return -1;
+ RETURN(-1);
}
+
branch = find_rorw_branch(dname);
+ DBG("branch = %d\n", branch);
+
+ // No branch found, so path does nowhere exist, error
+ if (branch < 0) goto out;
- if (branch < 0 || uopt.branches[branch].rw) goto out;
+ // Reminder rw_hint == -1 -> autodetect, we do not care which branch it is
+ if (uopt.branches[branch].rw
+ && (rw_hint == -1 || branch == rw_hint)) goto out;
if (!uopt.cow_enabled) {
// So path exists, but is not writable.
@@ -132,8 +145,14 @@ int find_rw_branch_cutlast(const char *path) {
goto out;
}
+ int branch_rw;
// since it is a directory, any rw-branch is fine
- int branch_rw = find_lowest_rw_branch(uopt.nbranches);
+ if (rw_hint == -1)
+ branch_rw = find_lowest_rw_branch(uopt.nbranches);
+ else
+ branch_rw = rw_hint;
+
+ DBG("branch_rw = %d\n", branch_rw);
// no writable branch found, we must return an error
if (branch_rw < 0) {
@@ -147,7 +166,15 @@ int find_rw_branch_cutlast(const char *path) {
out:
free(dname);
- return branch;
+ RETURN(branch);
+}
+
+/**
+ * Call __find_rw_branch_cutlast()
+ */
+int find_rw_branch_cutlast(const char *path) {
+ int rw_hint = -1; // autodetect rw_branch
+ RETURN(__find_rw_branch_cutlast(path, rw_hint));
}
/**
@@ -159,35 +186,35 @@ out:
* and a directory is to be copied from ro- to rw-branch.
*/
int find_rw_branch_cow(const char *path) {
- DBG_IN();
+ DBG("%s\n", path);
int branch_rorw = find_rorw_branch(path);
// not found anywhere
- if (branch_rorw < 0) return -1;
+ if (branch_rorw < 0) RETURN(-1);
// the found branch is writable, good!
- if (uopt.branches[branch_rorw].rw) return branch_rorw;
+ if (uopt.branches[branch_rorw].rw) RETURN(branch_rorw);
// cow is disabled and branch is not writable, so deny write permission
if (!uopt.cow_enabled) {
errno = EACCES;
- return -1;
+ RETURN(-1);
}
int branch_rw = find_lowest_rw_branch(branch_rorw);
if (branch_rw < 0) {
// no writable branch found
errno = EACCES;
- return -1;
+ RETURN(-1);
}
- if (cow_cp(path, branch_rorw, branch_rw)) return -1;
+ if (cow_cp(path, branch_rorw, branch_rw)) RETURN(-1);
// remove a file that might hide the copied file
remove_hidden(path, branch_rw);
- return branch_rw;
+ RETURN(branch_rw);
}
/**
@@ -198,8 +225,8 @@ int find_lowest_rw_branch(int branch_ro) {
int i = 0;
for (i = 0; i < branch_ro; i++) {
- if (uopt.branches[i].rw) return i; // found it it.
+ if (uopt.branches[i].rw) RETURN(i); // found it it.
}
- return -1;
+ RETURN(-1);
}
diff --git a/src/findbranch.h b/src/findbranch.h
index 6378d86..56cfc08 100644
--- a/src/findbranch.h
+++ b/src/findbranch.h
@@ -15,6 +15,7 @@ typedef enum searchflag {
int find_rorw_branch(const char *path);
int find_lowest_rw_branch(int branch_ro);
int find_rw_branch_cutlast(const char *path);
+int __find_rw_branch_cutlast(const char *path, int rw_hint);
int find_rw_branch_cow(const char *path);
#endif
diff --git a/src/general.c b/src/general.c
index 941a035..fcec693 100644
--- a/src/general.c
+++ b/src/general.c
@@ -30,64 +30,60 @@
#include "findbranch.h"
#include "general.h"
#include "debug.h"
-
+#include "usyslog.h"
/**
* Check if a file or directory with the hidden flag exists.
*/
-static bool filedir_hidden(const char *path) {
- DBG_IN();
-
+static int filedir_hidden(const char *path) {
// cow mode disabled, no need for hidden files
- if (!uopt.cow_enabled) return false;
+ if (!uopt.cow_enabled) RETURN(false);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", path, HIDETAG);
+ if (strlen(path) + strlen(HIDETAG) > PATHLEN_MAX) RETURN(-ENAMETOOLONG);
+ snprintf(p, PATHLEN_MAX, "%s%s", path, HIDETAG);
+ DBG("%s\n", p);
struct stat stbuf;
int res = lstat(p, &stbuf);
- if (res == 0) return true;
+ if (res == 0) RETURN(true);
- return false;
+ RETURN(0);
}
/**
* check if any dir or file within path is hidden
*/
-bool path_hidden(const char *path, int branch) {
- DBG_IN();
+int path_hidden(const char *path, int branch) {
+ DBG("%s\n", path);
- if (!uopt.cow_enabled) return false;
+ if (!uopt.cow_enabled) RETURN(false);
char whiteoutpath[PATHLEN_MAX];
- if (BUILD_PATH(whiteoutpath, uopt.branches[branch].path, METADIR, path)) return false;
+ if (BUILD_PATH(whiteoutpath, uopt.branches[branch].path, METADIR, path)) RETURN(false);
- char *walk = whiteoutpath;
+ // -1 as we MUST not end on the next path element
+ char *walk = whiteoutpath + uopt.branches[branch].path_len + strlen(METADIR) - 1;
// first slashes, e.g. we have path = /dir1/dir2/, will set walk = dir1/dir2/
while (*walk != '\0' && *walk == '/') walk++;
- bool first = true;
do {
// walk over the directory name, walk will now be /dir2
while (*walk != '\0' && *walk != '/') walk++;
- if (first) {
- // first dir in path is our branch, no need to check if it is hidden
- first = false;
- continue;
- }
// +1 due to \0, which gets added automatically
char p[PATHLEN_MAX];
- snprintf(p, (walk - whiteoutpath) + 1, "%s", whiteoutpath); // walk - path = strlen(/dir1)
- bool res = filedir_hidden(p);
- if (res) return res; // path is hidden
+ // walk - path = strlen(/dir1)
+ snprintf(p, (walk - whiteoutpath) + 1, "%s", whiteoutpath);
+ int res = filedir_hidden(p);
+ if (res) RETURN(res); // path is hidden or error
// as above the do loop, walk over the next slashes, walk = dir2/
while (*walk != '\0' && *walk == '/') walk++;
} while (*walk != '\0');
- return 0;
+ RETURN(0);
}
/**
@@ -95,16 +91,18 @@ bool path_hidden(const char *path, int branch) {
* If maxbranch == -1, try to delete it in all branches.
*/
int remove_hidden(const char *path, int maxbranch) {
- DBG_IN();
+ DBG("%s\n", path);
- if (!uopt.cow_enabled) return 0;
+ if (!uopt.cow_enabled) RETURN(0);
if (maxbranch == -1) maxbranch = uopt.nbranches;
int i;
for (i = 0; i <= maxbranch; i++) {
char p[PATHLEN_MAX];
- if (BUILD_PATH(p, uopt.branches[i].path, METADIR, path, HIDETAG)) return 1;
+ if (BUILD_PATH(p, uopt.branches[i].path, METADIR, path)) RETURN(-ENAMETOOLONG);
+ if (strlen(p) + strlen(HIDETAG) > PATHLEN_MAX) RETURN(-ENAMETOOLONG);
+ strcat(p, HIDETAG); // TODO check length
switch (path_is_dir(p)) {
case IS_FILE: unlink(p); break;
@@ -113,7 +111,7 @@ int remove_hidden(const char *path, int maxbranch) {
}
}
- return 0;
+ RETURN(0);
}
/**
@@ -122,60 +120,61 @@ int remove_hidden(const char *path, int maxbranch) {
* return proper types given by filetype_t
*/
filetype_t path_is_dir(const char *path) {
- DBG_IN();
+ DBG("%s\n", path);
struct stat buf;
- if (lstat(path, &buf) == -1) return NOT_EXISTING;
+ if (lstat(path, &buf) == -1) RETURN(NOT_EXISTING);
- if (S_ISDIR(buf.st_mode)) return IS_DIR;
+ if (S_ISDIR(buf.st_mode)) RETURN(IS_DIR);
- return IS_FILE;
+ RETURN(IS_FILE);
}
/**
* Create a file or directory that hides path below branch_rw
*/
static int do_create_whiteout(const char *path, int branch_rw, enum whiteout mode) {
- DBG_IN();
+ DBG("%s\n", path);
char metapath[PATHLEN_MAX];
- if (BUILD_PATH(metapath, METADIR, path)) return -1;
+ if (BUILD_PATH(metapath, METADIR, path)) RETURN(-1);
// p MUST be without path to branch prefix here! 2 x branch_rw is correct here!
// this creates e.g. branch/.unionfs/some_directory
path_create_cutlast(metapath, branch_rw, branch_rw);
char p[PATHLEN_MAX];
- if (BUILD_PATH(p, uopt.branches[branch_rw].path, metapath, HIDETAG)) return -1;
+ if (BUILD_PATH(p, uopt.branches[branch_rw].path, metapath)) RETURN(-1);
+ strcat(p, HIDETAG); // TODO check length
int res;
if (mode == WHITEOUT_FILE) {
res = open(p, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- if (res == -1) return -1;
+ if (res == -1) RETURN(-1);
res = close(res);
} else {
res = mkdir(p, S_IRWXU);
}
- return res;
+ RETURN(res);
}
/**
* Create a file that hides path below branch_rw
*/
int hide_file(const char *path, int branch_rw) {
- DBG_IN();
- return do_create_whiteout(path, branch_rw, WHITEOUT_FILE);
+ DBG("%s\n", path);
+ RETURN(do_create_whiteout(path, branch_rw, WHITEOUT_FILE));
}
/**
* Create a directory that hides path below branch_rw
*/
int hide_dir(const char *path, int branch_rw) {
- DBG_IN();
- return do_create_whiteout(path, branch_rw, WHITEOUT_DIR);
+ DBG("%s\n", path);
+ RETURN(do_create_whiteout(path, branch_rw, WHITEOUT_DIR));
}
/**
@@ -183,14 +182,14 @@ int hide_dir(const char *path, int branch_rw) {
* if the same file/dir does exist in a lower branch
*/
int maybe_whiteout(const char *path, int branch_rw, enum whiteout mode) {
- DBG_IN();
+ DBG("%s\n", path);
// we are not interested in the branch itself, only if it exists at all
if (find_rorw_branch(path) != -1) {
- return do_create_whiteout(path, branch_rw, mode);
+ RETURN(do_create_whiteout(path, branch_rw, mode));
}
- return 0;
+ RETURN(0);
}
/**
@@ -201,11 +200,11 @@ int set_owner(const char *path) {
if (ctx->uid != 0 && ctx->gid != 0) {
int res = lchown(path, ctx->uid, ctx->gid);
if (res) {
- usyslog(LOG_WARNING,
+ USYSLOG(LOG_WARNING,
":%s: Setting the correct file owner failed: %s !\n",
__func__, strerror(errno));
- return -errno;
+ RETURN(-errno);
}
}
- return 0;
+ RETURN(0);
}
diff --git a/src/general.h b/src/general.h
index b2e3cbf..33a67c6 100644
--- a/src/general.h
+++ b/src/general.h
@@ -8,7 +8,6 @@
#define GENERAL_H
#include <stdbool.h>
-#include <syslog.h>
enum whiteout {
WHITEOUT_FILE,
@@ -21,7 +20,7 @@ typedef enum filetype {
IS_FILE=1,
} filetype_t;
-bool path_hidden(const char *path, int branch);
+int path_hidden(const char *path, int branch);
int remove_hidden(const char *path, int maxbranch);
int hide_file(const char *path, int branch_rw);
int hide_dir(const char *path, int branch_rw);
@@ -29,18 +28,5 @@ filetype_t path_is_dir (const char *path);
int maybe_whiteout(const char *path, int branch_rw, enum whiteout mode);
int set_owner(const char *path);
-/**
- * Calling syslog() will deadlock if the filesystem is for /etc or /var.
- * This is a bit unexpected, since I thought it would just write into a buffer
- * and then the syslog-daemon would then independetely of the unionfs thread
- * write it's log entry. However, it seems the syslog() call waits for until
- * the log entry is written, which will cause a deadlock, of course.
- * Until we find a solution for that, we simply disable syslogs.
- * The only sane solution comming presently into my mind is to write into
- * a buffer and then to have an independent thread which will flush this buffer
- * to syslog. Other suggestions are welcome, of course!
- */
-#define usyslog(...) do {} while(0)
-
#endif
diff --git a/src/hashtable.c b/src/hashtable.c
index 763357e..ac29af3 100644
--- a/src/hashtable.c
+++ b/src/hashtable.c
@@ -5,7 +5,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <math.h>
/*
Credit for primes table: Aaron Krowne
@@ -24,6 +23,25 @@ static const unsigned int primes[] = {
const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]);
const float max_load_factor = 0.65;
+#define TOL 1e-6 // float tolerance
+
+/**
+ * my_ceil - calculate ceil value
+ * Using C ceil() from math.h with -lm requires to link this library only for
+ * this simple function. As it is not performance relevent for the hash table
+ * we use our own * implementation.
+ *
+ */
+static int my_ceil(float x)
+{
+ int y = (int) x;
+ if (y - x > TOL)
+ return y;
+ else
+ return y + 1;
+}
+
+
/*****************************************************************************/
struct hashtable *
create_hashtable(unsigned int minsize,
@@ -48,7 +66,7 @@ create_hashtable(unsigned int minsize,
h->entrycount = 0;
h->hashfn = hashf;
h->eqfn = eqf;
- h->loadlimit = (unsigned int) ceil(size * max_load_factor);
+ h->loadlimit = my_ceil(size * max_load_factor);
return h;
}
@@ -121,7 +139,7 @@ hashtable_expand(struct hashtable *h)
}
}
h->tablelength = newsize;
- h->loadlimit = (unsigned int) ceil(newsize * max_load_factor);
+ h->loadlimit = my_ceil(newsize * max_load_factor);
return -1;
}
diff --git a/src/opts.c b/src/opts.c
index 2cdaa92..a00b30a 100644
--- a/src/opts.c
+++ b/src/opts.c
@@ -174,6 +174,9 @@ static int parse_branches(const char *arg) {
}
/**
+ * get_opt_str - get the parameter string
+ * @arg - option argument
+ * @opt_name - option name, used for error messages
* fuse passes arguments with the argument prefix, e.g.
* "-o chroot=/path/to/chroot/" will give us "chroot=/path/to/chroot/"
* and we need to cut off the "chroot=" part
@@ -181,17 +184,19 @@ static int parse_branches(const char *arg) {
* to the chroot, it is absolutely required
* -o chroot=path is provided before specifying the braches!
*/
-char * get_chroot(const char *arg)
+static char * get_opt_str(const char *arg, char *opt_name)
{
char *str = index(arg, '=');
if (!str) {
- fprintf(stderr, "-o chroot parameter not properly specified, aborting!\n");
+ fprintf(stderr, "-o %s parameter not properly specified, aborting!\n",
+ opt_name);
exit(1); // still early phase, we can abort
}
if (strlen(str) < 3) {
- fprintf(stderr, "Chroot path has not sufficient characters, aborting!\n");
+ fprintf(stderr, "%s path has not sufficient characters, aborting!\n",
+ opt_name);
exit(1);
}
@@ -216,20 +221,27 @@ static void print_help(const char *progname) {
"The first argument is a colon separated list of directories to merge\n"
"\n"
"general options:\n"
+ " -d Enable debug output\n"
" -o opt,[opt...] mount options\n"
" -h --help print help\n"
" -V --version print version\n"
"\n"
"UnionFS options:\n"
- " -o dirs=branch[=RO/RW][:branch...]\n"
- " alternate way to specify directories to merge\n"
- " -o cow enable copy-on-write\n"
- " -o stats show statistics in the file 'stats' under the\n"
- " mountpoint\n"
- " -o statfs_omit_ro do not count blocks of ro-branches\n"
" -o chroot=path chroot into this path. Use this if you \n"
" want to have a union of \"/\" \n"
+ " -o cow enable copy-on-write\n"
+ " mountpoint\n"
+ " -o debug_file file to write debug information into\n"
+ " -o dirs=branch[=RO/RW][:branch...]\n"
+ " alternate way to specify directories to merge\n"
+ " -o hide_meta_files \".unionfs\" is a secret directory not\n"
+ " visible by readdir(), and so are\n"
+ " .fuse_hidden* files\n"
" -o max_files=number Increase the maximum number of open files\n"
+ " -o relaxed_permissions Disable permissions checks, but only if\n"
+ " running neither as UID=0 or GID=0\n"
+ " -o statfs_omit_ro do not count blocks of ro-branches\n"
+ " -o stats show statistics in the file 'stats' under the\n"
"\n",
progname);
}
@@ -277,6 +289,7 @@ void unionfs_post_opts(void) {
exit(1);
}
uopt.branches[i].fd = fd;
+ uopt.branches[i].path_len = strlen(path);
}
}
@@ -297,32 +310,46 @@ int unionfs_opt_proc(void *data, const char *arg, int key, struct fuse_args *out
if (res > 0) return 0;
uopt.retval = 1;
return 1;
- case KEY_STATS:
- uopt.stats_enabled = 1;
+ case KEY_CHROOT:
+ uopt.chroot = get_opt_str(arg, "chroot");
return 0;
case KEY_COW:
uopt.cow_enabled = true;
return 0;
- case KEY_STATFS_OMIT_RO:
- uopt.statfs_omit_ro = true;
+ case KEY_DEBUG:
+ uopt.debug = true;
+ return 1;
+ case KEY_DEBUG_FILE:
+ uopt.dbgpath = get_opt_str(arg, "debug_file");
+ uopt.debug = true;
return 0;
- case KEY_NOINITGROUPS:
- // option only for compatibility with older versions
+ case KEY_HELP:
+ print_help(outargs->argv[0]);
+ fuse_opt_add_arg(outargs, "-ho");
+ uopt.doexit = 1;
return 0;
- case KEY_CHROOT:
- uopt.chroot = get_chroot(arg);
+ case KEY_HIDE_META_FILES:
+ case KEY_HIDE_METADIR:
+ uopt.hide_meta_files = true;
return 0;
case KEY_MAX_FILES:
set_max_open_files(arg);
return 0;
- case KEY_HELP:
- print_help(outargs->argv[0]);
- fuse_opt_add_arg(outargs, "-ho");
- uopt.doexit = 1;
+ case KEY_NOINITGROUPS:
+ // option only for compatibility with older versions
+ return 0;
+ case KEY_STATFS_OMIT_RO:
+ uopt.statfs_omit_ro = true;
+ return 0;
+ case KEY_RELAXED_PERMISSIONS:
+ uopt.relaxed_permissions = true;
+ return 0;
+ case KEY_STATS:
+ uopt.stats_enabled = 1;
return 0;
case KEY_VERSION:
printf("unionfs-fuse version: "VERSION"\n");
-#ifdef HAVE_SETXATTR
+#ifdef HAVE_XATTR
printf("(compiled with xattr support)\n");
#endif
uopt.doexit = 1;
diff --git a/src/opts.h b/src/opts.h
index 4168001..629e9d2 100644
--- a/src/opts.h
+++ b/src/opts.h
@@ -26,18 +26,27 @@ typedef struct {
int doexit;
int retval;
char *chroot; // chroot we might go into
+ bool debug; // enable debugging
+ char *dbgpath; // debug file we write debug information into
+ bool hide_meta_files;
+ bool relaxed_permissions;
} uopt_t;
enum {
- KEY_DIRS,
- KEY_STATS,
- KEY_COW,
- KEY_STATFS_OMIT_RO,
- KEY_NOINITGROUPS,
KEY_CHROOT,
- KEY_MAX_FILES,
+ KEY_COW,
+ KEY_DEBUG,
+ KEY_DEBUG_FILE,
+ KEY_DIRS,
KEY_HELP,
+ KEY_HIDE_META_FILES,
+ KEY_HIDE_METADIR,
+ KEY_MAX_FILES,
+ KEY_NOINITGROUPS,
+ KEY_RELAXED_PERMISSIONS,
+ KEY_STATFS_OMIT_RO,
+ KEY_STATS,
KEY_VERSION
};
diff --git a/src/readdir.c b/src/readdir.c
index 934b69a..6961c67 100644
--- a/src/readdir.c
+++ b/src/readdir.c
@@ -31,13 +31,41 @@
#include "general.h"
#include "string.h"
+
+/**
+ * Hide metadata. As is causes a slight slowndown this is optional
+ *
+ */
+static bool hide_meta_files(int branch, const char *path, struct dirent *de)
+{
+
+ if (uopt.hide_meta_files == false) RETURN(false);
+
+ fprintf(stderr, "uopt.branches[branch].path = %s path = %s\n", uopt.branches[branch].path, path);
+ fprintf(stderr, "METANAME = %s, de->d_name = %s\n", METANAME, de->d_name);
+
+ // TODO Would it be faster to add hash comparison?
+
+ // HIDE out .unionfs directory
+ if (strcmp(uopt.branches[branch].path, path) == 0
+ && strcmp(METANAME, de->d_name) == 0) {
+ RETURN(true);
+ }
+
+ // HIDE fuse META files
+ if (strncmp(FUSE_META_FILE, de->d_name, FUSE_META_LENGTH) == 0)
+ RETURN(true);
+
+ RETURN(false);
+}
+
/**
* Check if fname has a hiding tag and return its status.
* Also, add this file and to the hiding hash table.
* Warning: If fname has the tag, fname gets modified.
*/
static bool is_hiding(struct hashtable *hides, char *fname) {
- DBG_IN();
+ DBG("%s\n", fname);
char *tag;
@@ -52,17 +80,17 @@ static bool is_hiding(struct hashtable *hides, char *fname) {
hashtable_insert(hides, strdup(fname), malloc(1));
}
- return true;
+ RETURN(true);
}
- return false;
+ RETURN(false);
}
/**
* Read whiteout files
*/
static void read_whiteouts(const char *path, struct hashtable *whiteouts, int branch) {
- DBG_IN();
+ DBG("%s\n", path);
char p[PATHLEN_MAX];
if (BUILD_PATH(p, uopt.branches[branch].path, METADIR, path)) return;
@@ -82,11 +110,12 @@ static void read_whiteouts(const char *path, struct hashtable *whiteouts, int br
* unionfs-fuse readdir function
*/
int unionfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
- DBG_IN();
+ DBG("%s\n", path);
(void)offset;
(void)fi;
int i = 0;
+ int rc = 0;
// we will store already added files here to handle same file names across different branches
struct hashtable *files = create_hashtable(16, string_hash, string_equal);
@@ -101,10 +130,19 @@ int unionfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t o
if (subdir_hidden) break;
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) {
+ rc = -ENAMETOOLONG;
+ goto out;
+ }
// check if branches below this branch are hidden
- if (path_hidden(path, i)) subdir_hidden = true;
+ int res = path_hidden(path, i);
+ if (res < 0) {
+ rc = res; // error
+ goto out;
+ }
+
+ if (res > 0) subdir_hidden = true;
DIR *dp = opendir(p);
if (dp == NULL) {
@@ -123,6 +161,8 @@ int unionfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t o
if (hashtable_search(whiteouts, de->d_name) != NULL) continue;
}
+ if (hide_meta_files(i, p, de) == true) continue;
+
// fill with something dummy, we're interested in key existence only
hashtable_insert(files, strdup(de->d_name), malloc(1));
@@ -138,6 +178,7 @@ int unionfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t o
if (uopt.cow_enabled) read_whiteouts(path, whiteouts, i);
}
+out:
hashtable_destroy(files, 1);
if (uopt.cow_enabled) hashtable_destroy(whiteouts, 1);
@@ -146,5 +187,85 @@ int unionfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t o
filler(buf, "stats", NULL, 0);
}
- return 0;
+ RETURN(rc);
+}
+
+/**
+ * check if a directory on all paths is empty
+ * return 0 if empty, 1 if not and negative value on error
+ *
+ * TODO: This shares lots of code with unionfs_readdir(), can we merge
+ * both functions?
+ */
+int dir_not_empty(const char *path) {
+
+ DBG("%s\n", path);
+
+ int i = 0;
+ int rc = 0;
+ int not_empty = 0;
+
+ struct hashtable *whiteouts = NULL;
+
+ if (uopt.cow_enabled) whiteouts = create_hashtable(16, string_hash, string_equal);
+
+ bool subdir_hidden = false;
+
+ for (i = 0; i < uopt.nbranches; i++) {
+ if (subdir_hidden) break;
+
+ char p[PATHLEN_MAX];
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) {
+ rc = -ENAMETOOLONG;
+ goto out;
+ }
+
+ // check if branches below this branch are hidden
+ int res = path_hidden(path, i);
+ if (res < 0) {
+ rc = res; // error
+ goto out;
+ }
+
+ if (res > 0) subdir_hidden = true;
+
+ DIR *dp = opendir(p);
+ if (dp == NULL) {
+ if (uopt.cow_enabled) read_whiteouts(path, whiteouts, i);
+ continue;
+ }
+
+ struct dirent *de;
+ while ((de = readdir(dp)) != NULL) {
+
+ // Ignore . and ..
+ if ((strcmp(de->d_name, ".") == 0) || (strcmp(de->d_name, "..") == 0))
+ continue;
+
+ // check if we need file hiding
+ if (uopt.cow_enabled) {
+ // file should be hidden from the user
+ if (hashtable_search(whiteouts, de->d_name) != NULL) continue;
+ }
+
+ if (hide_meta_files(i, p, de) == true) continue;
+
+ // When we arrive here, a valid entry was found
+ not_empty = 1;
+ closedir(dp);
+ goto out;
+ }
+
+ closedir(dp);
+ if (uopt.cow_enabled) read_whiteouts(path, whiteouts, i);
+ }
+
+out:
+ if (uopt.cow_enabled) hashtable_destroy(whiteouts, 1);
+
+ if (rc) RETURN(rc);
+
+ RETURN(not_empty);
}
+
+
diff --git a/src/readdir.h b/src/readdir.h
index e52a622..cfbf227 100644
--- a/src/readdir.h
+++ b/src/readdir.h
@@ -4,5 +4,6 @@
#include <fuse.h>
int unionfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi);
+int dir_not_empty(const char *path);
#endif
diff --git a/src/rmdir.c b/src/rmdir.c
index fa380aa..5170425 100644
--- a/src/rmdir.c
+++ b/src/rmdir.c
@@ -31,16 +31,18 @@
#include "general.h"
#include "findbranch.h"
#include "string.h"
+#include "readdir.h"
+#include "usyslog.h"
/**
* If the branch that has the directory to be removed is in read-write mode,
* we can really delete the file.
*/
static int rmdir_rw(const char *path, int branch_rw) {
- DBG_IN();
+ DBG("%s\n", path);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[branch_rw].path, path);
+ if (BUILD_PATH(p, uopt.branches[branch_rw].path, path)) return ENAMETOOLONG;
int res = rmdir(p);
if (res == -1) return errno;
@@ -55,7 +57,7 @@ static int rmdir_rw(const char *path, int branch_rw) {
* lower level directory.
*/
static int rmdir_ro(const char *path, int branch_ro) {
- DBG_IN();
+ DBG("%s\n", path);
// find a writable branch above branch_ro
int branch_rw = find_lowest_rw_branch(branch_ro);
@@ -68,7 +70,7 @@ static int rmdir_ro(const char *path, int branch_ro) {
case (ENOTDIR):
case (ENOTEMPTY):
// catch errors not allowed for rmdir()
- usyslog (LOG_ERR, "%s: Creating the whiteout failed: %s\n",
+ USYSLOG (LOG_ERR, "%s: Creating the whiteout failed: %s\n",
__func__, strerror(errno));
errno = EFAULT;
}
@@ -82,7 +84,9 @@ static int rmdir_ro(const char *path, int branch_ro) {
* rmdir() call
*/
int unionfs_rmdir(const char *path) {
- DBG_IN();
+ DBG("%s\n", path);
+
+ if (dir_not_empty(path)) return -ENOTEMPTY;
int i = find_rorw_branch(path);
if (i == -1) return -errno;
diff --git a/src/string.c b/src/string.c
index 99b3011..339bfa3 100644
--- a/src/string.c
+++ b/src/string.c
@@ -23,13 +23,13 @@
#include "opts.h"
#include "debug.h"
#include "general.h"
-
+#include "usyslog.h"
/**
* Check if the given fname suffixes the hide tag
*/
char *whiteout_tag(const char *fname) {
- DBG_IN();
+ DBG("%s\n", fname);
char *tag = strstr(fname, HIDETAG);
@@ -50,32 +50,85 @@ char *whiteout_tag(const char *fname) {
* check if the sum of the strings is larger than PATHLEN_MAX
*
* This function requires a NULL as last argument!
+ *
+ * path already MUST have been allocated!
*/
-int build_path(char *dest, int max_len, char *callfunc, ...) {
- DBG_IN();
-
+int build_path(char *path, int max_len, const char *callfunc, int line, ...) {
va_list ap; // argument pointer
int len = 0;
+ char *str_ptr = path;
- dest[0] = '\0';
+ (void)str_ptr; // please the compile to avoid warning in non-debug mode
+ (void)line;
+ (void)callfunc;
- va_start(ap, callfunc);
+ path[0] = '\0'; // that way can easily strcat even the first element
+
+ va_start(ap, line);
while (1) {
- char *str = va_arg (ap, char *);
+ char *str = va_arg (ap, char *); // the next path element
if (!str) break;
- len += strlen(str) + 1; // + 1 for '/' between the pathes
+ /* Prevent '//' in pathes, if len > 0 we are not in the first
+ * loop-run. This is rather ugly, but I don't see another way to
+ * make sure there really is a '/'. By simply cutting off
+ * the initial '/' of the added string, we could run into a bug
+ * and would not have a '/' between path elements at all
+ * if somewhere else a directory slash has been forgotten... */
+ if (len > 0) {
+ // walk to the end of path
+ while (*path != '\0') path++;
+
+ // we are on '\0', now go back to the last char
+ path--;
+
+ if (*path == '/') {
+ int count = len;
+
+ // count makes sure nobody tricked us and gave
+ // slashes as first path only...
+ while (*path == '/' && count > 1) {
+ // possibly there are several slashes...
+ // But we want only one slash
+ path--;
+ count--;
+ }
+
+ // now we are *before* '/', walk to slash again
+ path++;
+
+ // eventually we walk over the slashes of the
+ // next string
+ while (*str == '/') str++;
+ } else if (*str != '/') {
+ // neither path ends with a slash, nor str
+ // starts with a slash, prevent a wrong path
+ strcat(path, "/");
+ len++;
+ }
+ }
+ va_end(ap);
- if (len > max_len) {
- usyslog (LOG_WARNING, "%s: Path too long \n", callfunc);
+ len += strlen(str);
+
+ // +1 for final \0 not counted by strlen
+ if (len + 1 > max_len) {
+ USYSLOG (LOG_WARNING, "%s():%d Path too long \n", callfunc, line);
errno = ENAMETOOLONG;
- return -errno;
+ RETURN(-errno);
}
- strcat (dest, str);
+ strcat (path, str);
}
-
- return 0;
+
+ if (len == 0) {
+ USYSLOG(LOG_ERR, "from: %s():%d : No argument given?\n", callfunc, line);
+ errno = EIO;
+ RETURN(-errno);
+ }
+
+ DBG("from: %s():%d path: %s\n", callfunc, line, str_ptr);
+ RETURN(0);
}
/**
@@ -84,11 +137,11 @@ int build_path(char *dest, int max_len, char *callfunc, ...) {
* implementation
*/
char *u_dirname(const char *path) {
- DBG_IN();
+ DBG("%s\n", path);
char *ret = strdup(path);
if (ret == NULL) {
- usyslog("strdup failed, probably out of memory!\n");
+ USYSLOG(LOG_WARNING, "strdup failed, probably out of memory!\n");
return ret;
}
@@ -111,7 +164,7 @@ char *u_dirname(const char *path) {
* str needs to NULL terminated
*/
static unsigned int elfhash(const char *str) {
- DBG_IN();
+ DBG("%s\n", str);
unsigned int hash = 0;
diff --git a/src/string.h b/src/string.h
index 7c04016..9ce9cf5 100644
--- a/src/string.h
+++ b/src/string.h
@@ -10,7 +10,7 @@
#include <string.h>
char *whiteout_tag(const char *fname);
-int build_path(char *dest, int max_len, ...);
+int build_path(char *dest, int max_len, const char *callfunc, int line, ...);
char *u_dirname(const char *path);
unsigned int string_hash(void *s);
@@ -19,7 +19,7 @@ unsigned int string_hash(void *s);
* a maximum string length. Since there is no way in C to determine the given number of arguments, we
* simply add NULL here.
*/
-#define BUILD_PATH(dest, ...) build_path(dest, PATHLEN_MAX, __func__, __VA_ARGS__, NULL)
+#define BUILD_PATH(dest, ...) build_path(dest, PATHLEN_MAX, __func__, __LINE__, __VA_ARGS__, NULL)
/**
* Test if two strings are eqal.
diff --git a/src/unionfs.c b/src/unionfs.c
index 0d08721..26e3b98 100644
--- a/src/unionfs.c
+++ b/src/unionfs.c
@@ -16,6 +16,7 @@
#endif
#include <fuse.h>
+#include <fuse/fuse_common.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -27,13 +28,14 @@
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
+#include <inttypes.h>
#ifdef linux
#include <sys/vfs.h>
#else
#include <sys/statvfs.h>
#endif
-#ifdef HAVE_SETXATTR
+#ifdef HAVE_XATTR
#include <sys/xattr.h>
#endif
@@ -48,52 +50,59 @@
#include "rmdir.h"
#include "readdir.h"
#include "cow.h"
+#include "string.h"
+#include "usyslog.h"
static struct fuse_opt unionfs_opts[] = {
+ FUSE_OPT_KEY("chroot=%s,", KEY_CHROOT),
+ FUSE_OPT_KEY("cow", KEY_COW),
+ FUSE_OPT_KEY("-d", KEY_DEBUG),
+ FUSE_OPT_KEY("debug_file=%s", KEY_DEBUG_FILE),
+ FUSE_OPT_KEY("dirs=%s", KEY_DIRS),
FUSE_OPT_KEY("--help", KEY_HELP),
- FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_KEY("-h", KEY_HELP),
- FUSE_OPT_KEY("-V", KEY_VERSION),
- FUSE_OPT_KEY("dirs=%s", KEY_DIRS),
- FUSE_OPT_KEY("stats", KEY_STATS),
- FUSE_OPT_KEY("cow", KEY_COW),
+ FUSE_OPT_KEY("hide_meta_dir", KEY_HIDE_METADIR),
+ FUSE_OPT_KEY("hide_meta_files", KEY_HIDE_META_FILES),
+ FUSE_OPT_KEY("max_files=%s", KEY_MAX_FILES),
FUSE_OPT_KEY("noinitgroups", KEY_NOINITGROUPS),
+ FUSE_OPT_KEY("relaxed_permissions", KEY_RELAXED_PERMISSIONS),
FUSE_OPT_KEY("statfs_omit_ro", KEY_STATFS_OMIT_RO),
- FUSE_OPT_KEY("chroot=%s,", KEY_CHROOT),
- FUSE_OPT_KEY("max_files=%s", KEY_MAX_FILES),
+ FUSE_OPT_KEY("stats", KEY_STATS),
+ FUSE_OPT_KEY("--version", KEY_VERSION),
+ FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_END
};
static int unionfs_chmod(const char *path, mode_t mode) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rw_branch_cow(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = chmod(p, mode);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return 0;
+ RETURN(0);
}
static int unionfs_chown(const char *path, uid_t uid, gid_t gid) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rw_branch_cow(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = lchown(p, uid, gid);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return 0;
+ RETURN(0);
}
/**
@@ -101,20 +110,20 @@ static int unionfs_chown(const char *path, uid_t uid, gid_t gid) {
* libfuse will call this to create regular files
*/
static int unionfs_create(const char *path, mode_t mode, struct fuse_file_info *fi) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rw_branch_cutlast(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
// NOTE: We should do:
// Create the file with mode=0 first, otherwise we might create
// a file as root + x-bit + suid bit set, which might be used for
// security racing!
int res = open(p, fi->flags, 0);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
set_owner(p); // no error check, since creating the file succeeded
@@ -124,7 +133,8 @@ static int unionfs_create(const char *path, mode_t mode, struct fuse_file_info *
fi->fh = res;
remove_hidden(path, i);
- return 0;
+ DBG("fd = %" PRIx64 "\n", fi->fh);
+ RETURN(0);
}
@@ -134,32 +144,32 @@ static int unionfs_create(const char *path, mode_t mode, struct fuse_file_info *
* which flush the data/metadata on close()
*/
static int unionfs_flush(const char *path, struct fuse_file_info *fi) {
- DBG_IN();
+ DBG("fd = %"PRIx64"\n", fi->fh);
- if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) return 0;
+ if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) RETURN(0);
int fd = dup(fi->fh);
if (fd == -1) {
// What to do now?
- if (fsync(fi->fh) == -1) return -EIO;
+ if (fsync(fi->fh) == -1) RETURN(-EIO);
- return -errno;
+ RETURN(-errno);
}
int res = close(fd);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return 0;
+ RETURN(0);
}
/**
* Just a stub. This method is optional and can safely be left unimplemented
*/
static int unionfs_fsync(const char *path, int isdatasync, struct fuse_file_info *fi) {
- DBG_IN();
+ DBG("fd = %"PRIx64"\n", fi->fh);
- if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) return 0;
+ if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) RETURN(0);
int res;
if (isdatasync) {
@@ -172,30 +182,30 @@ static int unionfs_fsync(const char *path, int isdatasync, struct fuse_file_info
res = fsync(fi->fh);
}
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return 0;
+ RETURN(0);
}
static int unionfs_getattr(const char *path, struct stat *stbuf) {
- DBG_IN();
+ DBG("%s\n", path);
if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) {
memset(stbuf, 0, sizeof(stbuf));
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = STATS_SIZE;
- return 0;
+ RETURN(0);
}
-
+
int i = find_rorw_branch(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = lstat(p, stbuf);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
/* This is a workaround for broken gnu find implementations. Actually,
* n_links is not defined at all for directories by posix. However, it
@@ -206,7 +216,7 @@ static int unionfs_getattr(const char *path, struct stat *stbuf) {
*/
if (S_ISDIR(stbuf->st_mode)) stbuf->st_nlink = 1;
- return 0;
+ RETURN(0);
}
/**
@@ -222,7 +232,7 @@ static void * unionfs_init(struct fuse_conn_info *conn) {
if (uopt.chroot) {
int res = chroot(uopt.chroot);
if (res) {
- usyslog("Chdir to %s failed: %s ! Aborting!\n",
+ USYSLOG(LOG_WARNING, "Chdir to %s failed: %s ! Aborting!\n",
uopt.chroot, strerror(errno));
exit(1);
}
@@ -231,27 +241,28 @@ static void * unionfs_init(struct fuse_conn_info *conn) {
}
static int unionfs_link(const char *from, const char *to) {
- DBG_IN();
+ DBG("from %s to %s\n", from, to);
// hardlinks do not work across different filesystems so we need a copy of from first
int i = find_rw_branch_cow(from);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
- // FIXME, we actually MUST COW to i
- int j = find_rw_branch_cutlast(to);
- if (j == -1) return -errno;
+ int j = __find_rw_branch_cutlast(to, i);
+ if (j == -1) RETURN(-errno);
+
+ DBG("from branch: %d to branch: %d\n", i, j);
char f[PATHLEN_MAX], t[PATHLEN_MAX];
- snprintf(f, PATHLEN_MAX, "%s%s", uopt.branches[i].path, from);
- snprintf(t, PATHLEN_MAX, "%s%s", uopt.branches[j].path, to);
+ if (BUILD_PATH(f, uopt.branches[i].path, from)) RETURN(-ENAMETOOLONG);
+ if (BUILD_PATH(t, uopt.branches[j].path, to)) RETURN(-ENAMETOOLONG);
int res = link(f, t);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
// no need for set_owner(), since owner and permissions are copied over by link()
remove_hidden(to, i); // remove hide file (if any)
- return 0;
+ RETURN(0);
}
/**
@@ -261,32 +272,32 @@ static int unionfs_link(const char *from, const char *to) {
* make already hidden sub-branches visible again.
*/
static int unionfs_mkdir(const char *path, mode_t mode) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rw_branch_cutlast(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = mkdir(p, 0);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
set_owner(p); // no error check, since creating the file succeeded
// NOW, that the file has the proper owner we may set the requested mode
chmod(p, mode);
- return 0;
+ RETURN(0);
}
static int unionfs_mknod(const char *path, mode_t mode, dev_t rdev) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rw_branch_cutlast(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int file_type = mode & S_IFMT;
int file_perm = mode & (S_PROT_MASK);
@@ -298,15 +309,15 @@ static int unionfs_mknod(const char *path, mode_t mode, dev_t rdev) {
// since we now have the unionfs_create() method
// So can we remove it?
- usyslog (LOG_INFO, "deprecated mknod workaround, tell the unionfs-fuse authors if you see this!\n");
+ USYSLOG (LOG_INFO, "deprecated mknod workaround, tell the unionfs-fuse authors if you see this!\n");
res = creat(p, 0);
- if (res > 0 && close(res) == -1) usyslog(LOG_WARNING, "Warning, cannot close file\n");
+ if (res > 0 && close(res) == -1) USYSLOG(LOG_WARNING, "Warning, cannot close file\n");
} else {
res = mknod(p, file_type, rdev);
}
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
set_owner(p); // no error check, since creating the file succeeded
// NOW, that the file has the proper owner we may set the requested mode
@@ -314,19 +325,19 @@ static int unionfs_mknod(const char *path, mode_t mode, dev_t rdev) {
remove_hidden(path, i);
- return 0;
+ RETURN(0);
}
static int unionfs_open(const char *path, struct fuse_file_info *fi) {
- DBG_IN();
+ DBG("%s\n", path);
if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) {
if ((fi->flags & 3) == O_RDONLY) {
// This makes exec() fail
//fi->direct_io = 1;
- return 0;
+ RETURN(0);
}
- return -EACCES;
+ RETURN(-EACCES);
}
int i;
@@ -336,13 +347,13 @@ static int unionfs_open(const char *path, struct fuse_file_info *fi) {
i = find_rorw_branch(path);
}
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int fd = open(p, fi->flags);
- if (fd == -1) return -errno;
+ if (fd == -1) RETURN(-errno);
if (fi->flags & (O_WRONLY | O_RDWR)) {
// There might have been a hide file, but since we successfully
@@ -354,11 +365,12 @@ static int unionfs_open(const char *path, struct fuse_file_info *fi) {
//fi->direct_io = 1;
fi->fh = (unsigned long)fd;
- return 0;
+ DBG("fd = %"PRIx64"\n", fi->fh);
+ RETURN(0);
}
static int unionfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
- DBG_IN();
+ DBG("fd = %"PRIx64"\n", fi->fh);
if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) {
char out[STATS_SIZE] = "";
@@ -377,40 +389,40 @@ static int unionfs_read(const char *path, char *buf, size_t size, off_t offset,
int res = pread(fi->fh, buf, size, offset);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
if (uopt.stats_enabled) stats_add_read(&stats, size);
- return res;
+ RETURN(res);
}
static int unionfs_readlink(const char *path, char *buf, size_t size) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rorw_branch(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = readlink(p, buf, size - 1);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
buf[res] = '\0';
- return 0;
+ RETURN(0);
}
static int unionfs_release(const char *path, struct fuse_file_info *fi) {
- DBG_IN();
+ DBG("fd = %"PRIx64"\n", fi->fh);
- if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) return 0;
+ if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) RETURN(0);
int res = close(fi->fh);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return 0;
+ RETURN(0);
}
/**
@@ -419,34 +431,34 @@ static int unionfs_release(const char *path, struct fuse_file_info *fi) {
* all files to the renamed directory on the read-write branch.
*/
static int unionfs_rename(const char *from, const char *to) {
- DBG_IN();
+ DBG("from %s to %s\n", from, to);
bool is_dir = false; // is 'from' a file or directory
int j = find_rw_branch_cutlast(to);
- if (j == -1) return -errno;
+ if (j == -1) RETURN(-errno);
int i = find_rorw_branch(from);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
if (!uopt.branches[i].rw) {
i = find_rw_branch_cow(from);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
}
if (i != j) {
- usyslog(LOG_ERR, "%s: from and to are on different writable branches %d vs %d, which"
+ USYSLOG(LOG_ERR, "%s: from and to are on different writable branches %d vs %d, which"
"is not supported yet.\n", __func__, i, j);
- return -EXDEV;
+ RETURN(-EXDEV);
}
char f[PATHLEN_MAX], t[PATHLEN_MAX];
- snprintf(f, PATHLEN_MAX, "%s%s", uopt.branches[i].path, from);
- snprintf(t, PATHLEN_MAX, "%s%s", uopt.branches[i].path, to);
+ if (BUILD_PATH(f, uopt.branches[i].path, from)) RETURN(-ENAMETOOLONG);
+ if (BUILD_PATH(t, uopt.branches[i].path, to)) RETURN(-ENAMETOOLONG);
filetype_t ftype = path_is_dir(f);
if (ftype == NOT_EXISTING)
- return -ENOENT;
+ RETURN(-ENOENT);
else if (ftype == IS_DIR)
is_dir = true;
@@ -458,7 +470,7 @@ static int unionfs_rename(const char *from, const char *to) {
res = hide_dir(from, i);
else
res = hide_file(from, i);
- if (res) return -errno;
+ if (res) RETURN(-errno);
}
res = rename(f, t);
@@ -468,14 +480,14 @@ static int unionfs_rename(const char *from, const char *to) {
// if from was on a read-only branch we copied it, but now rename failed so we need to delete it
if (!uopt.branches[i].rw) {
if (unlink(f))
- usyslog(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now "
+ USYSLOG(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now "
"also unlink() failed\n", __func__, from);
if (remove_hidden(from, i))
- usyslog(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now "
+ USYSLOG(LOG_ERR, "%s: cow of %s succeeded, but rename() failed and now "
"also removing the whiteout failed\n", __func__, from);
}
- return -err;
+ RETURN(-err);
}
if (uopt.branches[i].rw) {
@@ -489,7 +501,7 @@ static int unionfs_rename(const char *from, const char *to) {
}
remove_hidden(to, i); // remove hide file (if any)
- return 0;
+ RETURN(0);
}
/**
@@ -508,7 +520,7 @@ static int statvfs_local(const char *path, struct statvfs *stbuf) {
*/
struct statfs stfs;
int res = statfs(path, &stfs);
- if (res == -1) return res;
+ if (res == -1) RETURN(res);
memset(stbuf, 0, sizeof(*stbuf));
stbuf->f_bsize = stfs.f_bsize;
@@ -532,9 +544,9 @@ static int statvfs_local(const char *path, struct statvfs *stbuf) {
stbuf->f_flag = 0;
stbuf->f_namemax = stfs.f_namelen;
- return 0;
+ RETURN(0);
#else
- return statvfs(path, stbuf);
+ RETURN(statvfs(path, stbuf));
#endif
}
@@ -549,7 +561,7 @@ static int statvfs_local(const char *path, struct statvfs *stbuf) {
static int unionfs_statfs(const char *path, struct statvfs *stbuf) {
(void)path;
- DBG_IN();
+ DBG("%s\n", path);
int first = 1;
@@ -610,139 +622,139 @@ static int unionfs_statfs(const char *path, struct statvfs *stbuf) {
}
}
- return 0;
+ RETURN(0);
}
static int unionfs_symlink(const char *from, const char *to) {
- DBG_IN();
+ DBG("from %s to %s\n", from, to);
int i = find_rw_branch_cutlast(to);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char t[PATHLEN_MAX];
- snprintf(t, PATHLEN_MAX, "%s%s", uopt.branches[i].path, to);
+ if (BUILD_PATH(t, uopt.branches[i].path, to)) RETURN(-ENAMETOOLONG);
int res = symlink(from, t);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
set_owner(t); // no error check, since creating the file succeeded
remove_hidden(to, i); // remove hide file (if any)
- return 0;
+ RETURN(0);
}
static int unionfs_truncate(const char *path, off_t size) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rw_branch_cow(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = truncate(p, size);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return 0;
+ RETURN(0);
}
static int unionfs_utimens(const char *path, const struct timespec ts[2]) {
- DBG_IN();
+ DBG("%s\n", path);
- if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) return 0;
+ if (uopt.stats_enabled && strcmp(path, STATS_FILENAME) == 0) RETURN(0);
int i = find_rw_branch_cow(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = utimensat(0, p, ts, AT_SYMLINK_NOFOLLOW);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return 0;
+ RETURN(0);
}
static int unionfs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
(void)path;
- DBG_IN();
+ DBG("fd = %"PRIx64"\n", fi->fh);
int res = pwrite(fi->fh, buf, size, offset);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
if (uopt.stats_enabled) stats_add_written(&stats, size);
- return res;
+ RETURN(res);
}
-#ifdef HAVE_SETXATTR
+#ifdef HAVE_XATTR
static int unionfs_getxattr(const char *path, const char *name, char *value, size_t size) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rorw_branch(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = lgetxattr(p, name, value, size);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return res;
+ RETURN(res);
}
static int unionfs_listxattr(const char *path, char *list, size_t size) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rorw_branch(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = llistxattr(p, list, size);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return res;
+ RETURN(res);
}
static int unionfs_removexattr(const char *path, const char *name) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rw_branch_cow(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = lremovexattr(p, name);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return res;
+ RETURN(res);
}
static int unionfs_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rw_branch_cow(path);
- if (i == -1) return -errno;
+ if (i == -1) RETURN(-errno);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[i].path, path);
+ if (BUILD_PATH(p, uopt.branches[i].path, path)) RETURN(-ENAMETOOLONG);
int res = lsetxattr(p, name, value, size, flags);
- if (res == -1) return -errno;
+ if (res == -1) RETURN(-errno);
- return res;
+ RETURN(res);
}
-#endif // HAVE_SETXATTR
+#endif // HAVE_XATTR
static struct fuse_operations unionfs_oper = {
.chmod = unionfs_chmod,
@@ -768,7 +780,7 @@ static struct fuse_operations unionfs_oper = {
.unlink = unionfs_unlink,
.utimens = unionfs_utimens,
.write = unionfs_write,
-#ifdef HAVE_SETXATTR
+#ifdef HAVE_XATTR
.getxattr = unionfs_getxattr,
.listxattr = unionfs_listxattr,
.removexattr = unionfs_removexattr,
@@ -779,17 +791,17 @@ static struct fuse_operations unionfs_oper = {
int main(int argc, char *argv[]) {
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
- int res = debug_init();
- if (res != 0) return res;
-
+ init_syslog();
uopt_init();
- if (fuse_opt_parse(&args, NULL, unionfs_opts, unionfs_opt_proc) == -1) return 1;
+ if (fuse_opt_parse(&args, NULL, unionfs_opts, unionfs_opt_proc) == -1) RETURN(1);
+
+ if (uopt.debug) debug_init();
if (!uopt.doexit) {
if (uopt.nbranches == 0) {
printf("You need to specify at least one branch!\n");
- return 1;
+ RETURN(1);
}
if (uopt.stats_enabled) stats_init(&stats);
@@ -797,13 +809,36 @@ int main(int argc, char *argv[]) {
// enable fuse permission checks, we need to set this, even we we are
// not root, since we don't have our own access() function
- if (fuse_opt_add_arg(&args, "-odefault_permissions")) {
- fprintf(stderr, "Severe failure, can't enable permssion checks, aborting!\n");
+ int uid = getuid();
+ int gid = getgid();
+ bool default_permissions = true;
+
+ if (uid != 0 && gid != 0 && uopt.relaxed_permissions) {
+ default_permissions = false;
+ } else if (uopt.relaxed_permissions) {
+ // protec the user of a very critical security issue
+ fprintf(stderr, "Relaxed permissions disallowed for root!\n");
exit(1);
}
+
+ if (default_permissions) {
+ if (fuse_opt_add_arg(&args, "-odefault_permissions")) {
+ fprintf(stderr, "Severe failure, can't enable permssion checks, aborting!\n");
+ exit(1);
+ }
+ }
unionfs_post_opts();
+#ifdef FUSE_CAP_BIG_WRITES
+ /* libfuse > 0.8 supports large IO, also for reads, to increase performance
+ * We support any IO sizes, so lets enable that option */
+ if (fuse_opt_add_arg(&args, "-obig_writes")) {
+ fprintf(stderr, "Failed to enable big writes!\n");
+ exit(1);
+ }
+#endif
+
umask(0);
- res = fuse_main(args.argc, args.argv, &unionfs_oper, NULL);
- return uopt.doexit ? uopt.retval : res;
+ int res = fuse_main(args.argc, args.argv, &unionfs_oper, NULL);
+ RETURN(uopt.doexit ? uopt.retval : res);
}
diff --git a/src/unionfs.h b/src/unionfs.h
index f941156..74b824f 100644
--- a/src/unionfs.h
+++ b/src/unionfs.h
@@ -9,13 +9,20 @@
#define PATHLEN_MAX 1024
#define HIDETAG "_HIDDEN~"
-#define METADIR ".unionfs/"
+
+#define METANAME ".unionfs"
+#define METADIR (METANAME "/") // string concetanation!
+
+// fuse meta files, we might want to hide those
+#define FUSE_META_FILE ".fuse_hidden"
+#define FUSE_META_LENGTH 12
// file access protection mask
#define S_PROT_MASK (S_ISUID| S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
typedef struct {
char *path;
+ int path_len; // strlen(path)
int fd; // used to prevent accidental umounts of path
unsigned char rw; // the writable flag
} branch_entry_t;
diff --git a/src/unlink.c b/src/unlink.c
index 06a7fa2..8b4000d 100644
--- a/src/unlink.c
+++ b/src/unlink.c
@@ -28,6 +28,7 @@
#include "cow.h"
#include "general.h"
#include "findbranch.h"
+#include "string.h"
/**
* If the branch that has the file to be unlinked is in read-only mode,
@@ -36,20 +37,20 @@
* lower level file.
*/
static int unlink_ro(const char *path, int branch_ro) {
- DBG_IN()
+ DBG("%s\n", path);
// find a writable branch above branch_ro
int branch_rw = find_lowest_rw_branch(branch_ro);
- if (branch_rw < 0) return EACCES;
+ if (branch_rw < 0) RETURN(EACCES);
if (hide_file(path, branch_rw) == -1) {
// creating the file with the hide tag failed
// TODO: open() error messages are not optimal on unlink()
- return errno;
+ RETURN(errno);
}
- return 0;
+ RETURN(0);
}
/**
@@ -57,25 +58,25 @@ static int unlink_ro(const char *path, int branch_ro) {
* we can really delete the file.
*/
static int unlink_rw(const char *path, int branch_rw) {
- DBG_IN();
+ DBG("%s\n", path);
char p[PATHLEN_MAX];
- snprintf(p, PATHLEN_MAX, "%s%s", uopt.branches[branch_rw].path, path);
+ if (BUILD_PATH(p, uopt.branches[branch_rw].path, path)) RETURN(ENAMETOOLONG);
int res = unlink(p);
- if (res == -1) return errno;
+ if (res == -1) RETURN(errno);
- return 0;
+ RETURN(0);
}
/**
* unlink() call
*/
int unionfs_unlink(const char *path) {
- DBG_IN();
+ DBG("%s\n", path);
int i = find_rorw_branch(path);
- if (i == -1) return errno;
+ if (i == -1) RETURN(errno);
int res;
if (!uopt.branches[i].rw) {
@@ -94,5 +95,5 @@ int unionfs_unlink(const char *path) {
}
}
- return -res;
+ RETURN(-res);
}
diff --git a/src/usyslog.c b/src/usyslog.c
new file mode 100644
index 0000000..75becac
--- /dev/null
+++ b/src/usyslog.c
@@ -0,0 +1,296 @@
+/*
+ * License: BSD-style license
+ * Copyright: Bernd Schubert <bernd.schubert@fastmail.fm>
+ *
+ * Details:
+ * Log files might be located on our own filesystem. If we then want to log
+ * a message to syslog, we would need to log to ourself, which easily ends up
+ * in a deadlock. Initializing openlog() using the flags
+ * LOG_NDELAY | LOG_NOWAIT should prevent that, but real live has shown that
+ * this does not work reliable and systems 'crashed' just because we
+ * tried to log a harmless message.
+ * So this file introduces a syslog thread and a syslog buffer. usyslog()
+ * calls write without a risk to deadlock into the syslog buffer (chained
+ * list) and then the seperate syslog_thread call syslog(). That way our
+ * our filesystem thread(s) cannot stall from syslog() calls.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <stdarg.h>
+
+#include "usyslog.h"
+#include "debug.h"
+
+static ulogs_t *free_head, *free_bottom; // free chained list log entries
+static ulogs_t *used_head = NULL, *used_bottom = NULL; //used chained list pointers
+
+static pthread_mutex_t list_lock; // locks the entire chained list
+static pthread_cond_t cond_message; // used to wake up the syslog thread
+
+// Only used for debugging, protected by list_lock
+static int free_entries;
+static int used_entries = 0;
+
+//#define USYSLOG_DEBUG
+
+#ifdef USYSLOG_DEBUG
+static void verify_lists()
+{
+ pthread_mutex_lock(&list_lock);
+
+ ulogs_t *entry = free_head;
+ int free_count = -1;
+ bool first_free = true;
+ while (entry) {
+ if (first_free) {
+ first_free = false;
+ free_count = 1;
+ } else
+ free_count++;
+ entry = entry->next;
+ }
+ if (free_count != free_entries && free_entries != 0)
+ DBG("usyslog list error detected: number of free entries inconsistent!"
+ " %d vs. %d", free_count, free_entries);
+
+ entry = used_head;
+ int used_count = -1;
+ bool first_used = true;
+ while (entry) {
+ if (first_used) {
+ first_used = false;
+ used_count = 1;
+ } else
+ used_count++;
+ entry = entry->next;
+ }
+ if (used_count != used_entries && used_entries != 0)
+ DBG("usyslog list error detected: number of used entries inconsistent!"
+ " (used: %d vs. %d) (free: %d vs. %d) \n",
+ used_count, used_entries, free_count, free_entries);
+
+ pthread_mutex_unlock(&list_lock);
+}
+#else
+#define verify_lists()
+#endif
+
+
+/**
+ * Walks the chained used-list and calls syslog()
+ */
+static void do_syslog(void)
+{
+ pthread_mutex_lock(&list_lock); // we MUST ensure not to keep that forever
+
+ ulogs_t *log_entry = used_head;
+
+ while (log_entry) {
+ pthread_mutex_t *entry_lock = &log_entry->lock;
+ int res = pthread_mutex_trylock(entry_lock);
+ if (res) {
+ if (res != EBUSY)
+ DBG("Entirely unexpected locking error %s\n",
+ strerror(res));
+ // If something goes wrong with the log_entry we do not
+ // block the critical list_lock forever!
+ // EBUSY might come up rarely, if we race with usyslog()
+ pthread_mutex_unlock(&list_lock);
+ sleep(1);
+ pthread_mutex_lock(&list_lock);
+ log_entry = used_head;
+ continue;
+ }
+ pthread_mutex_unlock(&list_lock);
+
+ // This syslog call and so this lock might block, so be
+ // carefull on using locks! The filesystem IO thread
+ // *MUST* only try to lock it using pthread_mutex_trylock()
+ syslog(log_entry->priority, "%s", log_entry->message);
+ log_entry->used = false;
+
+ // NOTE: The list is only locked now, after syslog() succeeded!
+ pthread_mutex_lock(&list_lock);
+ ulogs_t *next_entry = log_entry->next; // just to save the pointer
+
+ used_head = log_entry->next;
+ if (!used_head)
+ used_bottom = NULL; // no used entries left
+
+ if (free_bottom)
+ free_bottom->next = log_entry;
+ free_bottom = log_entry;
+ free_bottom->next = NULL;
+
+ if (!free_head)
+ free_head = log_entry;
+
+ free_entries++;
+ used_entries--;
+
+ pthread_mutex_unlock(&list_lock); // unlock ist ASAP
+
+ log_entry = next_entry;
+ pthread_mutex_unlock(entry_lock);
+ }
+
+ verify_lists();
+}
+
+/**
+ * syslog backgroung thread that tries to to empty the syslog buffer
+ */
+static void * syslog_thread(void *arg)
+{
+ // FIXME: What is a better way to prevent a compiler warning about
+ // unused variable 'arg'
+ int tinfo = *((int *) arg);
+ if (1 == 0)
+ printf("Starting thread %d", tinfo);
+
+ pthread_mutex_t sleep_mutex;
+
+ pthread_mutex_init(&sleep_mutex, NULL);
+ pthread_mutex_lock(&sleep_mutex);
+ while (1) {
+ pthread_cond_wait(&cond_message, &sleep_mutex);
+ do_syslog();
+ }
+
+ return NULL;
+}
+
+/**
+ * usyslog - function to be called if something shall be logged to syslog
+ */
+void usyslog(int priority, const char *format, ...)
+{
+ int res;
+ ulogs_t *log;
+
+ // Lock the entire list first, which means the syslog thread MUST NOT
+ // lock it if there is any chance it might be locked forever.
+ pthread_mutex_lock(&list_lock);
+
+ // Some sanity checks. If we fail here, we will leak a log entry,
+ // but will not lock up.
+
+ if (free_head == NULL) {
+ DBG("All syslog entries already busy\n");
+ pthread_mutex_unlock(&list_lock);
+ return;
+ }
+
+ log = free_head;
+ free_head = log->next;
+
+ res = pthread_mutex_trylock(&log->lock);
+ if (res == EBUSY) {
+ // huh, that never should happen!
+ DBG("Critical log error, log entry is BUSY, but should not\n");
+ pthread_mutex_unlock(&list_lock);
+ return;
+ } else if (res) {
+ // huh, that never should happen either!
+ DBG("Never should happen, can get lock: %s\n", strerror(res));
+ pthread_mutex_unlock(&list_lock);
+ return;
+ }
+
+ if (log->used) {
+ // huh, that never should happen either!
+ DBG("Never should happen, entry is busy, but should not!\n");
+ pthread_mutex_unlock(&log->lock);
+ pthread_mutex_unlock(&list_lock);
+ return;
+ }
+
+ if (!used_head)
+ used_head = used_bottom = log;
+ else {
+ used_bottom->next = log;
+ used_bottom = log;
+ }
+
+ if (log->next) {
+ // from free_list to end of used_list, so next is NULL now
+ log->next = NULL;
+ } else {
+ // so the last entry in free_list
+ free_bottom = NULL;
+ }
+
+
+ free_entries--;
+ used_entries++;
+
+ // Everything below is log entry related, so we can free the list_lock
+ pthread_mutex_unlock(&list_lock);
+
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(log->message, MAX_MSG_SIZE, format, ap);
+ log->priority = priority;
+ log->used = 1;
+
+ pthread_mutex_unlock(&log->lock);
+
+ pthread_cond_signal(&cond_message); // wake up the syslog thread
+}
+
+/**
+ * Initialize syslogs
+ */
+void init_syslog(void)
+{
+ openlog("unionfs-fuse: ", LOG_CONS | LOG_NDELAY | LOG_NOWAIT | LOG_PID, LOG_DAEMON);
+
+ pthread_mutex_init(&list_lock, NULL);
+ pthread_cond_init(&cond_message, NULL);
+ pthread_t thread;
+ pthread_attr_t attr;
+ int t_arg = 0; // thread argument, not required for us
+
+ int i;
+ ulogs_t *log, *last = NULL;
+ for (i = 0; i < MAX_SYSLOG_MESSAGES; i++) {
+ log = malloc(sizeof(ulogs_t));
+ if (log == NULL) {
+ fprintf(stderr, "\nLog initialization failed: %s\n", strerror(errno));
+ fprintf(stderr, "Aborting!\n");
+ // Still initialazation phase, we can abort.
+ exit (1);
+ }
+
+ log->used = false;
+ pthread_mutex_init(&log->lock, NULL);
+
+ if (last) {
+ last->next = log;
+ } else {
+ // so the very first entry
+ free_head = log;
+ }
+ last = log;
+ }
+ last->next = NULL;
+ free_bottom = last;
+
+ free_entries = MAX_SYSLOG_MESSAGES;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ int res = pthread_create(&thread, &attr, syslog_thread, (void *) &t_arg);
+ if (res != 0) {
+ fprintf(stderr, "Failed to initialize the syslog threads: %s\n",
+ strerror(res));
+ exit(1);
+ }
+}
+
diff --git a/src/usyslog.h b/src/usyslog.h
new file mode 100644
index 0000000..5b127a9
--- /dev/null
+++ b/src/usyslog.h
@@ -0,0 +1,31 @@
+/*
+ * License: BSD-style license
+ * Copyright: Bernd Schubert <bernd.schubert@fastmail.fm>
+ */
+
+#include <syslog.h>
+#include <stdbool.h>
+
+#define MAX_SYSLOG_MESSAGES 32 // max number of buffered syslog messages
+#define MAX_MSG_SIZE 256 // max string length for syslog messages
+
+/* chained buffer list of syslog entries */
+typedef struct ulogs {
+ int priority; // first argument for syslog()
+ char message[MAX_MSG_SIZE]; // 2nd argument for syslog()
+ bool used; // is this entry used?
+ pthread_mutex_t lock; // lock a single entry
+ struct ulogs *next; // pointer to the next entry
+} ulogs_t;
+
+
+void init_syslog(void);
+void usyslog(int priority, const char *format, ...);
+
+
+#define USYSLOG(priority, format, ...) \
+ do { \
+ DBG(format, ##__VA_ARGS__); \
+ usyslog(priority, format, ##__VA_ARGS__); \
+ } while (0);
+
diff --git a/test.sh b/test.sh
index fa488b0..0115d53 100755
--- a/test.sh
+++ b/test.sh
@@ -1,59 +1,87 @@
#!/bin/bash
+
set -v
set -e
-rm -rf original base working-copy
-mkdir original base working-copy original/play-dir original/del-dir
+
+rm -rf original union working-copy
+mkdir original union working-copy original/play-dir original/del-dir
echo v1 > original/file
echo v1 > original/play-with-me
echo v1 > original/delete-me
-src/unionfs -o cow working-copy=rw:original=ro base
-trap 'if [ "$(ls base)" ]; then umount base; fi; rm -rf base original working-copy' EXIT
+src/unionfs -d -o cow working-copy=rw:original=ro union >unionfs.log 2>&1 &
+trap 'if [ "$(ls union)" ]; then fusermount -u union; fi; rm -rf union original working-copy' EXIT
sleep 1
-[ "$(cat base/file)" = "v1" ]
+[ "$(cat union/file)" = "v1" ]
+
+echo "v2" > union/file
+[ "$(cat union/file)" = "v2" ]
-echo "v2" > base/file
-[ "$(cat base/file)" = "v2" ]
+echo "v2" > union/play-with-me
+[ "$(cat union/play-with-me)" = "v2" ]
-echo "v2" > base/play-with-me
-[ "$(cat base/play-with-me)" = "v2" ]
+[ -f union/play-with-me ]
+rm union/play-with-me
+[ ! -f union/play-with-me ]
-[ -f base/play-with-me ]
-rm base/play-with-me
-[ ! -f base/play-with-me ]
+[ -f union/delete-me ]
+rm union/delete-me
+[ ! -f union/delete-me ]
-[ -f base/delete-me ]
-rm base/delete-me
-[ ! -f base/delete-me ]
+[ "$(ls union/play-dir)" = "" ]
+echo "fool" > union/play-dir/foo
+[ "$(ls union/play-dir)" = "foo" ]
+rm union/play-dir/foo
+[ "$(ls union/play-dir)" = "" ]
-[ "$(ls base/play-dir)" = "" ]
-echo "fool" > base/play-dir/foo
-[ "$(ls base/play-dir)" = "foo" ]
-rm base/play-dir/foo
-[ "$(ls base/play-dir)" = "" ]
+[ -d union/play-dir ]
+rmdir union/play-dir
+[ ! -d union/play-dir ]
-[ -d base/play-dir ]
-rmdir base/play-dir
-[ ! -d base/play-dir ]
+[ -d union/del-dir ]
+rmdir union/del-dir
+[ ! -d union/del-dir ]
-[ -d base/del-dir ]
-rmdir base/del-dir
-[ ! -d base/del-dir ]
+! echo v1 > union/del-dir/foo
+
+[ ! -d union/del-dir ]
+mkdir union/del-dir
+[ ! -f union/del-dir/foo ]
+echo v1 > union/del-dir/foo
+[ -f union/del-dir/foo ]
+rm union/del-dir/foo
+[ -d union/del-dir ]
+rmdir union/del-dir
+[ ! -d union/del-dir ]
+
+# rmdir() test
+set +e
+set +v
+rc=0
+mkdir original/testdir
+touch original/testdir/testfile
+mkdir working-copy/testdir
+rmdir union/testdir 2>/dev/null
+if [ $? -eq 0 ]; then
+ echo "rmdir succeeded, although it must not"
+ rc=$(($rc + $?))
+fi
+rm union/testdir/testfile
+rc=$(($rc + $?))
+rmdir union/testdir/
+rc=$(($rc + $?))
+if [ $rc -ne 0 ]; then
+ echo "rmdir test failed"
+ exit 1
+else
+ echo "rmdir test passed"
+fi
+set -e
-! echo v1 > base/del-dir/foo
-[ ! -d base/del-dir ]
-mkdir base/del-dir
-[ ! -f base/del-dir/foo ]
-echo v1 > base/del-dir/foo
-[ -f base/del-dir/foo ]
-rm base/del-dir/foo
-[ -d base/del-dir ]
-rmdir base/del-dir
-[ ! -d base/del-dir ]
-fusermount -u base
+fusermount -u union
[ "$(cat original/file)" = "v1" ]
[ "$(cat original/play-with-me)" = "v1" ]