Hello community,

here is the log from the commit of package bindfs for openSUSE:Factory checked 
in at 2015-11-15 12:46:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/bindfs (Old)
 and      /work/SRC/openSUSE:Factory/.bindfs.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "bindfs"

Changes:
--------
--- /work/SRC/openSUSE:Factory/bindfs/bindfs.changes    2015-09-27 
08:39:47.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.bindfs.new/bindfs.changes       2015-11-15 
12:48:03.000000000 +0100
@@ -1,0 +2,9 @@
+Fri Nov 13 16:07:59 UTC 2015 - jeng...@inai.de
+
+- Update to new upstream release 1.13.0
+* Implemented --resolved-symlink-deletion and added tests.
+* Improved and documented --resolve_symlinks in some edge cases.
+* Better handle symlinks with destructive system calls.
+* Add functionality to transparently resolve symbolic links.
+
+-------------------------------------------------------------------

Old:
----
  bindfs-1.12.7.tar.gz

New:
----
  bindfs-1.13.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ bindfs.spec ++++++
--- /var/tmp/diff_new_pack.o8eDeT/_old  2015-11-15 12:48:04.000000000 +0100
+++ /var/tmp/diff_new_pack.o8eDeT/_new  2015-11-15 12:48:04.000000000 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           bindfs
-Version:        1.12.7
+Version:        1.13.0
 Release:        0
 Summary:        Mount Directories to other Locations and alter Permission Bits
 License:        GPL-2.0+

