]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/vfs/ea.c
Convert afp_moveandrename and all called funcs to XXXat semantics if available
[netatalk.git] / libatalk / vfs / ea.c
index 35048a3cc5cfa8dd43e2989aa103ded410508e83..f8e85acf0fe656707df16e9ec1d47334305646f2 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  $Id: ea.c,v 1.15 2009-11-18 08:02:33 didg Exp $
+  $Id: ea.c,v 1.20 2010-03-12 15:16:49 franklahm Exp $
   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
@@ -78,7 +78,7 @@ static char *mtoupath(const struct vol *vol, const char *mpath)
     char         *u;
     size_t       inplen;
     size_t       outlen;
-    uint16_t     flags = CONV_ESCAPEHEX | CONV_FORCE;
+    uint16_t     flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON;
 
     if (!mpath)
         return NULL;
@@ -275,48 +275,6 @@ static int pack_header(struct ea * restrict ea)
     return 0;
 }
 
-/*
- * Function: ea_path
- *
- * Purpose: return name of ea header filename
- *
- * Arguments:
- *
- *    ea           (r) ea handle
- *    eaname       (r) name of EA or NULL
- *
- * Returns: pointer to name in static buffer, NULL on error
- *
- * Effects:
- *
- * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
- * Files: "file" -> "file/.AppleDouble/file::EA"
- * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
- * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
- */
-static char * ea_path(const struct ea * restrict ea,
-                      const char * restrict eaname)
-{
-    char *adname;
-    static char pathbuf[MAXPATHLEN + 1];
-
-    /* get name of a adouble file from uname */
-    adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
-    /* copy it so we can work with it */
-    strlcpy(pathbuf, adname, MAXPATHLEN + 1);
-    /* append "::EA" */
-    strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
-
-    if (eaname) {
-        strlcat(pathbuf, "::", MAXPATHLEN + 1);
-        if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
-            return NULL;
-        strlcat(pathbuf, eaname, MAXPATHLEN + 1);
-    }
-
-    return pathbuf;
-}
-
 /*
  * Function: ea_addentry
  *
@@ -398,52 +356,6 @@ error:
     return -1;
 }
 
-/*
- * Function: ea_delentry
- *
- * Purpose: delete one EA from ea->ea_entries[]
- *
- * Arguments:
- *
- *    ea            (rw) pointer to struct ea
- *    attruname     (r) EA name
- *
- * Returns: new number of EA entries, -1 on error
- *
- * Effects:
- *
- * Remove entry from  ea->ea_entries[]. Decrement ea->ea_count.
- * Marks it as unused just by freeing name and setting it to NULL.
- * ea_close and pack_buffer must honor this.
- */
-static int ea_delentry(struct ea * restrict ea,
-                       const char * restrict attruname)
-{
-    int ret = 0;
-    unsigned int count = 0;
-
-    if (ea->ea_count == 0) {
-        LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
-        return -1;
-    }
-
-    while (count < ea->ea_count) {
-        /* search matching EA */
-        if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
-            free((*ea->ea_entries)[count].ea_name);
-            (*ea->ea_entries)[count].ea_name = NULL;
-
-            LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
-                attruname, count + 1, ea->ea_count);
-
-            break;
-        }
-        count++;
-    }
-
-    return ret;
-}
-
 /*
  * Function: create_ea_header
  *
@@ -534,7 +446,7 @@ static int write_ea(const struct ea * restrict ea,
     struct stat st;
     char *eaname;
 
-    if ((eaname = ea_path(ea, attruname)) == NULL) {
+    if ((eaname = ea_path(ea, attruname, 1)) == NULL) {
         LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname);
         return AFPERR_MISC;
     }
@@ -575,6 +487,51 @@ exit:
     return ret;
 }
 
+/*
+ * Function: ea_delentry
+ *
+ * Purpose: delete one EA from ea->ea_entries[]
+ *
+ * Arguments:
+ *
+ *    ea            (rw) pointer to struct ea
+ *    attruname     (r) EA name
+ *
+ * Returns: new number of EA entries, -1 on error
+ *
+ * Effects:
+ *
+ * Remove entry from  ea->ea_entries[]. Decrement ea->ea_count.
+ * Marks it as unused just by freeing name and setting it to NULL.
+ * ea_close and pack_buffer must honor this.
+ */
+static int ea_delentry(struct ea * restrict ea, const char * restrict attruname)
+{
+    int ret = 0;
+    unsigned int count = 0;
+
+    if (ea->ea_count == 0) {
+        LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
+        return -1;
+    }
+
+    while (count < ea->ea_count) {
+        /* search matching EA */
+        if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
+            free((*ea->ea_entries)[count].ea_name);
+            (*ea->ea_entries)[count].ea_name = NULL;
+
+            LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
+                attruname, count + 1, ea->ea_count);
+
+            break;
+        }
+        count++;
+    }
+
+    return ret;
+}
+
 /*
  * Function: delete_ea_file
  *
@@ -587,14 +544,13 @@ exit:
  *
  * Returns: 0 on success, -1 on error
  */
