From: Ralph Boehme Date: Wed, 24 Jul 2013 20:31:19 +0000 (+0200) Subject: New boolean volume option "delete veto files" X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=commitdiff_plain;h=85f0a9871848c178e301a804c9d9081f3fbaee33 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. 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. --- diff --git a/NEWS b/NEWS index 1c071750..f30bd615 100644 --- 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 ================ diff --git a/doc/manpages/man5/afp.conf.5.xml b/doc/manpages/man5/afp.conf.5.xml index 48df77b1..27e8b14e 100644 --- a/doc/manpages/man5/afp.conf.5.xml +++ b/doc/manpages/man5/afp.conf.5.xml @@ -1732,6 +1732,23 @@ + + delete veto files = BOOLEAN + (default: no) (V) + + + 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. + If this option is set to yes, then Netatalk will attempt to + recursively delete any files and directories within the vetoed + directory. + + + follow symlinks = BOOLEAN (default: no) (V) diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c index c1160af2..0d528399 100644 --- a/etc/afpd/directory.c +++ b/etc/afpd/directory.c @@ -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. diff --git a/etc/afpd/filedir.c b/etc/afpd/filedir.c index 234a26e5..f35edfc9 100644 --- a/etc/afpd/filedir.c +++ b/etc/afpd/filedir.c @@ -26,6 +26,7 @@ #include #include #include +#include #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: diff --git a/etc/afpd/filedir.h b/etc/afpd/filedir.h index 02c9ac15..f72e5445 100644 --- a/etc/afpd/filedir.h +++ b/etc/afpd/filedir.h @@ -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); diff --git a/include/atalk/volume.h b/include/atalk/volume.h index c850c20d..a9e497a6 100644 --- a/include/atalk/volume.h +++ b/include/atalk/volume.h @@ -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 */ diff --git a/libatalk/util/netatalk_conf.c b/libatalk/util/netatalk_conf.c index 4ffbde64..02ed81f0 100644 --- a/libatalk/util/netatalk_conf.c +++ b/libatalk/util/netatalk_conf.c @@ -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; diff --git a/libatalk/vfs/unix.c b/libatalk/vfs/unix.c index bc90c75a..79e4fd33 100644 --- a/libatalk/vfs/unix.c +++ b/libatalk/vfs/unix.c @@ -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 : diff --git a/libatalk/vfs/vfs.c b/libatalk/vfs/vfs.c index d79ac05e..a793cd28 100644 --- a/libatalk/vfs/vfs.c +++ b/libatalk/vfs/vfs.c @@ -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) diff --git a/man/man5/afp.conf.5.in b/man/man5/afp.conf.5.in index d9ecb39f..7a1f56d5 100644 --- a/man/man5/afp.conf.5.in +++ b/man/man5/afp.conf.5.in @@ -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"\&.