++++++ bindfs-1.12.7.tar.gz -> bindfs-1.13.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bindfs-1.12.7/ChangeLog new/bindfs-1.13.0/ChangeLog
--- old/bindfs-1.12.7/ChangeLog 2015-09-09 11:40:21.000000000 +0200
+++ new/bindfs-1.13.0/ChangeLog 2015-09-27 00:11:40.000000000 +0200
@@ -1,3 +1,9 @@
+2015-09-26  Martin Pärtel <martin dot partel at gmail dot com>
+
+       * Merged --resolve-symlinks by hstern@ (#23). Thanks!
+       * Implemented --resolved-symlink-deletion.
+       * Released 1.13.0
+
 2015-09-09  Martin Pärtel <martin dot partel at gmail dot com>
 
        * OS X xattr fixes prompted, assisted and tested by @retrography (#21).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bindfs-1.12.7/configure new/bindfs-1.13.0/configure
--- old/bindfs-1.12.7/configure 2015-09-09 11:40:25.000000000 +0200
+++ new/bindfs-1.13.0/configure 2015-09-27 00:11:42.000000000 +0200
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for bindfs 1.12.7.
+# Generated by GNU Autoconf 2.69 for bindfs 1.13.0.
 #
 # Report bugs to <martin.par...@gmail.com>.
 #
@@ -590,8 +590,8 @@
 # Identity of this package.
 PACKAGE_NAME='bindfs'
 PACKAGE_TARNAME='bindfs'
-PACKAGE_VERSION='1.12.7'
-PACKAGE_STRING='bindfs 1.12.7'
+PACKAGE_VERSION='1.13.0'
+PACKAGE_STRING='bindfs 1.13.0'
 PACKAGE_BUGREPORT='martin.par...@gmail.com'
 PACKAGE_URL=''
 
@@ -1327,7 +1327,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures bindfs 1.12.7 to adapt to many kinds of systems.
+\`configure' configures bindfs 1.13.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1397,7 +1397,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of bindfs 1.12.7:";;
+     short | recursive ) echo "Configuration of bindfs 1.13.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1513,7 +1513,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-bindfs configure 1.12.7
+bindfs configure 1.13.0
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1791,7 +1791,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by bindfs $as_me 1.12.7, which was
+It was created by bindfs $as_me 1.13.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2655,7 +2655,7 @@
 
 # Define the identity of the package.
  PACKAGE='bindfs'
- VERSION='1.12.7'
+ VERSION='1.13.0'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -12253,7 +12253,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by bindfs $as_me 1.12.7, which was
+This file was extended by bindfs $as_me 1.13.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -12319,7 +12319,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; 
s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-bindfs config.status 1.12.7
+bindfs config.status 1.13.0
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bindfs-1.12.7/configure.ac 
new/bindfs-1.13.0/configure.ac
--- old/bindfs-1.12.7/configure.ac      2015-09-09 11:40:21.000000000 +0200
+++ new/bindfs-1.13.0/configure.ac      2015-09-27 00:11:40.000000000 +0200
@@ -1,4 +1,4 @@
-AC_INIT([bindfs],[1.12.7],[martin.par...@gmail.com])
+AC_INIT([bindfs],[1.13.0],[martin.par...@gmail.com])
 
 AM_INIT_AUTOMAKE([foreign])
 AC_CONFIG_HEADERS([config.h])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bindfs-1.12.7/src/bindfs.1 
new/bindfs-1.13.0/src/bindfs.1
--- old/bindfs-1.12.7/src/bindfs.1      2015-09-09 11:40:21.000000000 +0200
+++ new/bindfs-1.13.0/src/bindfs.1      2015-09-27 00:11:40.000000000 +0200
@@ -168,7 +168,7 @@
 Makes chmod always fail with a 'permission denied' error.
 
 .TP
-.B \-\-chmod\-filter=\fIpermissions\fP,, \-o chmod\-filter=...
+.B \-\-chmod\-filter=\fIpermissions\fP, \-o chmod\-filter=...
 Changes the permission bits of a chmod request before it is applied to the
 original file. Accepts the same permission syntax as \-\-perms.
 See \fB\%PERMISSION \%SPECIFICATION\fP below for details.
@@ -221,6 +221,48 @@
 .B \-\-write\-rate=\fIN\fP, \-o write\-rate=\fIN\fP
 Same as above, but for writes.
 
+.SH LINK HANDLING
+
+.TP
+.B \-\-hide\-hard\-links, \-o hide\-hard\-links
+Shows the hard link count of all files as 1.
+
+.TP
+.B \-\-resolve\-symlinks, \-o resolve-symlinks
+Transparently resolves symbolic links.  Disables creation of new symbolic
+links.
+
+With the following exceptions, operations will operate directly on the target
+file instead of the symlink. Renaming/moving a resolved symlink (inside the 
same
+mount point) will move the symlink instead of the underlying file. Deleting a
+resolved symlink will delete the underlying symlink but not the destination
+file. This can be configured with \fB\-\-resolved-symlink-deletion\fP.
+
+Note that when some programs, such as \fBvim\fP, save files, they actually move
+the old file out of the way, create a new file in its place, and finally delete
+the old file. Doing these operations on a resolved symlink will replace it with
+a regular file.
+
+Symlinks pointing outside the source directory are supported with the following
+exception: accessing the mountpoint recursively through a resolved symlink is
+not supported and will return an error. This is because a FUSE filesystem 
cannot
+reliably call itself recursively without deadlocking, especially in
+single-threaded mode.
+
+.TP
+.B \-\-resolved\-symlink\-deletion=\fIpolicy\fP, \-o 
resolved\-symlink\-deletion=\fIpolicy\fP
+If \fB\-\-resolve\-symlinks\fP is enabled, decides what happens when a resolved
+symlink is deleted.  The options are: \fBdeny\fP (resolved symlinks cannot be
+deleted), \fBsymlink-only\fP (the underlying symlink is deleted, its target is
+not), \fBsymlink-first\fP (the symlink is deleted, and if that succeeds,
+the target is deleted but no error is reported if that fails) or
+\fBtarget-first\fP (the target is deleted first, and the symlink is deleted
+only if deleting the target succeeded).  The default is \fBsymlink-only\fP.
+
+Note that deleting files inside symlinked directories is always possible with
+all settings, including \fBdeny\fP, unless something else protects those files.
+
+
 .SH MISCELLANEOUS OPTIONS
 
 .TP
@@ -259,10 +301,6 @@
 The underlying file's ctime will still be updated normally.
 
 .TP
-.B \-\-hide-hard-links, \-o hide-hard-links
-Shows the hard link count of all files as 1.
-
-.TP
 .B \-\-multithreaded, \-o multithreaded
 Run bindfs in multithreaded mode. While bindfs is designed to be
 otherwise thread-safe, there is currently a race condition that may pose
@@ -402,6 +440,10 @@
 and a similar rate of requests are treated fairly as long as the kernel orders
 their requests fairly.
 
+Some features relying on xattrs might not work properly on OS X
+(\fBhttps://github.com/mpartel/bindfs/issues/21\fP).
+For instance, Finder tags seem to work but comments might not.
+
 Please report bugs and/or send pull requests to
 \fBhttps://github.com/mpartel/bindfs/issues\fP.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bindfs-1.12.7/src/bindfs.c 
new/bindfs-1.13.0/src/bindfs.c
--- old/bindfs-1.12.7/src/bindfs.c      2015-09-09 11:40:21.000000000 +0200
+++ new/bindfs-1.13.0/src/bindfs.c      2015-09-27 00:11:40.000000000 +0200
@@ -40,6 +40,7 @@
 #include <stdlib.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <stdbool.h>
 #include <string.h>
 #include <ctype.h>
 #ifdef HAVE_SYS_TYPES_H
@@ -92,18 +93,19 @@
     gid_t new_gid; /* user-specified gid */
     uid_t create_for_uid;
     gid_t create_for_gid;
-    const char *mntsrc;
-    const char *mntdest;
+    char *mntsrc;
+    char *mntdest;
+    int mntdest_len; /* caches strlen(mntdest) */
     int mntsrc_fd;
 
-    char* original_working_dir;
+    char *original_working_dir;
     mode_t original_umask;
 
-    UserMap* usermap; /* From the --map option. */
-    UserMap* usermap_reverse;
+    UserMap *usermap; /* From the --map option. */
+    UserMap *usermap_reverse;
 
-    RateLimiter* read_limiter;
-    RateLimiter* write_limiter;
+    RateLimiter *read_limiter;
+    RateLimiter *write_limiter;
 
     enum CreatePolicy {
         CREATE_AS_USER,
@@ -141,15 +143,24 @@
     } xattr_policy;
 
     int mirrored_users_only;
-    uid_t* mirrored_users;
+    uid_t *mirrored_users;
     int num_mirrored_users;
     gid_t *mirrored_members;
     int num_mirrored_members;
 
+    int hide_hard_links;
+    int resolve_symlinks;
+
+    enum ResolvedSymlinkDeletion {
+        RESOLVED_SYMLINK_DELETION_DENY,
+        RESOLVED_SYMLINK_DELETION_SYMLINK_ONLY,
+        RESOLVED_SYMLINK_DELETION_SYMLINK_FIRST,
+        RESOLVED_SYMLINK_DELETION_TARGET_FIRST
+    } resolved_symlink_deletion_policy;
+
     int realistic_permissions;
 
     int ctime_from_mtime;
-    int hide_hard_links;
 
 } settings;
 
@@ -162,8 +173,8 @@
 /* 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);
+/* Processes the virtual path to a real path. Always free() the result. */
+static char *process_path(const char *path, bool resolve_symlinks);
 
 /* The common parts of getattr and fgetattr. */
 static int getattr_common(const char *path, struct stat *stbuf);
@@ -171,6 +182,9 @@
 /* Chowns a new file if necessary. */
 static void chown_new_file(const char *path, struct fuse_context *fc, int 
(*chown_func)(const char*, uid_t, gid_t));
 
+/* Unified implementation of unlink and rmdir. */
+static int delete_file(const char *path, int (*target_delete_func)(const char 
*));
+
 /* FUSE callbacks */
 static void *bindfs_init();
 static void bindfs_destroy(void *private_data);
@@ -245,18 +259,39 @@
 }
 
 
-static const char *process_path(const char *path)
+static char *process_path(const char *path, bool resolve_symlinks)
 {
-    if (path == NULL) /* possible? */
+    if (path == NULL) { /* possible? */
+        errno = EINVAL;
         return NULL;
+    }
 
     while (*path == '/')
         ++path;
 
     if (*path == '\0')
-        return ".";
-    else
-        return path;
+        path = ".";
+
+    if (resolve_symlinks && settings.resolve_symlinks) {
+        char* result = realpath(path, NULL);
+        if (result == NULL) {
+            if (errno == ENOENT) {
+                /* Broken symlink (or missing file). Don't return null because
+                   we want to be able to operate on broken symlinks. */
+                return strdup(path);
+            }
+        } else if (strncmp(result, settings.mntdest, settings.mntdest_len) == 
0) {
+            /* Recursive call. We cannot handle this without deadlocking,
+               especially in single-threaded mode. */
+            DPRINTF("Denying recursive access to mountpoint `%s'", result);
+            free(result);
+            errno = EPERM;
+            return NULL;
+        }
+        return result;
+    } else {
+        return strdup(path);
+    }
 }
 
 static int getattr_common(const char *procpath, struct stat *stbuf)
