mirror of https://github.com/mpartel/bindfs.git

commit
2135ad723d
22 changed files with 3512 additions and 0 deletions
@ -0,0 +1,32 @@
|
||||
# Autotools stuff |
||||
Makefile.in |
||||
Makefile |
||||
|
||||
/autom4te.cache |
||||
/aclocal.m4 |
||||
/compile |
||||
/configure |
||||
/depcomp |
||||
/install-sh |
||||
/missing |
||||
|
||||
/config.log |
||||
/config.sub |
||||
/config.guess |
||||
/config.status |
||||
/config.h.in |
||||
/config.h |
||||
/libtool |
||||
/ltmain.sh |
||||
/stamp-h1 |
||||
|
||||
# C stuff |
||||
|
||||
*.o |
||||
.libs |
||||
.deps |
||||
|
||||
# Products |
||||
|
||||
src/bindfs |
||||
|
@ -0,0 +1,135 @@
|
||||
2010-08-07 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Improved --help and manpage. |
||||
* Disabled FUSE attribute cache when using mirroring to avoid |
||||
caching the owner of files when observed by a mirrored user. |
||||
* Added a testcase for the above. |
||||
* Released 1.9 |
||||
|
||||
2010-01-17 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Added options to control the behavior of chown and chgrp. |
||||
* Released 1.8.4 |
||||
|
||||
2009-03-28 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Added --ctime-from-mtime. Contributed by Shez. |
||||
* Added --chmod-allow-x. |
||||
* Released 1.8.3 |
||||
|
||||
2008-12-14 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Converted ChangeLog to UTF-8. |
||||
|
||||
2008-12-13 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Specified that the license is GPL v2 or later in all source files |
||||
and in the README file. |
||||
* Released 1.8.2 with no functional changes. |
||||
|
||||
2008-12-12 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Made xattr-rw the default instead of xattr-ro, |
||||
which returned a "permission denied" that could mislead some programs. |
||||
* Released 1.8.1 |
||||
|
||||
2008-08-17 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Fixed segfault in option parsing on platforms where |
||||
sizeof(int) != sizeof(long), such as amd64. |
||||
* Released 1.8 |
||||
|
||||
2008-07-08 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Symlinks to absolute paths didn't work. Now they do. |
||||
Reported by rpfuller. Thanks! |
||||
* Ownership of symlinks weren't set. Now they are. |
||||
Again, reported by rpfuller. Thanks again! |
||||
* Released 1.7 |
||||
|
||||
2008-06-26 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* --create-as-* and --create-for-* weren't applied for mknod(). |
||||
Bug report and patch by rpfuller. Thanks! |
||||
* Released 1.6.2 |
||||
|
||||
2008-06-25 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Added copyright messages to each source file. |
||||
* Escaped man-page dashes, since unescaped dashes are treated as |
||||
hyphens. |
||||
|
||||
2008-06-19 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Fixed missing '=' signs in the man-page. |
||||
|
||||
2008-05-14 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* If the mount source and destination directories are the same |
||||
then we no longer require that the directory be empty. |
||||
(-ononempty is added implicitly) |
||||
* Released 1.6.1 |
||||
|
||||
2008-05-10 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Added --create-with-perms. |
||||
* Added a little automated test suite. |
||||
* Moved the project to code.google.com. |
||||
* Released 1.6 |
||||
|
||||
2008-01-26 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Fixed an embarrassing segfault while parsing --mirror arguments. |
||||
Thanks to Stefan Kost for reporting it! |
||||
* Released 1.5 |
||||
|
||||
2007-12-31 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Made compatible with Mac OS X with the help of Theocharis Athanasakis. |
||||
* Released 1.4.2 |
||||
|
||||
2007-11-09 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Fixed a bug in userinfo.c that could prevent using numeric |
||||
user or group IDs when /etc/passwd or /etc/group have long records. |
||||
* Released 1.4.1 |
||||
|
||||
2007-10-31 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Applied another patch from Joel Daniels to fix a bug that |
||||
occurred when /etc/passwd or /etc/group had long records. |
||||
Thanks! |
||||
* Released 1.4 |
||||
|
||||
2007-07-17 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Renamed the new options added by Joel |
||||
to --create-for-user and --create-for-group. |
||||
* Made it an error to use --create-as-user as non-root. |
||||
* Released 1.3 |
||||
|
||||
2007-07-17 Joel Daniels <jdaniel4 at uiuc dot e d u> |
||||
|
||||
* Added the user_for_create and group_for_create options |
||||
|
||||
|
||||
2007-03-24 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Fixed minor errors in man-page. |
||||
* Released 1.2.1 |
||||
|
||||
2007-03-03 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Michael Roitzsch pointed out incorrect permission checks |
||||
for symlinks and fixed unlink() to correctly check for write permission |
||||
to the the directory (instead of the file). Thanks! |
||||
* Removed check_access() altogether. Will now rely on the kernel for |
||||
all permission checks by always enabling -o default_permissions. |
||||
* Released 1.2 |
||||
|
||||
|
||||
2007-01-14 Martin Pärtel <martin dot partel at gmail dot com> |
||||
|
||||
* Changed -o no_allow_others to -o no_allow_other. |
||||
* Added a way to specify group members in -m and -M. |
||||
* Released 1.1 |
@ -0,0 +1,6 @@
|
||||
# not a GNU package. You can remove this line, if
|
||||
# have all needed files, that a GNU package needs
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
SUBDIRS = src tests
|
||||
|
@ -0,0 +1,41 @@
|
||||
|
||||
bindfs - http://code.google.com/p/bindfs/ |
||||
|
||||
-- Overview -- |
||||
|
||||
bindfs is a FUSE filesystem for mirroring a directory to another |
||||
directory, similarly to mount --bind. The permissions of the mirrored |
||||
directory can be altered in various ways. |
||||
|
||||
Some things bindfs can be used for: |
||||
- Making a directory read-only. |
||||
- Making all executables non-executable. |
||||
- Sharing a directory with a list of users (or groups). |
||||
- Modifying permission bits using rules with chmod-like syntax. |
||||
- Changing the permissions with which files are created. |
||||
|
||||
Non-root users can use almost all features, but most interesting |
||||
use-cases need user_allow_other to be defined in /etc/fuse.conf |
||||
|
||||
|
||||
-- Installation -- |
||||
|
||||
Make sure fuse 2.5.3 or above is installed (http://fuse.sf.net/). |
||||
Then compile and install as usual: |
||||
./configure |
||||
make |
||||
make install |
||||
|
||||
If you want the mounts made by non-root users to be visible to other users, |
||||
you may have to add the line user_allow_other to /etc/fuse.conf. |
||||
|
||||
|
||||
-- Usage -- |
||||
|
||||
See the bindfs --help or the man-page for instructions and examples. |
||||
|
||||
|
||||
-- License -- |
||||
|
||||
GNU General Public License version 2 or any later version. |
||||
See the file COPYING. |
@ -0,0 +1,22 @@
|
||||
|
||||
Major (i.e. probably not very soon): |
||||
|
||||
- Applying options to a subset of all files; |
||||
something like --if-file-matches '*.md5' { -u checksummer -p u+rw } |
||||
- This would make for some new useful options like --hide or --deny |
||||
- We could also have special xattrs like 'bindfs:perms' that |
||||
don't get propagated to the base directory but control bindfs behaviour |
||||
instead. |
||||
- All this leads to the thought of an integrated minilanguage. |
||||
Taken to the extreme, would this make bindfs almost yet another scripting |
||||
language binding for FUSE? |
||||
- Stackable/pluggable scripts? Any benefit over a remount? |
||||
|
||||
Minor: |
||||
|
||||
- Decide what to do with the fuse options uid=N and gid=N, or at least |
||||
mention them in the docs. |
||||
|
||||
- Look at capabilities instead of uid==0 when checking for special privileges. |
||||
Do this in a portable way and fall back to uid==0 if not available. |
||||
|
@ -0,0 +1,57 @@
|
||||
#!/bin/bash |
||||
|
||||
# Enable environment variables to override tool commands. |
||||
: ${AUTOCONF=autoconf} |
||||
: ${AUTOHEADER=autoheader} |
||||
: ${AUTOMAKE=automake} |
||||
: ${ACLOCAL=aclocal} |
||||
: ${LIBTOOLIZE=libtoolize} |
||||
|
||||
# Apple calls the GNU libtoolize "glibtoolize" |
||||
if [[ ! -x `which "$LIBTOOLIZE"` ]]; then |
||||
LIBTOOLIZE=glibtoolize |
||||
fi |
||||
if [[ ! -x `which "$LIBTOOLIZE"` ]]; then |
||||
echo "Cannot find libtoolize" |
||||
exit 1 |
||||
fi |
||||
|
||||
# Add /usr/local/share/aclocal to aclocal's search path |
||||
if [[ -d /usr/local/share/aclocal ]]; then |
||||
ACLOCAL="$ACLOCAL -I /usr/local/share/aclocal" |
||||
fi |
||||
|
||||
rm -rf autom4te.cache |
||||
rm -f aclocal.m4 |
||||
rm -f missing mkinstalldirs depcomp install-sh libtool |
||||
|
||||
echo "Running $ACLOCAL..." |
||||
$ACLOCAL || exit 1 |
||||
|
||||
echo "Running $AUTOHEADER..." |
||||
$AUTOHEADER || exit 1 |
||||
|
||||
echo "Running $AUTOCONF..." |
||||
$AUTOCONF || exit 1 |
||||
|
||||
echo "Running $LIBTOOLIZE..." |
||||
$LIBTOOLIZE --automake --copy --force || exit 1 |
||||
|
||||
echo "Running $AUTOMAKE..." |
||||
$AUTOMAKE -a -c || exit 1 |
||||
|
||||
if [ "$1" == "-d" ]; then |
||||
echo "Running configure --enable-debug" |
||||
echo |
||||
sleep 1s |
||||
./configure --enable-debug |
||||
elif [ -n "$1" ]; then |
||||
echo |
||||
echo "./configure $@" |
||||
./configure $@ |
||||
else |
||||
echo |
||||
echo "autogen.sh completed successfully." |
||||
echo "Now run ./configure with the appropriate flags and then make." |
||||
fi |
||||
|
@ -0,0 +1,42 @@
|
||||
AC_INIT([bindfs],[1.9],[martin.partel@gmail.com]) |
||||
|
||||
AM_INIT_AUTOMAKE |
||||
AM_CONFIG_HEADER(config.h) |
||||
|
||||
AC_PROG_CC |
||||
AC_LANG(C) |
||||
AC_PROG_LIBTOOL |
||||
|
||||
# --enable and --with options |
||||
AC_ARG_ENABLE([debug], |
||||
[AS_HELP_STRING([--enable-debug], [enable extra debug output])]) |
||||
AC_ARG_WITH([core-foundation], |
||||
AS_HELP_STRING([--with-core-foundation], [link against Core Foundation (OS X only) @<:@default=no@:>@])) |
||||
|
||||
|
||||
if test x"$enable_debug" == "xyes" ; then |
||||
CFLAGS="${CFLAGS} -g -O0 -DMALLOC_CHECK_=2" |
||||
AC_DEFINE([BINDFS_DEBUG], [1], [Define to 1 to enable debugging messages]) |
||||
else |
||||
CFLAGS="${CFLAGS} -O2" |
||||
fi |
||||
|
||||
if test x"$with_core_foundation" == "xyes" ; then |
||||
AC_MSG_NOTICE([Linking with Core Foundation]) |
||||
LDFLAGS="${LDFLAGS} -framework CoreFoundation" |
||||
fi |
||||
|
||||
CFLAGS="${CFLAGS} -Wall -D_REENTRANT -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=25" |
||||
|
||||
# Check for xattrs |
||||
AC_CHECK_FUNCS([setxattr getxattr listxattr removexattr]) |
||||
AC_CHECK_FUNCS([lsetxattr lgetxattr llistxattr lremovexattr]) |
||||
|
||||
# Check for fuse |
||||
PKG_CHECK_MODULES([fuse], [fuse >= 2.5.3]) |
||||
|
||||
AC_CONFIG_FILES([Makefile \ |
||||
src/Makefile \ |
||||
tests/Makefile]) |
||||
AC_OUTPUT |
||||
|
@ -0,0 +1,11 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
bin_PROGRAMS = bindfs
|
||||
|
||||
noinst_HEADERS = debug.h permchain.h userinfo.h misc.h
|
||||
bindfs_SOURCES = bindfs.c permchain.c userinfo.c misc.c
|
||||
|
||||
AM_CFLAGS = $(fuse_CFLAGS)
|
||||
bindfs_LDADD = $(fuse_LIBS)
|
||||
|
||||
man_MANS = bindfs.1
|
@ -0,0 +1,338 @@
|
||||
.TH BINDFS 1 |
||||
|
||||
|
||||
.SH NAME |
||||
bindfs \(hy mount \-\-bind in user\-space |
||||
|
||||
|
||||
.SH SYNOPSIS |
||||
\fBbindfs\fP [\fIoptions\fP]\fI dir mountpoint |
||||
|
||||
|
||||
.SH DESCRIPTION |
||||
A FUSE filesystem for mirroring the contents of a directory to another |
||||
directory. Additionally, one can change the permissions |
||||
of files in the mirrored directory. |
||||
|
||||
|
||||
.SH OPTIONS |
||||
.TP |
||||
.B \-h, \-\-help |
||||
Displays a help message and exits. |
||||
|
||||
.TP |
||||
.B \-V, \-\-version |
||||
Displays version information and exits. |
||||
|
||||
.TP |
||||
.B \-u, \-\-user, \-\-owner=\fIuser\fP, \-o owner=... |
||||
Makes all files owned by the specified user. |
||||
Also causes chown on the mounted filesystem to always fail. |
||||
|
||||
.TP |
||||
.B \-g, \-\-group=\fIgroup\fP, \-o group=... |
||||
Makes all files owned by the specified group. |
||||
Also causes chgrp on the mounted filesystem to always fail. |
||||
|
||||
.TP |
||||
.B \-p, \-\-perms=\fIpermissions\fP, \-o perms=... |
||||
Takes a comma\- or colon\-separated list of chmod\-like permission |
||||
specifications to be applied to the permission bits in order. |
||||
See \fB\%PERMISSION \%SPECIFICATION\fP below for details. |
||||
|
||||
This only affects how the permission bits of existing files are altered |
||||
when shown in the mounted directory. You can use \-\-create\-with\-perms to |
||||
change the permissions that newly created files get in the source directory. |
||||
|
||||
Note that, as usual, the root user isn't bound by the permissions set here. |
||||
You can get a truly read-only mount by using \fB-r\fP. |
||||
|
||||
.TP |
||||
.B \-m, \-\-mirror=\fIusers\fP, \-o mirror=... |
||||
Takes a comma\- or colon\-separated list of users who will see themselves as |
||||
the owners of all files. Users who are not listed here will still be able |
||||
to access the mount if the permissions otherwise allow them to. |
||||
|
||||
You can also give a group name prefixed with an '@' to mirror all members of |
||||
a group. This will not change which group the files are shown to have. |
||||
|
||||
.TP |
||||
.B \-M, \-\-mirror\-only=\fIusers\fP, \-o mirror\-only=... |
||||
Like \fB\-\-mirror\fP but disallows access for all other users (except root). |
||||
|
||||
.TP |
||||
.B \-n, \-\-no\-allow\-other, \-o no\-allow\-other |
||||
Does not add \fB\-o allow_other\fP to FUSE options. |
||||
This causes the mount to be accessible only by the current user. |
||||
|
||||
|
||||
.SH FILE CREATION POLICY |
||||
New files and directories are created so they are owned by the mounter. |
||||
bindfs can let this happen (the default for normal users), |
||||
or it can try to change the owner to the uid/gid of the process that |
||||
wants to create the file (the default for root). It is also possible to |
||||
have bindfs try to change the owner to a particular user or group. |
||||
|
||||
.TP |
||||
.B \-\-create\-as\-user, \-o create\-as\-user |
||||
Tries to change the owner and group of new files and directories to the |
||||
uid and gid of the caller. This can work only if the mounter is root. |
||||
It is also the default behavior (mimicing mount \-\-bind) if the mounter is root. |
||||
|
||||
.TP |
||||
.B \-\-create\-as\-mounter, \-o create\-as\-mounter |
||||
All new files and directories will be owned by the mounter. |
||||
This is the default behavior for non\-root mounters. |
||||
|
||||
.TP |
||||
.B \-\-create\-for\-user=\fIuser\fP, \-o create\-for\-user=... |
||||
Tries to change the owner of new files and directories to the user |
||||
specified here. This can work only if the mounter is root. This |
||||
option overrides the \-\-create\-as\-user and \-\-create\-as\-mounter options. |
||||
|
||||
.TP |
||||
.B \-\-create\-for\-group=\fIgroup\fP, \-o create\-for\-group=... |
||||
Tries to change the owning group of new files and directories to the |
||||
group specified here. This can work only if the mounter is root. This |
||||
option overrides the \-\-create\-as\-user and \-\-create\-as\-mounter options. |
||||
|
||||
.TP |
||||
.B \-\-create\-with\-perms=\fIpermissions\fP, \-o create\-with\-perms=... |
||||
Works like \-\-perms but is applied to the permission bits of new files |
||||
get in the source directory. |
||||
Normally the permissions of new files depend on the creating process's |
||||
preferences and umask. |
||||
This option can be used to modify those permissions or override |
||||
them completely. |
||||
See \fB\%PERMISSION \%SPECIFICATION\fP below for details. |
||||
|
||||
|
||||
.SH CHOWN/CHGRP POLICY |
||||
The behaviour on chown/chgrp calls can be changed. By default they are passed |
||||
through to the source directory even if bindfs is set to show |
||||
a fake owner/group. A chown/chgrp call will only succeed if the user has |
||||
enough mirrored permissions to chmod the mirrored file AND |
||||
the mounter has enough permissions to chmod the real file. |
||||
|
||||
.TP |
||||
.B \-\-chown\-normal, \-o chown\-normal |
||||
Tries to chown the underlying file. This is the default. |
||||
|
||||
.TP |
||||
.B \-\-chown\-ignore, \-o chown\-ignore |
||||
Lets chown succeed (if the user has enough mirrored permissions) |
||||
but actually does nothing. A combined chown/chgrp is effectively turned |
||||
into a chgrp-only request. |
||||
|
||||
.TP |
||||
.B \-\-chown\-deny, \-o chown\-deny |
||||
Makes chown always fail with a 'permission denied' error. |
||||
A combined chown/chgrp request will fail as well. |
||||
|
||||
.TP |
||||
.B \-\-chgrp\-normal, \-o chgrp\-normal |
||||
Tries to chgrp the underlying file. This is the default. |
||||
|
||||
.TP |
||||
.B \-\-chgrp\-ignore, \-o chgrp\-ignore |
||||
Lets chgrp succeed (if the user has enough mirrored permissions) |
||||
but actually does nothing. A combined chown/chgrp is effectively turned into a |
||||
chown-only request. |
||||
|
||||
.TP |
||||
.B \-\-chgrp\-deny, \-o chgrp\-deny |
||||
Makes chgrp always fail with a 'permission denied' error. |
||||
A combined chown/chgrp request will fail as well. |
||||
|
||||
|
||||
.SH CHMOD POLICY |
||||
Chmod calls are forwarded to the source directory by default. |
||||
This may cause unexpected behaviour if bindfs is altering permission bits. |
||||
|
||||
.TP |
||||
.B \-\-chmod\-normal, \-o chmod\-normal |
||||
Tries to chmod the underlying file. This will succeed if the user has |
||||
the appropriate mirrored permissions to chmod the mirrored file AND |
||||
the mounter has enough permissions to chmod the real file. |
||||
This is the default (in order to behave like mount \-\-bind by default). |
||||
|
||||
.TP |
||||
.B \-\-chmod\-ignore, \-o chmod\-ignore |
||||
Lets chmod succeed (if the user has enough mirrored permissions) |
||||
but actually does nothing. |
||||
|
||||
.TP |
||||
.B \-\-chmod\-deny, \-o chmod\-deny |
||||
Makes chmod always fail with a 'permission denied' error. |
||||
|
||||
.TP |
||||
.B \-\-chmod\-allow\-x, \-o chmod\-allow\-x |
||||
Allows setting and clearing the executable attribute on files |
||||
(but not directories). When used with \-\-chmod\-ignore, |
||||
chmods will only affect execute bits on files and changes to other bits are |
||||
discarded. |
||||
With \-\-chmod\-deny, all chmods that would change any bits except |
||||
excecute bits on files will still fail with a 'permission denied'. |
||||
This option does nothing with \-\-chmod\-normal. |
||||
|
||||
|
||||
.SH XATTR POLICY |
||||
Extended attributes are mirrored by default, |
||||
though not all underlying file systems support xattrs. |
||||
|
||||
.TP |
||||
.B \-\-xattr\-none, \-o xattr\-none |
||||
Disable extended attributes altogether. All operations will |
||||
return 'Operation not supported'. |
||||
|
||||
.TP |
||||
.B \-\-xattr\-ro, \-o xattr\-ro |
||||
Let extended attributes be read\-only. |
||||
|
||||
.TP |
||||
.B \-\-xattr\-rw, \-o xattr\-rw |
||||
Let extended attributes be read\-write (the default). |
||||
The read/write permissions are checked against the (possibly modified) |
||||
file permissions inside the mount. |
||||
|
||||
|
||||
.SH TIME-RELATED OPTIONS |
||||
|
||||
Recall that a unix file has three standard timestamps: |
||||
\fBatime\fP (last access i.e. read time), |
||||
\fBmtime\fP (last content modification time) |
||||
\fBctime\fP (last content or metadata (inode) change time) |
||||
|
||||
It may sometimes be useful to alter these timestamps, but care should be taken |
||||
not to cause programs (e.g. backup jobs) to miss important changes. |
||||
|
||||
.TP |
||||
.B \-\-ctime\-from-mtime, \-o ctime\-from\-mtime |
||||
Reads the ctime of each file and directory from its mtime. |
||||
In other words, only content modifications (as opposed to metadata changes) |
||||
will be reflected in a mirrored file's ctime. |
||||
(The underlying file's ctime will still be updated normally.) |
||||
|
||||
|
||||
.SH FUSE OPTIONS |
||||
.TP |
||||
.B \-o \fIoptions |
||||
Fuse options. |
||||
|
||||
.TP |
||||
.B \-r, \-o ro |
||||
Make the mount strictly read-only. |
||||
This even prevents root from writing to it. |
||||
If this is all you need, then (since Linux 2.6.26) you can get a |
||||
more efficent mount with \fBmount \-\-bind\fP and then \fBmount \-o remount,ro\fP. |
||||
|
||||
.TP |
||||
.B \-d, \-o debug |
||||
Enable debug output (implies \-f). |
||||
|
||||
.TP |
||||
.B \-f |
||||
Foreground operation. |
||||
|
||||
.TP |
||||
.B \-s |
||||
Disable multithreaded operation. bindfs should be thread-safe. |
||||
|
||||
|
||||
.SH PERMISSION SPECIFICATION |
||||
The \fB\-p\fP option takes a comma\- or colon\-separated list of either octal |
||||
numeric permission bits or symbolic representations of permission bit |
||||
operations. |
||||
The symbolic representation is based on that of the \fBchmod\fP(1) command. |
||||
setuid, setgid and sticky bits are ignored. |
||||
|
||||
This program extends the chmod symbolic representation with the following |
||||
operands: |
||||
|
||||
`\fBD\fP' (right hand side) |
||||
Works like \fBX\fP but applies only to directories (not to executables). |
||||
|
||||
`\fBd\fP' and `\fBf\fP' (left hand side) |
||||
Makes this directive only apply to directories (d) or files (f). |
||||
e.g. \fBgd\-w\fP would remove the group write bit from all directories. |
||||
|
||||
`\fBu\fP', `\fBg\fP', `\fBo\fP' (right hand side) |
||||
Uses the user (u), group (g) or others (o) permission bits of |
||||
the original file. |
||||
e.g. \fBg=u\fP would copy the user's permission bits to the group. |
||||
\fBug+o\fP would add the others' permissions to the owner and group. |
||||
|
||||
|
||||
.I Examples |
||||
.TP |
||||
.B o\-rwx |
||||
Removes all permission bits from others. |
||||
|
||||
.TP |
||||
.B g=rD |
||||
Allows group to read all files and enter all directories, but nothing else. |
||||
|
||||
.TP |
||||
.B 0644,a+X |
||||
Sets permission bits to 0644 and adds the execute bit for everyone |
||||
to all directories and executables. |
||||
|
||||
.TP |
||||
.B og\-x:og+rD:u=rwX:g+rw |
||||
Removes execute bit for others and group, |
||||
adds read and directory execute for others and group, |
||||
sets user permissions to read, write and execute directory/executable, |
||||
adds read and write for group. |
||||
|
||||
|
||||
.SH EXAMPLES |
||||
.BR |
||||
.TP |
||||
.B bindfs \-u www \-g nogroup \-p 0000,u=rD ~/mywebsite ~/public_html/mysite |
||||
|
||||
Publishes a website in public_html so that only the 'www' user can |
||||
read the site. |
||||
|
||||
.TP |
||||
.B bindfs \-M foo,bar,1007,@mygroup \-p 0600,u+X dir mnt |
||||
|
||||
Gives access to 'foo', 'bar', the user with the UID 1007 as well as |
||||
everyone in the group 'mygroup'. Sets the permission bits to 0600, |
||||
thus giving the specified users read/write access, |
||||
and adds the user execute bit for directories and executables. |
||||
|
||||
.TP |
||||
.B bindfs \-ono\-allow\-other,perms=a\-w somedir somedir |
||||
|
||||
Makes a directory read\-only and accessable only by the current user. |
||||
|
||||
.TP |
||||
.B bindfs#/home/bob/shared /var/www/shared/bob fuse perms=0000:u+rD 0 0 |
||||
|
||||
An example \fI/etc/fstab\fP entry. Note that the colon must be used to |
||||
separate arguments to perms, because the comma is an option separator in |
||||
\fI/etc/fstab\fP. |
||||
|
||||
|
||||
.SH NOTES |
||||
|
||||
Setuid and setgid bits have no effect inside the mount. |
||||
This is a necessary security feature of FUSE. |
||||
|
||||
MacFuse caches file contents by default. |
||||
This means that changes in source files are not always immediately visible under the mount point. |
||||
\fB\-o nolocalcaches\fP can be used to disable the cache. |
||||
|
||||
.SH BUGS |
||||
|
||||
Please report to the issue tracker on the project home page at |
||||
http://code.google.com/p/bindfs/ |
||||
|
||||
|
||||
.SH AUTHOR |
||||
Martin P\[:a]rtel <martin dot partel at gmail dot com> |
||||
|
||||
|
||||
.SH SEE ALSO |
||||
\fBchmod\fP(1), \fBfusermount\fP(1) |
||||
|
@ -0,0 +1,1329 @@
|
||||
/*
|
||||
Copyright 2006,2007,2008,2009,2010 Martin Pärtel <martin.partel@gmail.com> |
||||
|
||||
This file is part of bindfs. |
||||
|
||||
bindfs is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 2 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
bindfs is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with bindfs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
This file is based on fusexmp_fh.c from FUSE 2.5.3, |
||||
which had the following notice: |
||||
--- |
||||
FUSE: Filesystem in Userspace |
||||
Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu> |
||||
|
||||
This program can be distributed under the terms of the GNU GPL. |
||||
See the file COPYING. |
||||
--- |
||||
|
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
/* For pread/pwrite */ |
||||
#define _XOPEN_SOURCE 500 |
||||
|
||||
#include <stdlib.h> |
||||
#include <stddef.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <ctype.h> |
||||
#ifdef HAVE_SYS_TYPES_H |
||||
#include <sys/types.h> |
||||
#endif |
||||
#ifdef HAVE_SYS_STAT_H |
||||
#include <sys/stat.h> |
||||
#endif |
||||
#include <sys/statvfs.h> |
||||
#include <unistd.h> |
||||
#include <fcntl.h> |
||||
#include <dirent.h> |
||||
#include <errno.h> |
||||
#include <getopt.h> |
||||
#include <assert.h> |
||||
#include <pwd.h> |
||||
#include <grp.h> |
||||
#ifdef HAVE_SETXATTR |
||||
#include <sys/xattr.h> |
||||
#endif |
||||
|
||||
#include <fuse.h> |
||||
#include <fuse_opt.h> |
||||
|
||||
#include "debug.h" |
||||
#include "permchain.h" |
||||
#include "userinfo.h" |
||||
#include "misc.h" |
||||
|
||||
/* SETTINGS */ |
||||
static struct settings { |
||||
const char *progname; |
||||
struct permchain *permchain; /* permission bit rules. see permchain.h */ |
||||
uid_t new_uid; /* user-specified uid */ |
||||
gid_t new_gid; /* user-specified gid */ |
||||
uid_t create_for_uid; |
||||
gid_t create_for_gid; |
||||
const char *mntsrc; |
||||
const char *mntdest; |
||||
int mntsrc_fd; |
||||
|
||||
enum CreatePolicy { |
||||
CREATE_AS_USER, |
||||
CREATE_AS_MOUNTER |
||||
} create_policy; |
||||
|
||||
struct permchain *create_permchain; /* the --create-with-perms option */ |
||||
|
||||
enum ChownPolicy { |
||||
CHOWN_NORMAL, |
||||
CHOWN_IGNORE, |
||||
CHOWN_DENY |
||||
} chown_policy; |
||||
|
||||
enum ChgrpPolicy { |
||||
CHGRP_NORMAL, |
||||
CHGRP_IGNORE, |
||||
CHGRP_DENY |
||||
} chgrp_policy; |
||||
|
||||
enum ChmodPolicy { |
||||
CHMOD_NORMAL, |
||||
CHMOD_IGNORE, |
||||
CHMOD_DENY |
||||
} chmod_policy; |
||||
|
||||
int chmod_allow_x; |
||||
|
||||
enum XAttrPolicy { |
||||
XATTR_UNIMPLEMENTED, |
||||
XATTR_READ_ONLY, |
||||
XATTR_READ_WRITE |
||||
} xattr_policy; |
||||
|
||||
int mirrored_users_only; |
||||
uid_t* mirrored_users; |
||||
int num_mirrored_users; |
||||
gid_t *mirrored_members; |
||||
int num_mirrored_members; |
||||
|
||||
int ctime_from_mtime; |
||||
} settings; |
||||
|
||||
|
||||
|
||||
/* PROTOTYPES */ |
||||
|
||||
static int is_mirroring_enabled(); |
||||
|
||||
/* Checks whether the uid is to be the mirrored owner of all files. */ |
||||
static int is_mirrored_user(uid_t uid); |
||||
|
||||
/* Processes the virtual path to a real path. Don't free() the result. */ |
||||
static const char *process_path(const char *path); |
||||
|
||||
/* The common parts of getattr and fgetattr */ |
||||
static int getattr_common(const char *path, struct stat *stbuf); |
||||
|
||||
|
||||
/* FUSE callbacks */ |
||||
static void *bindfs_init(); |
||||
static void bindfs_destroy(void *private_data); |
||||
static int bindfs_getattr(const char *path, struct stat *stbuf); |
||||
static int bindfs_fgetattr(const char *path, struct stat *stbuf, |
||||
struct fuse_file_info *fi); |
||||
static int bindfs_readlink(const char *path, char *buf, size_t size); |
||||
static int bindfs_opendir(const char *path, struct fuse_file_info *fi); |
||||
static inline DIR *get_dirp(struct fuse_file_info *fi); |
||||
static int bindfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, |
||||
off_t offset, struct fuse_file_info *fi); |
||||
static int bindfs_releasedir(const char *path, struct fuse_file_info *fi); |
||||
static int bindfs_mknod(const char *path, mode_t mode, dev_t rdev); |
||||
static int bindfs_mkdir(const char *path, mode_t mode); |
||||
static int bindfs_unlink(const char *path); |
||||
static int bindfs_rmdir(const char *path); |
||||
static int bindfs_symlink(const char *from, const char *to); |
||||
static int bindfs_rename(const char *from, const char *to); |
||||
static int bindfs_link(const char *from, const char *to); |
||||
static int bindfs_chmod(const char *path, mode_t mode); |
||||
static int bindfs_chown(const char *path, uid_t uid, gid_t gid); |
||||
static int bindfs_truncate(const char *path, off_t size); |
||||
static int bindfs_ftruncate(const char *path, off_t size, |
||||
struct fuse_file_info *fi); |
||||
static int bindfs_utime(const char *path, struct utimbuf *buf); |
||||
static int bindfs_create(const char *path, mode_t mode, struct fuse_file_info *fi); |
||||
static int bindfs_open(const char *path, struct fuse_file_info *fi); |
||||
static int bindfs_read(const char *path, char *buf, size_t size, off_t offset, |
||||
struct fuse_file_info *fi); |
||||
static int bindfs_write(const char *path, const char *buf, size_t size, |
||||
off_t offset, struct fuse_file_info *fi); |
||||
static int bindfs_statfs(const char *path, struct statvfs *stbuf); |
||||
static int bindfs_release(const char *path, struct fuse_file_info *fi); |
||||
static int bindfs_fsync(const char *path, int isdatasync, |
||||
struct fuse_file_info *fi); |
||||
|
||||
|
||||
static void print_usage(const char *progname); |
||||
static void atexit_func(); |
||||
static int process_option(void *data, const char *arg, int key, |
||||
struct fuse_args *outargs); |
||||
|
||||
static int is_mirroring_enabled() |
||||
{ |
||||
return settings.num_mirrored_users + settings.num_mirrored_members > 0; |
||||
} |
||||
|
||||
static int is_mirrored_user(uid_t uid) |
||||
{ |
||||
int i; |
||||
for (i = 0; i < settings.num_mirrored_users; ++i) { |
||||
if (settings.mirrored_users[i] == uid) |
||||
break; |
||||
} |
||||
if (i < settings.num_mirrored_users) { /* found in mirrored_users */ |
||||
return 1; |
||||
} else { |
||||
for (i = 0; i < settings.num_mirrored_members; ++i) { |
||||
if (user_belongs_to_group(uid, settings.mirrored_members[i])) |
||||
break; |
||||
} |
||||
if (i < settings.num_mirrored_members) /* found in mirrored_members */ |
||||
return 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
|
||||
static const char *process_path(const char *path) |
||||
{ |
||||
if (path == NULL) /* possible? */ |
||||
return NULL; |
||||
|
||||
while (*path == '/') |
||||
++path; |
||||
|
||||
if (*path == '\0') |
||||
return "."; |
||||
else |
||||
return path; |
||||
} |
||||
|
||||
static int getattr_common(const char *procpath, struct stat *stbuf) |
||||
{ |
||||
struct fuse_context *fc = fuse_get_context(); |
||||
|
||||
/* Copy mtime (file content modification time)
|
||||
to ctime (inode/status change time) |
||||
if the user asked for that */ |
||||
if (settings.ctime_from_mtime) |
||||
stbuf->st_ctime = stbuf->st_mtime; |
||||
|
||||
/* Report user-defined owner/group if specified */ |
||||
if (settings.new_uid != -1) |
||||
stbuf->st_uid = settings.new_uid; |
||||
if (settings.new_gid != -1) |
||||
stbuf->st_gid = settings.new_gid; |
||||
|
||||
/* Mirrored user? */ |
||||
if (is_mirroring_enabled() && is_mirrored_user(fc->uid)) { |
||||
stbuf->st_uid = fc->uid; |
||||
} else if (settings.mirrored_users_only && fc->uid != 0) { |
||||
stbuf->st_mode &= ~0777; /* Deny all access if mirror-only and not root */ |
||||
return 0; |
||||
} |
||||
|
||||
if ((stbuf->st_mode & S_IFLNK) == S_IFLNK) |
||||
return 0; /* don't bother with symlink permissions -- they don't matter */ |
||||
|
||||
/* Apply user-defined permission bit modifications */ |
||||
stbuf->st_mode = permchain_apply(settings.permchain, stbuf->st_mode); |
||||
|
||||
/* Check that we can really do what we promise */ |
||||
if (access(procpath, R_OK) == -1) |
||||
stbuf->st_mode &= ~0444; |
||||
if (access(procpath, W_OK) == -1) |
||||
stbuf->st_mode &= ~0222; |
||||
if (access(procpath, X_OK) == -1) |
||||
stbuf->st_mode &= ~0111; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void *bindfs_init() |
||||
{ |
||||
assert(settings.permchain != NULL); |
||||
assert(settings.mntsrc_fd > 0); |
||||
|
||||
if (fchdir(settings.mntsrc_fd) != 0) { |
||||
fprintf( |
||||
stderr, |
||||
"Could not change working directory to '%s': %s\n", |
||||
settings.mntsrc, |
||||
strerror(errno) |
||||
); |
||||
fuse_exit(fuse_get_context()->fuse); |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
static void bindfs_destroy(void *private_data) |
||||
{ |
||||
} |
||||
|
||||
static int bindfs_getattr(const char *path, struct stat *stbuf) |
||||
{ |
||||
path = process_path(path); |
||||
|
||||
if (lstat(path, stbuf) == -1) |
||||
return -errno; |
||||
return getattr_common(path, stbuf); |
||||
} |
||||
|
||||
static int bindfs_fgetattr(const char *path, struct stat *stbuf, |
||||
struct fuse_file_info *fi) |
||||
{ |
||||
path = process_path(path); |
||||
|
||||
if (fstat(fi->fh, stbuf) == -1) |
||||
return -errno; |
||||
return getattr_common(path, stbuf); |
||||
} |
||||
|
||||
static int bindfs_readlink(const char *path, char *buf, size_t size) |
||||
{ |
||||
int res; |
||||
|
||||
path = process_path(path); |
||||
|
||||
/* No need to check for access to the link itself, since symlink
|
||||
permissions don't matter. Access to the path components of the symlink |
||||
are automatically queried by FUSE. */ |
||||
|
||||
res = readlink(path, buf, size - 1); |
||||
if (res == -1) |
||||
return -errno; |
||||
|
||||
buf[res] = '\0'; |
||||
return 0; |
||||
} |
||||
|
||||
static int bindfs_opendir(const char *path, struct fuse_file_info *fi) |
||||
{ |
||||
DIR *dp; |
||||
|
||||
path = process_path(path); |
||||
|
||||
dp = opendir(path); |
||||
if (dp == NULL) |
||||
return -errno; |
||||
|
||||
fi->fh = (unsigned long) dp; |
||||
return 0; |
||||
} |
||||
|
||||
static inline DIR *get_dirp(struct fuse_file_info *fi) |
||||
{ |
||||
return (DIR *) (uintptr_t) fi->fh; |
||||
} |
||||
|
||||
static int bindfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, |
||||
off_t offset, struct fuse_file_info *fi) |
||||
{ |
||||
DIR *dp = get_dirp(fi); |
||||
struct dirent *de; |
||||
|
||||
(void) path; |
||||
seekdir(dp, offset); |
||||
while ((de = readdir(dp)) != NULL) { |
||||
struct stat st; |
||||
memset(&st, 0, sizeof(st)); |
||||
st.st_ino = de->d_ino; |
||||
st.st_mode = de->d_type << 12; |
||||
if (filler(buf, de->d_name, &st, telldir(dp))) |
||||
break; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bindfs_releasedir(const char *path, struct fuse_file_info *fi) |
||||
{ |
||||
DIR *dp = get_dirp(fi); |
||||
(void) path; |
||||
closedir(dp); |
||||
return 0; |
||||
} |
||||
|
||||
static int bindfs_mknod(const char *path, mode_t mode, dev_t rdev) |
||||
{ |
||||
int res; |
||||
struct fuse_context *fc; |
||||
uid_t file_owner = -1; |
||||
gid_t file_group = -1; |
||||
|
||||
path = process_path(path); |
||||
|
||||
mode = permchain_apply(settings.create_permchain, mode); |
||||
|
||||
if (S_ISFIFO(mode)) |
||||
res = mkfifo(path, mode); |
||||
else |
||||
res = mknod(path, mode, rdev); |
||||
if (res == -1) |
||||
return -errno; |
||||
|
||||
if (settings.create_policy == CREATE_AS_USER) { |
||||
fc = fuse_get_context(); |
||||
file_owner = fc->uid; |
||||
file_group = fc->gid; |
||||
} |
||||
|
||||
if (settings.create_for_uid != -1) |
||||
file_owner = settings.create_for_uid; |
||||
if (settings.create_for_gid != -1) |
||||
file_group = settings.create_for_gid; |
||||
|
||||
if ((file_owner != -1) || (file_group != -1)) { |
||||
if (chown(path, file_owner, file_group) == -1) { |
||||
DPRINTF("Failed to chown new device node (%d)", errno); |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bindfs_mkdir(const char *path, mode_t mode) |
||||
{ |
||||
int res; |
||||
struct fuse_context *fc; |
||||
uid_t file_owner = -1; |
||||
gid_t file_group = -1; |
||||
|
||||
path = process_path(path); |
||||
|
||||
mode |= S_IFDIR; /* tell permchain_apply this is a directory */ |
||||
mode = permchain_apply(settings.create_permchain, mode); |
||||
|
||||
res = mkdir(path, mode & 0777); |
||||
if (res == -1) |
||||
return -errno; |
||||
|
||||
if (settings.create_policy == CREATE_AS_USER) { |
||||
fc = fuse_get_context(); |
||||
file_owner = fc->uid; |
||||
file_group = fc->gid; |
||||
} |
||||
|
||||
if (settings.create_for_uid != -1) |
||||
file_owner = settings.create_for_uid; |
||||
if (settings.create_for_gid != -1) |
||||
file_group = settings.create_for_gid; |
||||
|
||||
if ((file_owner != -1) || (file_group != -1)) { |
||||
if (chown(path, file_owner, file_group) == -1) { |
||||
DPRINTF("Failed to chown new directory (%d)", errno); |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bindfs_unlink(const char *path) |
||||
{ |
||||
int res; |
||||
|
||||
path = process_path(path); |
||||
|
||||
res = unlink(path); |
||||
if (res == -1) |
||||
return -errno; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bindfs_rmdir(const char *path) |
||||
{ |
||||
int res; |
||||
|
||||
path = process_path(path); |
||||
|
||||
res = rmdir(path); |
||||
if (res == -1) |
||||
return -errno; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bindfs_symlink(const char *from, const char *to) |
||||
{ |
||||
int res; |
||||
struct fuse_context *fc; |
||||
uid_t file_owner = -1; |
||||
gid_t file_group = -1; |
||||
|
||||
to = process_path(to); |
||||
|
||||
res = symlink(from, to); |
||||
if (res == -1) |
||||
return -errno; |
||||
|
||||
if (settings.create_policy == CREATE_AS_USER) { |
||||
fc = fuse_get_context(); |
||||
file_owner = fc->uid; |
||||
file_group = fc->gid; |
||||
} |
||||
|
||||
if (settings.create_for_uid != -1) |
||||
file_owner = settings.create_for_uid; |
||||
if (settings.create_for_gid != -1) |
||||
file_group = settings.create_for_gid; |
||||
|
||||
if ((file_owner != -1) || (file_group != -1)) { |
||||
if (lchown(to, file_owner, file_group) == -1) { |
||||
DPRINTF("Failed to lchown new symlink (%d)", errno); |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bindfs_rename(const char *from, const char *to) |
||||
{ |
||||
int res; |
||||
|
||||
from = process_path(from); |
||||
to = process_path(to); |
||||
|
||||
res = rename(from, to); |
||||
if (res == -1) |
||||
return -errno; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bindfs_link(const char *from, const char *to) |
||||
{ |
||||
int res; |
||||
|
||||
from = process_path(from); |
||||
to = process_path(to); |
||||
|
||||
res = link(from, to); |
||||
if (res == -1) |
||||
return -errno; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int bindfs_chmod(const char *path, |