-static int delete_ea_file(const struct ea * restrict ea,
-                          const char *eaname)
+static int delete_ea_file(const struct ea * restrict ea, const char *eaname)
 {
     int ret = 0;
     char *eafile;
     struct stat st;
 
-    if ((eafile = ea_path(ea, eaname)) == NULL) {
+    if ((eafile = ea_path(ea, eaname, 1)) == NULL) {
         LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname);
         return -1;
     }
@@ -612,6 +568,53 @@ static int delete_ea_file(const struct ea * restrict ea,
     return ret;
 }
 
+/*************************************************************************************
+ * ea_path, ea_open and ea_close are only global so that dbd can call them
+ *************************************************************************************/
+
+/*
+ * Function: ea_path
+ *
+ * Purpose: return name of ea header filename
+ *
+ * Arguments:
+ *
+ *    ea           (r) ea handle
+ *    eaname       (r) name of EA or NULL
+ *    macname      (r) if != 0 call mtoupath on eaname
+ *
+ * Returns: pointer to name in static buffer, NULL on error
+ *
+ * Effects:
+ *
+ * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
+ * Files: "file" -> "file/.AppleDouble/file::EA"
+ * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
+ * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
+ */
+char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname)
+{
+    char *adname;
+    static char pathbuf[MAXPATHLEN + 1];
+
+    /* get name of a adouble file from uname */
+    adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
+    /* copy it so we can work with it */
+    strlcpy(pathbuf, adname, MAXPATHLEN + 1);
+    /* append "::EA" */
+    strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
+
+    if (eaname) {
+        strlcat(pathbuf, "::", MAXPATHLEN + 1);
+        if (macname)
+            if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
+                return NULL;
+        strlcat(pathbuf, eaname, MAXPATHLEN + 1);
+    }
+
+    return pathbuf;
+}
+
 /*
  * Function: ea_open
  *
@@ -627,7 +630,9 @@ static int delete_ea_file(const struct ea * restrict ea,
  *                    Eiterh EA_RDONLY or EA_RDWR MUST be requested
  *    ea          (w) pointer to a struct ea that we fill
  *
- * Returns: 0 on success, -1 on error
+ * Returns: 0 on success
+ *         -1 on misc error with errno = EFAULT
+ *         -2 if no EA header exists with errno = ENOENT
  *
  * Effects:
  *
@@ -636,10 +641,10 @@ static int delete_ea_file(const struct ea * restrict ea,
  * file is either read or write locked depending on the open flags.
  * When you're done with struct ea you must call ea_close on it.
  */
