New boolean volume option "delete veto files"
authorRalph Boehme <sloowfranklin@gmail.com>
Wed, 24 Jul 2013 20:31:19 +0000 (22:31 +0200)
committerRalph Boehme <sloowfranklin@gmail.com>
Fri, 9 Aug 2013 07:50:22 +0000 (09:50 +0200)
If this option is set to yes, then Netatalk will attempt to
recursively delete any vetoed files and directories.

Remove obselete code that tries to delete dangling symlinks, this
is not necessary anymore since Netatalk support UNIX symlinks.

Modify behaviour of the VFS function deletecurdir of the "ea" VFS
module so it deletes all files beginning with "._".

From feature request #82.

NEWS
doc/manpages/man5/afp.conf.5.xml
etc/afpd/directory.c
etc/afpd/filedir.c
etc/afpd/filedir.h
include/atalk/volume.h
libatalk/util/netatalk_conf.c
libatalk/vfs/unix.c
libatalk/vfs/vfs.c
man/man5/afp.conf.5.in

diff --git a/NEWS b/NEWS
index 1c071750b8981ed739cd7084ac8561d178bf37d1..f30bd6150ff4f6f5428bf4194f28885efd4e481e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,9 @@ Changes in 3.0.5
        Then whenever a client tries to access any file or directory
        with a vetoed name, it will be sent an AFP message indicating
        the name and the directory. From FR #81.
+* NEW: New boolean volume option "delete veto files". If this option is
+       set to yes, then Netatalk will attempt to recursively delete any
+       vetoed files and directories. FR #82.
 
 Changes in 3.0.4
 ================
index 48df77b1fd3fbf2d66b095fe9f425904a2498fe1..27e8b14e81d3aff738e6a25aeeeea572392e252c 100644 (file)
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term>delete veto files = <replaceable>BOOLEAN</replaceable>
+          (default: <emphasis>no</emphasis>) <type>(V)</type></term>
+
+          <listitem>
+            <para>This option is used when Netatalk is attempting to delete a
+            directory that contains one or more vetoed files or directories
+            (see the veto files option). If this option is set to no (the
+            default) then if a directory contains any non-vetoed files or
+            directories then the directory delete will fail. This is usually
+            what you want.</para>
+            <para>If this option is set to yes, then Netatalk will attempt to
+            recursively delete any files and directories within the vetoed
+            directory.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term>follow symlinks = <replaceable>BOOLEAN</replaceable> (default:
           <emphasis>no</emphasis>) <type>(V)</type></term>
index c1160af22c86cb119e8f33875a4ace21091b596f..0d5283992c736d97ade36481436bbfe3b4c720e7 100644 (file)
@@ -2311,33 +2311,11 @@ int deletecurdir(struct vol *vol)
     }
     err = vol->vfs->vfs_deletecurdir(vol);
     if (err) {
-        LOG(log_error, logtype_afpd, "deletecurdir: error deleting .AppleDouble in \"%s\"",
+        LOG(log_error, logtype_afpd, "deletecurdir: error deleting AppleDouble files in \"%s\"",
             cfrombstr(curdir->d_fullpath));
         return err;
     }
 
-    /* now get rid of dangling symlinks */
-    if ((dp = opendir("."))) {
-        while ((de = readdir(dp))) {
-            /* skip this and previous directory */
-            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-                continue;
-
-            /* bail if it's not a symlink */
-            if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
-                LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): not empty",
-                    bdata(curdir->d_fullpath));
-                closedir(dp);
-                return AFPERR_DIRNEMPT;
-            }
-
-            if ((err = netatalk_unlink(de->d_name))) {
-                closedir(dp);
-                return err;
-            }
-        }
-    }
-
     if (movecwd(vol, pdir) < 0) {
         err = afp_errno;
         goto delete_done;
@@ -2347,16 +2325,28 @@ int deletecurdir(struct vol *vol)
         cfrombstr(curdir->d_fullpath));
 
     err = netatalk_rmdir_all_errors(-1, cfrombstr(fdir->d_u_name));
-    if ( err ==  AFP_OK || err == AFPERR_NOOBJ) {
-        AFP_CNID_START("cnid_delete");
-        cnid_delete(vol->v_cdb, fdir->d_did);
-        AFP_CNID_DONE();
-        dir_remove( vol, fdir );
-    } else {
+
+    switch (err) {
+    case AFP_OK:
+    case AFPERR_NOOBJ:
+        break;
+    case AFPERR_DIRNEMPT:
+        if (delete_vetoed_files(vol, bdata(fdir->d_u_name), false) != 0)
+            goto delete_done;
+        err = AFP_OK;
+        break;
+    default:
         LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): netatalk_rmdir_all_errors error",
             cfrombstr(curdir->d_fullpath));
