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
================
</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>
}
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;
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.
#include <atalk/globals.h>
#include <atalk/fce_api.h>
#include <atalk/netatalk_conf.h>
+#include <atalk/errchk.h>
#include "directory.h"
#include "dircache.h"
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)
{
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:
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);
#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 */
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;
case ENOENT :
return AFPERR_NOOBJ;
case ENOTEMPTY :
+ case EEXIST:
return AFPERR_DIRNEMPT;
case EPERM:
case EACCES :
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;
}
#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)
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"\&.