-static int ea_open(const struct vol * restrict vol,
-                   const char * restrict uname,
-                   eaflags_t eaflags,
-                   struct ea * restrict ea)
+int ea_open(const struct vol * restrict vol,
+            const char * restrict uname,
+            eaflags_t eaflags,
+            struct ea * restrict ea)
 {
     int ret = 0;
     char *eaname;
@@ -656,6 +661,8 @@ static int ea_open(const struct vol * restrict vol,
 
     ea->vol = vol;              /* ea_close needs it */
     ea->ea_flags = eaflags;
+    ea->dirfd = -1;             /* no *at (cf openat) semantics by default */
+
     /* Dont care for errors, eg when removing the file is already gone */
     if (!stat(uname, &st) && S_ISDIR(st.st_mode))
         ea->ea_flags |=  EA_DIR;
@@ -665,7 +672,7 @@ static int ea_open(const struct vol * restrict vol,
         return -1;
     }
 
-    eaname = ea_path(ea, NULL);
+    eaname = ea_path(ea, NULL, 0);
     LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
 
     /* Check if it exists, if not create it if EA_CREATE is in eaflags */
@@ -676,7 +683,7 @@ static int ea_open(const struct vol * restrict vol,
 
             if ( ! (eaflags & EA_CREATE)) {
                 /* creation was not requested, so return with error */
-                ret = -1;
+                ret = -2;
                 goto exit;
             }
 
@@ -708,6 +715,11 @@ static int ea_open(const struct vol * restrict vol,
     /* header file exists, so read and parse it */
 
     /* malloc buffer where we read disk file into */
+    if (st.st_size < EA_HEADER_SIZE) {
+        LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname);
+        ret = -1;
+        goto exit;
+    }
     ea->ea_size = st.st_size;
     ea->ea_data = malloc(st.st_size);
     if (! ea->ea_data) {
@@ -754,9 +766,14 @@ static int ea_open(const struct vol * restrict vol,
     }
 
 exit:
-    if (ret == 0) {
+    switch (ret) {
+    case 0:
         ea->ea_inited = EA_INITED;
-    } else {
+        break;
+    case -1:
+        errno = EFAULT; /* force some errno distinguishable from ENOENT */
+        /* fall through */
+    case -2:
         if (ea->ea_data) {
             free(ea->ea_data);
             ea->ea_data = NULL;
@@ -765,9 +782,72 @@ exit:
             close(ea->ea_fd);
             ea->ea_fd = -1;
         }
+        break;
+    }
+
+    return ret;
+}
+
+/*
+ * Function: ea_openat
+ *
+ * Purpose: openat like wrapper for ea_open, takes a additional file descriptor
+ *
+ * Arguments:
+ *
+ *    vol         (r) current volume
+ *    sfd         (r) openat like file descriptor
+ *    uname       (r) filename for which we have to open a header
+ *    flags       (r) EA_CREATE: create if it doesn't exist (without it won't be created)
+ *                    EA_RDONLY: open read only
+ *                    EA_RDWR: open read/write
+ *                    Eiterh EA_RDONLY or EA_RDWR MUST be requested
+ *    ea          (w) pointer to a struct ea that we fill
+ *
+ * Returns: 0 on success
+ *         -1 on misc error with errno = EFAULT
+ *         -2 if no EA header exists with errno = ENOENT
+ *
+ * Effects:
+ *
+ * opens header file and stores fd in ea->ea_fd. Size of file is put into ea->ea_size.
+ * number of EAs is stored in ea->ea_count. flags are remembered in ea->ea_flags.
+ * file is either read or write locked depending on the open flags.
+ * When you're done with struct ea you must call ea_close on it.
+ */
+int ea_openat(const struct vol * restrict vol,
+              int dirfd,
+              const char * restrict uname,
+              eaflags_t eaflags,
+              struct ea * restrict ea)
+{
+    int ret = 0;
+    int cwdfd = -1;
+
+    if (dirfd != -1) {
+        if (((cwdfd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
+            ret = -1;
+            goto exit;
+        }
+    }
+
+    ret = ea_open(vol, uname, eaflags, ea);
+    ea->dirfd = dirfd;
+
+    if (dirfd != -1) {
+        if (fchdir(cwdfd) != 0) {
+            LOG(log_error, logtype_afpd, "ea_openat: cant chdir back, exiting");
+            exit(EXITERR_SYS);
+        }
     }
 
+
+exit:
+    if (cwdfd != -1)
+        close(cwdfd);
+
     return ret;
+
 }
 
 /*
@@ -786,7 +866,7 @@ exit:
  * Flushes and then closes and frees all resouces held by ea handle.
  * Pack data in ea into ea_data, then write ea_data to disk
  */
-static int ea_close(struct ea * restrict ea)
+int ea_close(struct ea * restrict ea)
 {
     int ret = 0; 
     unsigned int count = 0;
@@ -808,9 +888,9 @@ static int ea_close(struct ea * restrict ea)
         } else {
             if (ea->ea_count == 0) {
                 /* Check if EA header exists and remove it */
-                eaname = ea_path(ea, NULL);
-                if ((stat(eaname, &st)) == 0) {
-                    if ((unlink(eaname)) != 0) {
+                eaname = ea_path(ea, NULL, 0);
+                if ((lstatat(ea->dirfd, eaname, &st)) == 0) {
+                    if ((netatalk_unlinkat(ea->dirfd, eaname)) != 0) {
                         LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
                             eaname, strerror(errno));
                         ret = -1;
@@ -915,8 +995,12 @@ int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
     LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
 
     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
-        LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
-        return AFPERR_MISC;
+        if (errno != ENOENT)
+            LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
+
+        memset(rbuf, 0, 4);
+        *rbuflen += 4;
+        return ret;
     }
 
     while (count < ea.ea_count) {
@@ -974,13 +1058,16 @@ int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
     LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
 
     if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
-        LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
-        return AFPERR_MISC;
+        if (errno != ENOENT)
+            LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
+        memset(rbuf, 0, 4);
+        *rbuflen += 4;
+        return ret;
     }
 
     while (count < ea.ea_count) {
         if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
-            if ( (eafile = ea_path(&ea, attruname)) == NULL) {
+            if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) {
                 ret = AFPERR_MISC;
                 break;
             }
@@ -1221,12 +1308,13 @@ int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
 {
     unsigned int count = 0;
     int ret = AFP_OK;
+    int cwd = -1;
     struct ea ea;
 
     LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
 
     /* Open EA stuff */
-    if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
+    if ((ea_openat(vol, dirfd, file, EA_RDWR, &ea)) != 0) {
         if (errno == ENOENT)
             /* no EA files, nothing to do */
             return AFP_OK;
@@ -1236,6 +1324,13 @@ int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
         }
     }
 
+    if (dirfd != -1) {
+        if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+    }
+
     while (count < ea.ea_count) {
         if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
             ret = AFPERR_MISC;
@@ -1249,9 +1344,18 @@ int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
     /* ea_close removes the EA header file for us because all names are NULL */
     if ((ea_close(&ea)) != 0) {
         LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
-        return AFPERR_MISC;
+        ret = AFPERR_MISC;
     }
 
+    if (dirfd != -1 && fchdir(cwd) != 0) {
+        LOG(log_error, logtype_afpd, "ea_deletefile: cant chdir back. exit!");
+        exit(EXITERR_SYS);
+    }
+
+exit:
+    if (cwd != -1)
+        close(cwd);
+
     return ret;
 }
 
@@ -1271,7 +1375,7 @@ int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
             
 
     /* Open EA stuff */
-    if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
+    if ((ea_openat(vol, dirfd, src, EA_RDWR, &srcea)) != 0) {
         if (errno == ENOENT)
             /* no EA files, nothing to do */
             return AFP_OK;
@@ -1305,12 +1409,12 @@ int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
         easize = (*srcea.ea_entries)[count].ea_size;
 
         /* Build src and dst paths for rename() */
-        if ((eapath = ea_path(&srcea, eaname)) == NULL) {
+        if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
             ret = AFPERR_MISC;
             goto exit;
         }
         strcpy(srceapath, eapath);
-        if ((eapath = ea_path(&dstea, eaname)) == NULL) {
+        if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
             ret = AFPERR_MISC;
             goto exit;
         }
@@ -1336,7 +1440,7 @@ int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
         }
 
         /* Now rename the EA */
-        if ((rename( srceapath, eapath)) < 0) {
+        if ((unix_rename(dirfd, srceapath, -1, eapath)) < 0) {
             LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
                 src, dst, srceapath, eapath);
             ret = AFPERR_MISC;
@@ -1368,7 +1472,7 @@ int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
     LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
 
     /* Open EA stuff */
-    if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
+    if ((ea_openat(vol, sfd, src, EA_RDWR, &srcea)) != 0) {
         if (errno == ENOENT)
             /* no EA files, nothing to do */
             return AFP_OK;
@@ -1402,12 +1506,12 @@ int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
         easize = (*srcea.ea_entries)[count].ea_size;
 
         /* Build src and dst paths for copy_file() */
-        if ((eapath = ea_path(&srcea, eaname)) == NULL) {
+        if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
             ret = AFPERR_MISC;
             goto exit;
         }
         strcpy(srceapath, eapath);
-        if ((eapath = ea_path(&dstea, eaname)) == NULL) {
+        if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
             ret = AFPERR_MISC;
             goto exit;
         }
@@ -1424,7 +1528,7 @@ int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
         }
 
         /* Now copy the EA */
-        if ((copy_file( srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
+        if ((copy_file(sfd, srceapath, eapath, (0666 & ~vol->v_umask))) < 0) {
             LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
                 src, dst, srceapath, eapath);
             ret = AFPERR_MISC;
@@ -1460,7 +1564,7 @@ int ea_chown(VFS_FUNC_ARGS_CHOWN)
         }
     }
 
-    if ((chown(ea_path(&ea, NULL), uid, gid)) != 0) {
+    if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) {
         switch (errno) {
         case EPERM:
         case EACCES:
@@ -1473,11 +1577,11 @@ int ea_chown(VFS_FUNC_ARGS_CHOWN)
     }
 
     while (count < ea.ea_count) {
-        if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
+        if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
             ret = AFPERR_MISC;
             goto exit;
         }
-        if ((chown(eaname, uid, gid)) != 0) {
+        if ((lchown(eaname, uid, gid)) != 0) {
             switch (errno) {
             case EPERM:
             case EACCES:
@@ -1521,8 +1625,8 @@ int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
     }
 
     /* Set mode on EA header file */
-    if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
-        LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL), strerror(errno));
+    if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
+        LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
         switch (errno) {
         case EPERM:
         case EACCES:
@@ -1536,7 +1640,7 @@ int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
 
     /* Set mode on EA files */
     while (count < ea.ea_count) {
-        if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
+        if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
             ret = AFPERR_MISC;
             goto exit;
         }
@@ -1597,8 +1701,8 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
     }
 
     /* Set mode on EA header */
-    if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
-        LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL), strerror(errno));
+    if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
+        LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
         switch (errno) {
         case EPERM:
         case EACCES:
@@ -1623,7 +1727,7 @@ int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
             LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
             eaname = eaname_safe;
         }
-        if ((eaname = ea_path(&ea, eaname)) == NULL) {
+        if ((eaname = ea_path(&ea, eaname, 1)) == NULL) {
             ret = AFPERR_MISC;
             goto exit;
         }