@@ -356,6 +391,75 @@
     }
 }
 
+static int delete_file(const char *path, int (*target_delete_func)(const char 
*)) {
+    int res;
+    char *real_path;
+    struct stat st;
+    char *also_try_delete = NULL;
+    char *unlink_first = NULL;
+    int (*main_delete_func)(const char*) = target_delete_func;
+
+    real_path = process_path(path, false);
+    if (real_path == NULL)
+        return -errno;
+
+    if (settings.resolve_symlinks) {
+        if (lstat(real_path, &st) == -1) {
+            free(real_path);
+            return -errno;
+        }
+
+        if (S_ISLNK(st.st_mode)) {
+            switch(settings.resolved_symlink_deletion_policy) {
+            case RESOLVED_SYMLINK_DELETION_DENY:
+                free(real_path);
+                return -EPERM;
+            case RESOLVED_SYMLINK_DELETION_SYMLINK_ONLY:
+                main_delete_func = &unlink;
+                break;
+            case RESOLVED_SYMLINK_DELETION_SYMLINK_FIRST:
+                main_delete_func = &unlink;
+
+                also_try_delete = realpath(real_path, NULL);
+                if (also_try_delete == NULL && errno != ENOENT) {
+                    free(real_path);
+                    return -errno;
+                }
+                break;
+            case RESOLVED_SYMLINK_DELETION_TARGET_FIRST:
+                unlink_first = realpath(real_path, NULL);
+                if (unlink_first == NULL && errno != ENOENT) {
+                    free(real_path);
+                    return -errno;
+                }
+
+                if (unlink_first != NULL) {
+                    res = unlink(unlink_first);
+                    free(unlink_first);
+                    if (res == -1) {
+                        free(real_path);
+                        return -errno;
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    res = main_delete_func(real_path);
+    free(real_path);
+    if (res == -1) {
+        free(also_try_delete);
+        return -errno;
+    }
+
+    if (also_try_delete != NULL) {
+        (void)target_delete_func(also_try_delete);
+        free(also_try_delete);
+    }
+
+    return 0;
+}
 
 
 static void *bindfs_init()
@@ -384,34 +488,57 @@
 
 static int bindfs_getattr(const char *path, struct stat *stbuf)
 {
-    path = process_path(path);
+    int res;
+    char *real_path;
 
-    if (lstat(path, stbuf) == -1)
+    real_path = process_path(path, true);
+    if (real_path == NULL)
         return -errno;
-    return getattr_common(path, stbuf);
+
+    if (lstat(real_path, stbuf) == -1) {
+        free(real_path);
+        return -errno;
+    }
+
+    res = getattr_common(real_path, stbuf);
+    free(real_path);
+    return res;
 }
 
 static int bindfs_fgetattr(const char *path, struct stat *stbuf,
                            struct fuse_file_info *fi)
 {
-    path = process_path(path);
+    int res;
+    char *real_path;
 
-    if (fstat(fi->fh, stbuf) == -1)
+    real_path = process_path(path, true);
+    if (real_path == NULL)
         return -errno;
-    return getattr_common(path, stbuf);
+
+    if (fstat(fi->fh, stbuf) == -1) {
+        free(real_path);
+        return -errno;
+    }
+    res = getattr_common(real_path, stbuf);
+    free(real_path);
+    return res;
 }
 
 static int bindfs_readlink(const char *path, char *buf, size_t size)
 {
     int res;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
     /* 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);
+    res = readlink(real_path, buf, size - 1);
+    free(real_path);
     if (res == -1)
         return -errno;
 
@@ -422,10 +549,14 @@
 static int bindfs_opendir(const char *path, struct fuse_file_info *fi)
 {
     DIR *dp;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
-    dp = opendir(path);
+    dp = opendir(real_path);
+    free(real_path);
     if (dp == NULL)
         return -errno;
 
@@ -447,10 +578,14 @@
     struct stat st;
     int result = 0;
     long pc_ret;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
+    pc_ret = pathconf(real_path, _PC_NAME_MAX);
+    free(real_path);
 
-    pc_ret = pathconf(path, _PC_NAME_MAX);
     if (pc_ret < 0) {
         DPRINTF("pathconf failed: %s (%d)", strerror(errno), errno);
         pc_ret = NAME_MAX;
@@ -491,20 +626,26 @@
 {
     int res;
     struct fuse_context *fc;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
     mode = permchain_apply(settings.create_permchain, mode);
 
     if (S_ISFIFO(mode))
-        res = mkfifo(path, mode);
+        res = mkfifo(real_path, mode);
     else
-        res = mknod(path, mode, rdev);
-    if (res == -1)
+        res = mknod(real_path, mode, rdev);
+    if (res == -1) {
+        free(real_path);
         return -errno;
+    }
 
     fc = fuse_get_context();
-    chown_new_file(path, fc, &chown);
+    chown_new_file(real_path, fc, &chown);
+    free(real_path);
 
     return 0;
 }
@@ -513,61 +654,60 @@
 {
     int res;
     struct fuse_context *fc;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
     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)
+    res = mkdir(real_path, mode & 0777);
+    if (res == -1) {
+        free(real_path);
         return -errno;
+    }
 
     fc = fuse_get_context();
-    chown_new_file(path, fc, &chown);
+    chown_new_file(real_path, fc, &chown);
+    free(real_path);
 
     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;
+    return delete_file(path, &unlink);
 }
 
 static int bindfs_rmdir(const char *path)
 {
-    int res;
-
-    path = process_path(path);
-
-    res = rmdir(path);
-    if (res == -1)
-        return -errno;
-
-    return 0;
+    return delete_file(path, &rmdir);
 }
 
 static int bindfs_symlink(const char *from, const char *to)
 {
     int res;
     struct fuse_context *fc;
+    char *real_to;
 
-    to = process_path(to);
+    if (settings.resolve_symlinks)
+        return -EPERM;
 
-    res = symlink(from, to);
-    if (res == -1)
+    real_to = process_path(to, false);
+    if (real_to == NULL)
+        return -errno;
+
+    res = symlink(from, real_to);
+    if (res == -1) {
+        free(real_to);
         return -errno;
+    }
 
     fc = fuse_get_context();
-    chown_new_file(to, fc, &lchown);
+    chown_new_file(real_to, fc, &lchown);
+    free(real_to);
 
     return 0;
 }
@@ -575,11 +715,21 @@
 static int bindfs_rename(const char *from, const char *to)
 {
     int res;
+    char *real_from, *real_to;
+
+    real_from = process_path(from, false);
+    if (real_from == NULL)
+        return -errno;
 
-    from = process_path(from);
-    to = process_path(to);
+    real_to = process_path(to, true);
+    if (real_to == NULL) {
+        free(real_from);
+        return -errno;
+    }
 
-    res = rename(from, to);
+    res = rename(real_from, real_to);
+    free(real_from);
+    free(real_to);
     if (res == -1)
         return -errno;
 
@@ -589,11 +739,21 @@
 static int bindfs_link(const char *from, const char *to)
 {
     int res;
+    char *real_from, *real_to;
 
-    from = process_path(from);
-    to = process_path(to);
+    real_from = process_path(from, true);
+    if (real_from == NULL)
+        return -errno;
 
-    res = link(from, to);
+    real_to = process_path(to, true);
+    if (real_to == NULL) {
+        free(real_from);
+        return -errno;
+    }
+
+    res = link(real_from, real_to);
+    free(real_from);
+    free(real_to);
     if (res == -1)
         return -errno;
 
@@ -605,13 +765,18 @@
     int file_execute_only = 0;
     struct stat st;
     mode_t diff = 0;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
     if (settings.chmod_allow_x) {
         /* Get the old permission bits and see which bits would change. */
-        if (lstat(path, &st) == -1)
+        if (lstat(real_path, &st) == -1) {
+            free(real_path);
             return -errno;
+        }
 
         if (S_ISREG(st.st_mode)) {
             diff = (st.st_mode & 07777) ^ (mode & 07777);
@@ -622,26 +787,36 @@
     switch (settings.chmod_policy) {
     case CHMOD_NORMAL:
         mode = permchain_apply(settings.chmod_permchain, mode);
-        if (chmod(path, mode) == -1)
+        if (chmod(real_path, mode) == -1) {
+            free(real_path);
             return -errno;
+        }
+        free(real_path);
         return 0;
     case CHMOD_IGNORE:
         if (file_execute_only) {
             diff &= 00111; /* See which execute bits were flipped.
                               Forget about other differences. */
-            if (chmod(path, st.st_mode ^ diff) == -1)
+            if (chmod(real_path, st.st_mode ^ diff) == -1) {
+                free(real_path);
                 return -errno;
+            }
         }
+        free(real_path);
         return 0;
     case CHMOD_DENY:
         if (file_execute_only) {
             if ((diff & 07666) == 0) {
                 /* Only execute bits have changed, so we can allow this. */
-                if (chmod(path, mode) == -1)
+                if (chmod(real_path, mode) == -1) {
+                    free(real_path);
                     return -errno;
+                }
+                free(real_path);
                 return 0;
             }
         }
+        free(real_path);
         return -EPERM;
     default:
         assert(0);
@@ -651,6 +826,7 @@
 static int bindfs_chown(const char *path, uid_t uid, gid_t gid)
 {
     int res;
+    char *real_path;
 
     if (uid != -1) {
         switch (settings.chown_policy) {
@@ -679,8 +855,12 @@
     }
 
     if (uid != -1 || gid != -1) {
-        path = process_path(path);
-        res = lchown(path, uid, gid);
+        real_path = process_path(path, true);
+        if (real_path == NULL)
+            return -errno;
+
+        res = lchown(real_path, uid, gid);
+        free(real_path);
         if (res == -1)
             return -errno;
     }
@@ -691,10 +871,14 @@
 static int bindfs_truncate(const char *path, off_t size)
 {
     int res;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
-    res = truncate(path, size);
+    res = truncate(real_path, size);
+    free(real_path);
     if (res == -1)
         return -errno;
 
@@ -717,22 +901,26 @@
 static int bindfs_utimens(const char *path, const struct timespec ts[2])
 {
     int res;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
 #ifdef HAVE_UTIMENSAT
-    res = utimensat(settings.mntsrc_fd, path, ts, AT_SYMLINK_NOFOLLOW);
+    res = utimensat(settings.mntsrc_fd, real_path, ts, AT_SYMLINK_NOFOLLOW);
 #elif HAVE_LUTIMES
     struct timeval tv[2];
     tv[0].tv_sec = ts[0].tv_sec;
     tv[0].tv_usec = ts[0].tv_nsec / 1000;
     tv[1].tv_sec = ts[1].tv_sec;
     tv[1].tv_usec = ts[1].tv_nsec / 1000;
-    res = lutimes(path, tv);
+    res = lutimes(real_path, tv);
 #else
 #error "No symlink-compatible utime* function available."
 #endif
-
+ 
+    free(real_path);
     if (res == -1)
         return -errno;
 
@@ -743,18 +931,24 @@
 {
     int fd;
     struct fuse_context *fc;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
     mode |= S_IFREG; /* tell permchain_apply this is a regular file */
     mode = permchain_apply(settings.create_permchain, mode);
 
-    fd = open(path, fi->flags, mode & 0777);
-    if (fd == -1)
+    fd = open(real_path, fi->flags, mode & 0777);
+    if (fd == -1) {
+        free(real_path);
         return -errno;
+    }
 
     fc = fuse_get_context();
-    chown_new_file(path, fc, &chown);
+    chown_new_file(real_path, fc, &chown);
+    free(real_path);
 
     fi->fh = fd;
     return 0;
@@ -763,10 +957,14 @@
 static int bindfs_open(const char *path, struct fuse_file_info *fi)
 {
     int fd;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
-    fd = open(path, fi->flags);
+    fd = open(real_path, fi->flags);
+    free(real_path);
     if (fd == -1)
         return -errno;
 
@@ -811,10 +1009,14 @@
 static int bindfs_statfs(const char *path, struct statvfs *stbuf)
 {
     int res;
+    char *real_path;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
-    res = statvfs(path, stbuf);
+    res = statvfs(real_path, stbuf);
+    free(real_path);
     if (res == -1)
         return -errno;
 
@@ -864,13 +1066,16 @@
 #endif
 {
     int res;
+    char *real_path;
 
     DPRINTF("setxattr %s %s=%s", path, name, value);
 
     if (settings.xattr_policy == XATTR_READ_ONLY)
         return -EACCES;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
 #if defined(__APPLE__)
     if (!strncmp(name, XATTR_APPLE_PREFIX, sizeof(XATTR_APPLE_PREFIX) - 1)) {
@@ -881,16 +1086,17 @@
         char new_name[MAXPATHLEN];
         memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
         memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
-        res = setxattr(path, new_name, value, size, position, flags);
+        res = setxattr(real_path, new_name, value, size, position, flags);
     } else {
-        res = setxattr(path, name, value, size, position, flags);
+        res = setxattr(real_path, name, value, size, position, flags);
     }
 #elif defined(HAVE_LSETXATTR)
-    res = lsetxattr(path, name, value, size, flags);
+    res = lsetxattr(real_path, name, value, size, flags);
 #else
-    res = setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW);
+    res = setxattr(real_path, name, value, size, 0, flags | XATTR_NOFOLLOW);
 #endif
 
+    free(real_path);
     if (res == -1)
         return -errno;
     return 0;
@@ -905,25 +1111,29 @@
 #endif
 {
     int res;
+    char *real_path;
 
     DPRINTF("getxattr %s %s", path, name);
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
 #if defined(__APPLE__)
     if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) {
         char new_name[MAXPATHLEN];
         memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
         memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
-        res = getxattr(path, new_name, value, size, position, XATTR_NOFOLLOW);
+        res = getxattr(real_path, new_name, value, size, position, 
XATTR_NOFOLLOW);
     } else {
-        res = getxattr(path, name, value, size, position, XATTR_NOFOLLOW);
+        res = getxattr(real_path, name, value, size, position, XATTR_NOFOLLOW);
     }
 #elif defined(HAVE_LGETXATTR)
-    res = lgetxattr(path, name, value, size);
+    res = lgetxattr(real_path, name, value, size);
 #else
-    res = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
+    res = getxattr(real_path, name, value, size, 0, XATTR_NOFOLLOW);
 #endif
+    free(real_path);
     if (res == -1)
         return -errno;
     return res;
@@ -931,12 +1141,16 @@
 
 static int bindfs_listxattr(const char *path, char* list, size_t size)
 {
+    char *real_path;
+
     DPRINTF("listxattr %s", path);
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
 #if defined(__APPLE__)
-    ssize_t res = listxattr(path, list, size, XATTR_NOFOLLOW);
+    ssize_t res = listxattr(real_path, list, size, XATTR_NOFOLLOW);
     if (res > 0) {
         if (list) {
             size_t len = 0;
@@ -955,7 +1169,7 @@
             // TODO: 
https://github.com/osxfuse/fuse/blob/master/example/fusexmp_fh.c
             // had this commented out bit here o_O
             /*
-            ssize_t res2 = getxattr(path, G_KAUTH_FILESEC_XATTR, NULL, 0, 0,
+            ssize_t res2 = getxattr(real_path, G_KAUTH_FILESEC_XATTR, NULL, 0, 
0,
                                     XATTR_NOFOLLOW);
             if (res2 >= 0) {
                 res -= sizeof(G_KAUTH_FILESEC_XATTR);
@@ -964,10 +1178,11 @@
         }
     }
 #elif defined(HAVE_LLISTXATTR)
-    int res = llistxattr(path, list, size);
+    int res = llistxattr(real_path, list, size);
 #else
-    int res = listxattr(path, list, size, XATTR_NOFOLLOW);
+    int res = listxattr(real_path, list, size, XATTR_NOFOLLOW);
 #endif
+    free(real_path);
     if (res == -1)
         return -errno;
     return res;
@@ -976,29 +1191,33 @@
 static int bindfs_removexattr(const char *path, const char *name)
 {
     int res;
+    char *real_path;
 
     DPRINTF("removexattr %s %s", path, name);
 
     if (settings.xattr_policy == XATTR_READ_ONLY)
         return -EACCES;
 
-    path = process_path(path);
+    real_path = process_path(path, true);
+    if (real_path == NULL)
+        return -errno;
 
 #if defined(__APPLE__)
     if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) {
         char new_name[MAXPATHLEN];
         memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
         memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
-        res = removexattr(path, new_name, XATTR_NOFOLLOW);
+        res = removexattr(real_path, new_name, XATTR_NOFOLLOW);
     } else {
-        res = removexattr(path, name, XATTR_NOFOLLOW);
+        res = removexattr(real_path, name, XATTR_NOFOLLOW);
     }
 #elif defined(HAVE_LREMOVEXATTR)
-    res = lremovexattr(path, name);
+    res = lremovexattr(real_path, name);
 #else
-    res = removexattr(path, name, XATTR_NOFOLLOW);
+    res = removexattr(real_path, name, XATTR_NOFOLLOW);
 #endif
 
+    free(real_path);
     if (res == -1)
         return -errno;
     return 0;
@@ -1106,6 +1325,8 @@
            "  --ctime-from-mtime        Read file properties' change time\n"
            "                            from file content modification time.\n"
            "  --hide-hard-links         Always report a hard link count of 
1.\n"
+           "  --resolve-symlinks        Resolve symbolic links.\n"
+           "  --resolved-symlink-deletion=...  Decide how to delete resolved 
symlinks.\n"
            "  --multithreaded           Enable multithreaded mode. See man 
page\n"
            "                            for security issue with current 
implementation.\n"
            "\n"
@@ -1144,6 +1365,7 @@
     OPTKEY_REALISTIC_PERMISSIONS,
     OPTKEY_CTIME_FROM_MTIME,
     OPTKEY_HIDE_HARD_LINKS,
+    OPTKEY_RESOLVE_SYMLINKS,
     OPTKEY_MULTITHREADED
 };
 
@@ -1225,13 +1447,27 @@
     case OPTKEY_HIDE_HARD_LINKS:
         settings.hide_hard_links = 1;
         return 0;
+    case OPTKEY_RESOLVE_SYMLINKS:
+        settings.resolve_symlinks = 1;
+        return 0;
 
     case OPTKEY_NONOPTION:
         if (!settings.mntsrc) {
-            settings.mntsrc = arg;
+            settings.mntsrc = realpath(arg, NULL);
+            if (settings.mntsrc == NULL) {
+                fprintf(stderr, "Failed to resolve source directory `%s': ", 
arg);
+                perror(NULL);
+                return -1;
+            }
             return 0;
         } else if (!settings.mntdest) {
-            settings.mntdest = arg;
+            settings.mntdest = realpath(arg, NULL);
+            if (settings.mntdest == NULL) {
+                fprintf(stderr, "Failed to resolve mount point `%s': ", arg);
+                perror(NULL);
+                return -1;
+            }
+            settings.mntdest_len = strlen(settings.mntdest);
             return 1; /* leave this argument for fuse_main */
         } else {
             fprintf(stderr, "Too many arguments given\n");
@@ -1250,7 +1486,7 @@
     char *p, *tmpstr;
 
     settings.num_mirrored_users = count_chars(mirror, ',') +
-                                    count_chars(mirror, ':') + 1;
+                                  count_chars(mirror, ':') + 1;
     settings.num_mirrored_members = ((*mirror == '@') ? 1 : 0) +
                                     count_substrs(mirror, ",@") +
                                     count_substrs(mirror, ":@");
@@ -1432,6 +1668,8 @@
 
 static void atexit_func()
 {
+    free(settings.mntsrc);
+    free(settings.mntdest);
     free(settings.original_working_dir);
     settings.original_working_dir = NULL;
     if (settings.read_limiter) {
@@ -1480,6 +1718,7 @@
         char *create_for_group;
         char *create_with_perms;
         char *chmod_filter;
+        char *resolved_symlink_deletion;
         int no_allow_other;
         int multithreaded;
     } od;
@@ -1537,9 +1776,12 @@
         OPT2("--xattr-ro", "xattr-ro", OPTKEY_XATTR_READ_ONLY),
         OPT2("--xattr-rw", "xattr-rw", OPTKEY_XATTR_READ_WRITE),
 
+        OPT2("--hide-hard-links", "hide-hard-links", OPTKEY_HIDE_HARD_LINKS),
+        OPT2("--resolve-symlinks", "resolve-symlinks", 
OPTKEY_RESOLVE_SYMLINKS),
+        OPT_OFFSET2("--resolved-symlink-deletion=%s", 
"resolved-symlink-deletion=%s", resolved_symlink_deletion, -1),
+
         OPT2("--realistic-permissions", "realistic-permissions", 
OPTKEY_REALISTIC_PERMISSIONS),
         OPT2("--ctime-from-mtime", "ctime-from-mtime", 
OPTKEY_CTIME_FROM_MTIME),
-        OPT2("--hide-hard-links", "hide-hard-links", OPTKEY_HIDE_HARD_LINKS),
         OPT_OFFSET2("--multithreaded", "multithreaded", multithreaded, -1),
         FUSE_OPT_END
     };
@@ -1561,6 +1803,7 @@
     settings.create_for_gid = -1;
     settings.mntsrc = NULL;
     settings.mntdest = NULL;
+    settings.mntdest_len = 0;
     settings.original_working_dir = get_working_dir();
     settings.create_policy = (getuid() == 0) ? CREATE_AS_USER : 
CREATE_AS_MOUNTER;
     settings.create_permchain = permchain_create();
@@ -1575,9 +1818,11 @@
     settings.num_mirrored_users = 0;
     settings.mirrored_members = NULL;
     settings.num_mirrored_members = 0;
+    settings.hide_hard_links = 0;
+    settings.resolve_symlinks = 0;
+    settings.resolved_symlink_deletion_policy = 
RESOLVED_SYMLINK_DELETION_SYMLINK_ONLY;
     settings.realistic_permissions = 0;
     settings.ctime_from_mtime = 0;
-    settings.hide_hard_links = 0;
     atexit(&atexit_func);
 
     /* Parse options */
@@ -1710,6 +1955,23 @@
             return 1;
         }
     }
+
+
+    /* Parse resolved_symlink_deletion */
+    if (od.resolved_symlink_deletion) {
+        if (strcmp(od.resolved_symlink_deletion, "deny") == 0) {
+            settings.resolved_symlink_deletion_policy = 
RESOLVED_SYMLINK_DELETION_DENY;
+        } else if (strcmp(od.resolved_symlink_deletion, "symlink-only") == 0) {
+            settings.resolved_symlink_deletion_policy = 
RESOLVED_SYMLINK_DELETION_SYMLINK_ONLY;
+        } else if (strcmp(od.resolved_symlink_deletion, "symlink-first") == 0) 
{
+            settings.resolved_symlink_deletion_policy = 
RESOLVED_SYMLINK_DELETION_SYMLINK_FIRST;
+        } else if (strcmp(od.resolved_symlink_deletion, "target-first") == 0) {
+            settings.resolved_symlink_deletion_policy = 
RESOLVED_SYMLINK_DELETION_TARGET_FIRST;
+        } else {
+            fprintf(stderr, "Invalid setting for --resolved-symlink-deletion: 
'%s'\n", od.resolved_symlink_deletion);
+            return 1;
+        }
+    }
 
 
     /* Single-threaded mode by default */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bindfs-1.12.7/tests/common.rb 
new/bindfs-1.13.0/tests/common.rb
--- old/bindfs-1.12.7/tests/common.rb   2015-09-09 11:40:21.000000000 +0200
+++ new/bindfs-1.13.0/tests/common.rb   2015-09-27 00:11:40.000000000 +0200
@@ -179,6 +179,18 @@
     end
 end
 
+# Like testenv but skips the test if not running as non-root.
+# TODO: make all tests runnable as root
+def nonroot_testenv(bindfs_args, options = {}, &block)
+    if Process.uid != 0
+        testenv(bindfs_args, options, &block)
+    else
+        puts "--- #{bindfs_args} ---"
+        puts "[  #{bindfs_args}  ]"
+        puts "SKIP (requires running as non-root)"
+    end
+end
+
 def umount_cmd
     if `which fusermount`.strip.empty?
     then 'umount'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/bindfs-1.12.7/tests/test_bindfs.rb 
new/bindfs-1.13.0/tests/test_bindfs.rb
--- old/bindfs-1.12.7/tests/test_bindfs.rb      2015-09-09 11:40:21.000000000 
+0200
+++ new/bindfs-1.13.0/tests/test_bindfs.rb      2015-09-27 00:11:40.000000000 
+0200
@@ -373,7 +373,7 @@
 testenv("", :title => "utimens on symlinks") do
     touch('mnt/file')
     Dir.chdir "mnt" do
-      system('ln -sf file link')
+      symlink('file', 'link')
     end
 
     system("#{$tests_dir}/utimens_nofollow mnt/link 12 34 56 78")
@@ -385,6 +385,179 @@
     assert { File.lstat('mnt/file').mtime.to_i > 100 }
 end
 
+testenv("--resolve-symlinks", :title => "resolving symlinks") do
+  mkdir('src/dir')
+  File.write('src/dir/file', 'hello')
+  Dir.chdir 'src' do
+    symlink('dir', 'dirlink')
+    symlink('dir/file', 'filelink')
+    symlink('dirlink/file', 'filelink2')
+  end
+
+  assert { !File.lstat('mnt/dirlink').symlink? }
+  assert { File.lstat('mnt/dirlink').directory? }
+  assert { !File.lstat('mnt/dirlink/file').symlink? }
+  assert { File.lstat('mnt/dirlink/file').file? }
+  assert { File.lstat('mnt/filelink').file? }
+  assert { File.read('mnt/filelink') == 'hello' }
+end
+
+testenv("--resolve-symlinks", :title => "attributes of resolved symlinks") do
+  Dir.chdir 'src' do
+    touch('file')
+    symlink('file', 'link')
+    chmod(0654, 'file')
+  end
+
+  assert { File.lstat('mnt/link').mode & 0777 == 0654 }
+end
+
+testenv("--resolve-symlinks", :title => "writing through resolved symlinks") do
+  Dir.chdir 'src' do
+    File.write('file', 'initial_content')
+    symlink('file', 'link')
+  end
+
+  File.write('mnt/link', 'new_content')
+  assert { File.read('src/file') == 'new_content' }
+  assert { File.read('src/link') == 'new_content' }
+  assert { File.symlink?('src/link') }
+end
+
+testenv("--resolve-symlinks", :title => "moving over resolved symlinks") do
+  Dir.chdir 'src' do
+    File.write('file', 'initial_content')
+    File.write('newfile', 'new_content')
+    symlink('file', 'link')
+  end
+
+  Dir.chdir 'mnt' do
+    system("mv newfile link")
+  end
+  assert { File.symlink?('src/link') }
+  assert { File.read('src/file') == 'new_content' }
+  assert { !File.exist?('src/newfile') }
+end
+
+testenv("--resolve-symlinks", :title => "moving resolved symlinks") do
+  Dir.chdir 'src' do
+    touch('file')
+    symlink('file', 'link')
+  end
+
+  Dir.chdir 'mnt' do
+    system("mv link lonk")
+  end
+  assert { !File.symlink?('src/link') }
+  assert { File.lstat('src/lonk').symlink? }
+  assert { File.readlink('src/lonk') == 'file' }
+end
+
+testenv("--resolve-symlinks", :title => "--resolve-symlinks disallows new 
symlinks") do
+  touch('mnt/file')
+  Dir.chdir "mnt" do
+    begin
+      File.symlink("file", "link")
+    rescue Errno::EPERM => exception
+    end
+    assert { exception != nil }
+  end
+end
+
+testenv("--resolve-symlinks", :title => "deleting a resolved symlink deletes 
the underlying symlink only by default") do
+  Dir.chdir 'src' do
+    touch('file')
+    symlink('file', 'link')
+    symlink('broken', 'broken_link')
+  end
+
+  File.unlink('mnt/link')
+  assert { !File.symlink?('src/link') }
+  assert { File.exist?('src/file') }
+
+  File.unlink('mnt/broken_link')
+  assert { !File.symlink?('src/broken_link') }
+end
+
+testenv("--resolve-symlinks --resolved-symlink-deletion=deny") do
+  Dir.chdir 'src' do
+    touch('file')
+    symlink('file', 'link')
+  end
+
+  begin
+    File.unlink('mnt/link')
+  rescue Errno::EPERM => exception
+  end
+  assert { exception != nil }
+  assert { File.symlink?('src/link') }
+  assert { File.exist?('src/file') }
+end
+
+# TODO: make all tests runnable as root. This is nonroot because we can't
+# easily prevent a bindfs running as root from deleting a file.
+nonroot_testenv("--resolve-symlinks 
--resolved-symlink-deletion=symlink-first") do
+  begin
+    Dir.chdir 'src' do
+      mkdir('dir')
+      touch('deletable_file')
+      touch('dir/undeletable_file')
+      chmod(0555, 'dir')
+      symlink('deletable_file', 'link1')
+      symlink('dir/undeletable_file', 'link2')
+      symlink('broken', 'link3')
+    end
+
+    File.unlink('mnt/link1')
+    assert { !File.symlink?('src/link1') }
+    assert { !File.exist?('src/dir/deletable_file') }
+
+    File.unlink('mnt/link2')
+    assert { !File.symlink?('src/link2') }
+    assert { File.exist?('src/dir/undeletable_file') }
+
+    File.unlink('mnt/link3')
+    assert { !File.symlink?('src/link3') }
+  ensure
+    chmod(0777, 'src/dir')  # So the cleanup code can delete dir/*
+  end
+end
+
+# TODO: make all tests runnable as root. This is nonroot because we can't
+# easily prevent a bindfs running as root from deleting a file.
+nonroot_testenv("--resolve-symlinks --resolved-symlink-deletion=target-first 
-p a+w") do
+  begin
+    Dir.chdir 'src' do
+      mkdir('dir')
+      touch('file1')
+      touch('file2')
+      symlink('file1', 'deletable_link')
+      Dir.chdir('dir') do
+        symlink('../file2', 'undeletable_link')
+      end
+      chmod(0555, 'dir')
+      symlink('broken', 'broken_link')
+    end
+
+    File.unlink('mnt/deletable_link')
+    assert { !File.symlink?('src/deletable_link') }
+    assert { !File.exist?('src/file1') }
+
+    begin
+      File.unlink('mnt/dir/undeletable_link')
+    rescue Errno::EACCES => exception
+    end
+    assert { exception != nil }
+    assert { File.symlink?('src/dir/undeletable_link') }
+    assert { !File.exist?('src/file2') }
+
+    File.unlink('mnt/broken_link')
+    assert { !File.symlink?('src/broken_link') }
+  ensure
+    chmod(0777, 'src/dir')  # So the cleanup code can delete dir/*
+  end
+end
+
 # FIXME: this stuff around testenv is a hax, and testenv may also exit(), 
which defeats the 'ensure' below.
 # the test setup ought to be refactored. It might well use MiniTest or 
something.
 if Process.uid == 0


Reply via email to