+        goto delete_done;
     }
 
+    AFP_CNID_START("cnid_delete");
+    cnid_delete(vol->v_cdb, fdir->d_did);
+    AFP_CNID_DONE();
+
+    dir_remove( vol, fdir );
+
 delete_done:
     if (dp) {
         /* inode is used as key for cnid.
index 234a26e550e11f47d8faf208a97371a746107773..f35edfc9ad6496e4cf3e00d949015b97b7964374 100644 (file)
@@ -26,6 +26,7 @@
 #include <atalk/globals.h>
 #include <atalk/fce_api.h>
 #include <atalk/netatalk_conf.h>
+#include <atalk/errchk.h>
 
 #include "directory.h"
 #include "dircache.h"
@@ -468,6 +469,83 @@ int afp_rename(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
     return( rc );
 }
 
+/* 
+ * Recursivley delete vetoed files and directories if the volume option is set
+ *
+ * @param vol   (r) volume handle
+ * @param upath (r) path of directory
+ *
+ * If the volume option delete veto files is set, this function recursively scans the
+ * directory "upath" for vetoed files and tries deletes these, the it will try to delete
+ * the directory. That may fail if the directory contains normal files that aren't vetoed.
+ *
+ * @returns 0 if the directory upath and all of its contents were deleted, otherwise -1.
+ *            If the volume option is not set it returns -1.
+ */
+int delete_vetoed_files(struct vol *vol, const char *upath, bool in_vetodir)
+{
+    EC_INIT;
+    DIR            *dp = NULL;
+    struct dirent  *de;
+    struct stat     sb;
+    int             pwd = -1;
+    bool            vetoed;
+
+    if (!(vol->v_flags & AFPVOL_DELVETO))
+        return -1;
+
+    EC_NEG1( pwd = open(".", O_RDONLY));
+    EC_ZERO( chdir(upath) );
+    EC_NULL( dp = opendir(".") );
+
+    while ((de = readdir(dp))) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+            continue;
+
+        if (stat(de->d_name, &sb) != 0) {
+            LOG(log_error, logtype_afpd, "delete_vetoed_files(\"%s/%s\"): %s",
+                upath, de->d_name, strerror(errno));
+                EC_EXIT_STATUS(AFPERR_DIRNEMPT);
+        }
+
+        if (in_vetodir || veto_file(vol->v_veto, de->d_name))
+            vetoed = true;
+        else
+            vetoed = false;
+
+        if (vetoed) {
+            LOG(log_debug, logtype_afpd, "delete_vetoed_files(\"%s/%s\"): deleting vetoed file",
+                upath, de->d_name);
+            switch (sb.st_mode & S_IFMT) {
+            case S_IFDIR:
+                /* recursion */
+                EC_ZERO( delete_vetoed_files(vol, de->d_name, vetoed));
+                break;
+            case S_IFREG:
+            case S_IFLNK:
+                EC_ZERO( netatalk_unlink(de->d_name) );
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+    EC_ZERO_LOG( fchdir(pwd) );
+    pwd = -1;
+    EC_ZERO_LOG( rmdir(upath) );
+
+EC_CLEANUP:
+    if (dp)
+        closedir(dp);
+    if (pwd != -1) {
+        if (fchdir(pwd) != 0)
+            ret = -1;
+    }
+
+    EC_EXIT;
+}
+
 /* ------------------------------- */
 int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
@@ -512,7 +590,9 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size
             if (rmdir(upath) != 0) {
                 switch (errno) {
                 case ENOTEMPTY:
-                    return AFPERR_DIRNEMPT;
+                    if (delete_vetoed_files(vol, upath, false) != 0)
+                        return AFPERR_DIRNEMPT;
+                    break;
                 case EACCES:
                     return AFPERR_ACCESS;
                 default:
index 02c9ac15b5382ed99c5ee4cf7a84166d4e85cfda..f72e5445b0f52b544dd6c2ddec7bfc40f0599981 100644 (file)
@@ -15,6 +15,7 @@ extern int            veto_file (const char *veto_str, const char *path);
 extern int             check_name (const struct vol *vol, char *name);
 
 extern int matchfile2dirperms (char *, struct vol *, int);
+extern int delete_vetoed_files(struct vol *vol, const char *upath, bool in_vetodir);
 
 /* FP functions */
 int afp_moveandrename (AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf,  size_t *rbuflen);
index c850c20d04fea10c34334cc2bfde64751fd0821f..a9e497a6d76ddf4cf4053b673da181de09a03a79 100644 (file)
@@ -134,6 +134,7 @@ struct vol {
 #define AFPVOL_SEARCHDB  (1 << 25)   /* Use fast CNID db search instead of filesystem */
 #define AFPVOL_NONETIDS  (1 << 26)   /* signal the client it shall do privelege mapping */
 #define AFPVOL_FOLLOWSYM (1 << 27)   /* follow symlinks on the server, default is not to */
+#define AFPVOL_DELVETO   (1 << 28)   /* delete veto files and dirs */
 
 /* Extended Attributes vfs indirection  */
 #define AFPVOL_EA_NONE           0   /* No EAs */
index 4ffbde6455595fd824bc1eaeb1dc3c3436422ddc..02ed81f086e4721750212525b5ffc72018fce82d 100644 (file)
@@ -772,6 +772,8 @@ static struct vol *creatvol(AFPObj *obj,
         volume->v_flags |= AFPVOL_NOV2TOEACONV;
     if (getoption_bool(obj->iniconfig, section, "follow symlinks", preset, 0))
         volume->v_flags |= AFPVOL_FOLLOWSYM;
+    if (getoption_bool(obj->iniconfig, section, "delete veto files", preset, 0))
+        volume->v_flags |= AFPVOL_DELVETO;
 
     if (getoption_bool(obj->iniconfig, section, "preexec close", preset, 0))
         volume->v_preexec_close = 1;
index bc90c75ad539bcdaa7f8a00e14271adf37538092..79e4fd33c29b151a08cd4afb4b157c638ea444b4 100644 (file)
@@ -74,6 +74,7 @@ int netatalk_rmdir_all_errors(int dirfd, const char *name)
         case ENOENT :
             return AFPERR_NOOBJ;
         case ENOTEMPTY :
+        case EEXIST:
             return AFPERR_DIRNEMPT;
         case EPERM:
         case EACCES :
index d79ac05e1142cb14d53bf226ba45e53722ef74ab..a793cd283f5aa34c394981be77cde3f33425a6e1 100644 (file)
@@ -467,9 +467,15 @@ static int deletecurdir_ea_osx_chkifempty_loop(const struct vol *vol, struct dir
 static int deletecurdir_ea_osx_loop(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _U_)
 {
     int ret;
-    
-    if ((ret = netatalk_unlink(name)) != 0)
-        return ret;
+    struct stat sb;
+
+    if (strncmp(name, "._", strlen("._")) == 0) {
+        if (lstat(name, &sb) != 0)
+            return -1;
+        if (S_ISREG(sb.st_mode))
+            if ((ret = netatalk_unlink(name)) != 0)
+                return ret;
+    }
 
     return 0;
 }
@@ -480,17 +486,6 @@ static int RF_deletecurdir_ea(VFS_FUNC_ARGS_DELETECURDIR)
 #ifndef HAVE_EAFD
     int err;
     /* delete stray ._AppleDouble files */
-
-    /* first check if there's really no other file besides files starting with ._ */
-    if ((err = for_each_adouble("deletecurdir_ea_osx", ".",
-                                deletecurdir_ea_osx_chkifempty_loop,
-                                vol, NULL, 0)) != 0) {
-        if (err == 1)
-            return AFPERR_DIRNEMPT;
-        return AFPERR_MISC;
-    }
-
-    /* Now delete orphaned ._ files */
     if ((err = for_each_adouble("deletecurdir_ea_osx", ".",
                                 deletecurdir_ea_osx_loop,
                                 vol, NULL, 0)) != 0)
index d9ecb39f406005a2a8a0076c110bc2db65bc7490..7a1f56d5af83129550089c83264f302815e8022a 100644 (file)
@@ -1063,6 +1063,13 @@ is performed when accessing filesystems from clients\&. This is generally useful
 on volumes and do the conversion with that\&. Then this option can be set to no\&.
 .RE
 .PP
+delete veto files = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
+.RS 4
+This option is used when Netatalk is attempting to delete a directory that contains one or more vetoed files or directories (see the veto files option)\&. If this option is set to no (the default) then if a directory contains any non\-vetoed files or directories then the directory delete will fail\&. This is usually what you want\&.
+.sp
+If this option is set to yes, then Netatalk will attempt to recursively delete any files and directories within the vetoed directory\&.
+.RE
+.PP
 follow symlinks = \fIBOOLEAN\fR (default: \fIno\fR) \fB(V)\fR
 .RS 4
 The default setting is false thus symlinks are not followed on the server\&. This is the same behaviour as OS X\*(Aqs AFP server\&. Setting the option to true causes afpd to follow symlinks on the server\&. symlinks may point outside of the AFP volume, currently afpd doesn\*(Aqt do any checks for "wide symlinks"\&.