/*
- $Id: ea.c,v 1.1 2009-10-02 09:32:41 franklahm Exp $
+ $Id: ea.c,v 1.9 2009-10-22 12:35:39 franklahm Exp $
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
#include <atalk/volume.h>
#include <atalk/vfs.h>
#include <atalk/util.h>
+#include <atalk/unix.h>
/*
* Store Extended Attributes inside .AppleDouble folders as follows:
* - store EAs in files "fileWithEAs::EA::testEA1" and "fileWithEAs::EA::testEA2"
*/
+/*
+ * Build mode for EA header from file mode
+ */
+static inline mode_t ea_header_mode(mode_t mode)
+{
+ /* Same as ad_hf_mode(mode) */
+ mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
+ /* Owner must be able to open, read and w-lock it, in order to chmod from eg 0000 -> 0xxxx*/
+ mode |= S_IRUSR | S_IWUSR;
+ return mode;
+}
+
+/*
+ * Build mode for EA file from file mode
+ */
+static inline mode_t ea_mode(mode_t mode)
+{
+ /* Same as ad_hf_mode(mode) */
+ mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
+ return mode;
+}
+
+/*
+ Taken form afpd/desktop.c
+*/
+static char *mtoupath(const struct vol *vol, const char *mpath)
+{
+ static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
+ const char *m;
+ char *u;
+ size_t inplen;
+ size_t outlen;
+ uint16_t flags = CONV_ESCAPEHEX;
+
+ if (!mpath)
+ return NULL;
+
+ if ( *mpath == '\0' ) {
+ return( "." );
+ }
+
+ m = mpath;
+ u = upath;
+
+ inplen = strlen(m);
+ outlen = MAXPATHLEN;
+
+ if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
+ vol->v_volcharset,
+ vol->v_maccharset,
+ m, inplen, u, outlen, &flags)) ) {
+ return NULL;
+ }
+
+ return( upath );
+}
+
+
/*
* Function: unpack_header
*
* ea (r) ea handle
* eaname (r) name of EA or NULL
*
- * Returns: pointer to name in static buffer
+ * Returns: pointer to name in static buffer, NULL on error
*
* Effects:
*
if (eaname) {
strlcat(pathbuf, "::", MAXPATHLEN + 1);
+ if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
+ return NULL;
strlcat(pathbuf, eaname, MAXPATHLEN + 1);
}
* Arguments:
*
* ea (rw) pointer to struct ea
- * uname (r) name of file
* attruname (r) name of EA
* attrsize (r) size of ea
+ * bitmap (r) bitmap from FP func
*
* Returns: new number of EA entries, -1 on error
*
* Otherwise realloc and put entry at the end. Increments ea->ea_count.
*/
static int ea_addentry(struct ea * restrict ea,
- const char * restrict uname,
const char * restrict attruname,
- size_t attrsize)
+ size_t attrsize,
+ int bitmap)
{
+ int count = 0;
void *tmprealloc;
+ /* First check if an EA of the requested name already exist */
+ if (ea->ea_count > 0) {
+ while (count < ea->ea_count) {
+ if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
+ LOG(log_debug, logtype_afpd, "ea_addentry('%s'): exists", attruname);
+ if (bitmap & kXAttrCreate)
+ /* its like O_CREAT|O_EXCL -> fail */
+ return -1;
+ if ( ! (bitmap & kXAttrReplace))
+ /* replace was not requested, then its an error */
+ return -1;
+ break;
+ }
+ count++;
+ }
+ }
+
if (ea->ea_count == 0) {
ea->ea_entries = malloc(sizeof(struct ea_entry));
if ( ! ea->ea_entries) {
* Arguments:
*
* ea (rw) pointer to struct ea
- * uname (r) name of EA
- * attruname (r) size of ea
+ * attruname (r) EA name
*
* Returns: new number of EA entries, -1 on error
*
* ea_close and pack_buffer must honor this.
*/
static int ea_delentry(struct ea * restrict ea,
- const char * restrict uname,
const char * restrict attruname)
{
int ret = 0, count = 0;
if (ea->ea_count == 0) {
+ LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
return -1;
}
*(uint16_t *)ptr = 0; /* count */
ea->ea_size = EA_HEADER_SIZE;
+ ea->ea_inited = EA_INITED;
exit:
if (err != 0) {
struct stat st;
char *eaname;
- eaname = ea_path(ea, attruname);
- LOG(log_maxdebug, logtype_afpd, "write_ea: ea_apth: %s", eaname);
+ if ((eaname = ea_path(ea, attruname)) == NULL) {
+ LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname);
+ return AFPERR_MISC;
+ }
+
+ LOG(log_maxdebug, logtype_afpd, "write_ea('%s')", eaname);
/* Check if it exists, remove if yes*/
if ((stat(eaname, &st)) == 0) {
}
if ((write(fd, ibuf, attrsize)) != attrsize) {
- LOG(log_error, logtype_afpd, "write_ea: short write: %s", eaname);
+ LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno));
ret = -1;
goto exit;
}
char *eafile;
struct stat st;
- eafile = ea_path(ea, eaname);
+ if ((eafile = ea_path(ea, eaname)) == NULL) {
+ LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname);
+ return -1;
+ }
/* Check if it exists, remove if yes*/
if ((stat(eafile, &st)) == 0) {
return -1;
}
- if ((stat(uname, &st)) != 0) {
- LOG(log_debug, logtype_afpd, "ea_open: cant stat: %s", uname);
- return -1;
- }
-
- /* set it all to 0 */
+ /* Set it all to 0 */
memset(ea, 0, sizeof(struct ea));
ea->vol = vol; /* ea_close needs it */
-
ea->ea_flags = eaflags;
+ /* Dont check for errors, eg when removing the file is already gone */
+ stat(uname, &st);
if (S_ISDIR(st.st_mode))
ea->ea_flags |= EA_DIR;
/* Now lock, open and read header file from disk */
if ((ea->ea_fd = open(eaname, (ea->ea_flags & EA_RDWR) ? O_RDWR : O_RDONLY)) == -1) {
- LOG(log_error, logtype_afpd, "ea_open: error on open for header: %s", eaname);
+ LOG(log_error, logtype_afpd, "ea_open('%s'): error: %s", eaname, strerror(errno));
ret = -1;
goto exit;
}
}
exit:
- if (ret != 0) {
+ if (ret == 0) {
+ ea->ea_inited = EA_INITED;
+ } else {
if (ea->ea_data) {
free(ea->ea_data);
ea->ea_data = NULL;
ea->ea_fd = -1;
}
}
+
return ret;
}
LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
+ if (ea->ea_inited != EA_INITED) {
+ LOG(log_warning, logtype_afpd, "ea_close('%s'): non initialized ea", ea->filename);
+ return 0;
+ }
+
/* pack header and write it to disk if it was opened EA_RDWR*/
if (ea->ea_flags & EA_RDWR) {
if ((pack_header(ea)) != 0) {
*
* Copies EA size into rbuf in network order. Increments *rbuflen +4.
*/
-int get_easize(const struct vol * restrict vol,
- char * restrict rbuf,
- int * restrict rbuflen,
- const char * restrict uname,
- int oflag,
- const char * restrict attruname)
+int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
{
int ret = AFPERR_MISC, count = 0;
uint32_t uint32;
*
* Copies EA into rbuf. Increments *rbuflen accordingly.
*/
-int get_eacontent(const struct vol * restrict vol,
- char * restrict rbuf,
- int * restrict rbuflen,
- const char * restrict uname,
- int oflag,
- const char * restrict attruname,
- int maxreply)
+int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
{
int ret = AFPERR_MISC, count = 0, fd = -1;
uint32_t uint32;
size_t toread;
struct ea ea;
+ char *eafile;
LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
while (count < ea.ea_count) {
if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
- if ((fd = open(ea_path(&ea, attruname), O_RDONLY)) == -1) {
+ if ( (eafile = ea_path(&ea, attruname)) == NULL) {
+ ret = AFPERR_MISC;
+ break;
+ }
+
+ if ((fd = open(eafile, O_RDONLY)) == -1) {
ret = AFPERR_MISC;
break;
}
* Copies names of all EAs of uname as consecutive C strings into rbuf.
* Increments *buflen accordingly.
*/
-int list_eas(const struct vol * restrict vol,
- char * restrict attrnamebuf,
- int * restrict buflen,
- const char * restrict uname,
- int oflag)
+int list_eas(VFS_FUNC_ARGS_EA_LIST)
{
int count = 0, attrbuflen = *buflen, ret = AFP_OK, len;
char *buf = attrnamebuf;
}
exit:
- *buflen += attrbuflen;
+ *buflen = attrbuflen;
if ((ea_close(&ea)) != 0) {
LOG(log_error, logtype_afpd, "list_eas: error closing ea handle for file: %s", uname);
return AFPERR_MISC;
}
- if ((ea_addentry(&ea, uname, attruname, attrsize)) == -1) {
+ if ((ea_addentry(&ea, attruname, attrsize, oflag)) == -1) {
LOG(log_error, logtype_afpd, "set_ea('%s'): ea_addentry error", uname);
ret = AFPERR_MISC;
goto exit;
return AFPERR_MISC;
}
- if ((ea_delentry(&ea, uname, attruname)) == -1) {
+ if ((ea_delentry(&ea, attruname)) == -1) {
LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
ret = AFPERR_MISC;
goto exit;
}
#endif /* HAVE_SOLARIS_EAS */
+
+/******************************************************************************************
+ * EA VFS funcs that deal with file/dir cp/mv/rm
+ ******************************************************************************************/
+
+int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
+{
+ LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
+
+ int count = 0, ret = AFP_OK;
+ struct ea ea;
+
+ /* Open EA stuff */
+ if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
+ if (errno == ENOENT)
+ /* no EA files, nothing to do */
+ return AFP_OK;
+ else {
+ LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error calling ea_open", file);
+ return AFPERR_MISC;
+ }
+ }
+
+ while (count < ea.ea_count) {
+ if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
+ ret = AFPERR_MISC;
+ continue;
+ }
+ free((*ea.ea_entries)[count].ea_name);
+ (*ea.ea_entries)[count].ea_name = NULL;
+ count++;
+ }
+
+ /* 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;
+ }
+
+ return ret;
+}
+
+int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
+{
+ int count = 0;
+ int ret = AFP_OK;
+ size_t easize;
+ char srceapath[ MAXPATHLEN + 1];
+ char *eapath;
+ char *eaname;
+ struct ea srcea;
+ struct ea dstea;
+ struct adouble ad;
+
+ LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
+
+
+ /* Open EA stuff */
+ if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
+ if (errno == ENOENT)
+ /* no EA files, nothing to do */
+ return AFP_OK;
+ else {
+ LOG(log_error, logtype_afpd, "ea_renamefile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
+ return AFPERR_MISC;
+ }
+ }
+
+ if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
+ if (errno == ENOENT) {
+ /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
+ ad_init(&ad, vol->v_adouble, vol->v_ad_options);
+ if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): ad_open error: '%s'", src, dst, dst);
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ ad_close(&ad, ADFLAGS_HF);
+ if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ }
+ }
+
+ /* Loop through all EAs: */
+ while (count < srcea.ea_count) {
+ /* Move EA */
+ eaname = (*srcea.ea_entries)[count].ea_name;
+ easize = (*srcea.ea_entries)[count].ea_size;
+
+ /* Build src and dst paths for rename() */
+ if ((eapath = ea_path(&srcea, eaname)) == NULL) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ strcpy(srceapath, eapath);
+ if ((eapath = ea_path(&dstea, eaname)) == NULL) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
+ src, dst, srceapath, eapath);
+
+ /* Add EA to dstea */
+ if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
+ LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
+ src, dst, srceapath, eapath);
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ /* Remove EA entry from srcea */
+ if ((ea_delentry(&srcea, eaname)) == -1) {
+ LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
+ src, dst, srceapath, eapath);
+ ea_delentry(&dstea, eaname);
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ /* Now rename the EA */
+ if ((rename( srceapath, eapath)) < 0) {
+ LOG(log_error, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
+ src, dst, srceapath, eapath);
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ count++;
+ }
+
+
+exit:
+ ea_close(&srcea);
+ ea_close(&dstea);
+ return ret;
+}
+
+int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
+{
+ int count = 0;
+ int ret = AFP_OK;
+ size_t easize;
+ char srceapath[ MAXPATHLEN + 1];
+ char *eapath;
+ char *eaname;
+ struct ea srcea;
+ struct ea dstea;
+ struct adouble ad;
+
+ LOG(log_debug, logtype_afpd, "ea_copyfile('%s'/'%s')", src, dst);
+
+ /* Open EA stuff */
+ if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
+ if (errno == ENOENT)
+ /* no EA files, nothing to do */
+ return AFP_OK;
+ else {
+ LOG(log_error, logtype_afpd, "ea_copyfile('%s'/'%s'): ea_open error: '%s'", src, dst, src);
+ return AFPERR_MISC;
+ }
+ }
+
+ if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
+ if (errno == ENOENT) {
+ /* Possibly the .AppleDouble folder didn't exist, we create it and try again */
+ ad_init(&ad, vol->v_adouble, vol->v_ad_options);
+ if ((ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ad_open error: '%s'", src, dst, dst);
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ ad_close(&ad, ADFLAGS_HF);
+ if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ }
+ }
+
+ /* Loop through all EAs: */
+ while (count < srcea.ea_count) {
+ /* Copy EA */
+ eaname = (*srcea.ea_entries)[count].ea_name;
+ easize = (*srcea.ea_entries)[count].ea_size;
+
+ /* Build src and dst paths for copy_file() */
+ if ((eapath = ea_path(&srcea, eaname)) == NULL) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ strcpy(srceapath, eapath);
+ if ((eapath = ea_path(&dstea, eaname)) == NULL) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ LOG(log_maxdebug, logtype_afpd, "ea_copyfile('%s/%s'): copying EA '%s' to '%s'",
+ src, dst, srceapath, eapath);
+
+ /* Add EA to dstea */
+ if ((ea_addentry(&dstea, eaname, easize, 0)) == -1) {
+ LOG(log_error, logtype_afpd, "ea_copyfile('%s/%s'): ea_addentry('%s') error",
+ src, dst, eaname);
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ /* Now copy the EA */
+ if ((copy_file( 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;
+ goto exit;
+ }
+
+ count++;
+ }
+
+exit:
+ ea_close(&srcea);
+ ea_close(&dstea);
+ return ret;
+}
+
+int ea_chown(VFS_FUNC_ARGS_CHOWN)
+{
+ LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
+
+ int count = 0, ret = AFP_OK;
+ char *eaname;
+ struct ea ea;
+
+ /* Open EA stuff */
+ if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
+ if (errno == ENOENT)
+ /* no EA files, nothing to do */
+ return AFP_OK;
+ else {
+ LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
+ return AFPERR_MISC;
+ }
+ }
+
+ if ((chown(ea_path(&ea, NULL), uid, gid)) != 0) {
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = AFPERR_ACCESS;
+ goto exit;
+ default:
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ }
+
+ while (count < ea.ea_count) {
+ if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ if ((chown(eaname, uid, gid)) != 0) {
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = AFPERR_ACCESS;
+ goto exit;
+ default:
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ continue;
+ }
+
+ count++;
+ }
+
+exit:
+ if ((ea_close(&ea)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);
+ return AFPERR_MISC;
+ }
+
+ return ret;
+}
+
+int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
+{
+ LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
+
+ int count = 0, ret = AFP_OK;
+ const char *eaname;
+ struct ea ea;
+
+ /* Open EA stuff */
+ if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
+ if (errno == ENOENT)
+ /* no EA files, nothing to do */
+ return AFP_OK;
+ else
+ return AFPERR_MISC;
+ }
+
+ /* 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));
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = AFPERR_ACCESS;
+ goto exit;
+ default:
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ }
+
+ /* Set mode on EA files */
+ while (count < ea.ea_count) {
+ if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", eaname, strerror(errno));
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = AFPERR_ACCESS;
+ goto exit;
+ default:
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ continue;
+ }
+
+ count++;
+ }
+
+exit:
+ if ((ea_close(&ea)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name);
+ return AFPERR_MISC;
+ }
+
+ return ret;
+}
+
+int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
+{
+ LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
+
+ int ret = AFP_OK;
+ uid_t uid;
+ const char *eaname;
+ const char *eaname_safe = NULL;
+ struct ea ea;
+
+ /* .AppleDouble already might be inaccesible, so we must run as id 0 */
+ uid = geteuid();
+ if (seteuid(0)) {
+ LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno));
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+
+ /* Open EA stuff */
+ if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
+ if (errno == ENOENT)
+ /* no EA files, nothing to do */
+ return AFP_OK;
+ else
+ return AFPERR_MISC;
+ }
+
+ /* 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));
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = AFPERR_ACCESS;
+ goto exit;
+ default:
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ }
+
+ /* Set mode on EA files */
+ int count = 0;
+ while (count < ea.ea_count) {
+ eaname = (*ea.ea_entries)[count].ea_name;
+ /*
+ * Be careful with EA names from the EA header!
+ * Eg NFS users might have access to them, can inject paths using ../ or /.....
+ * FIXME:
+ * Until the EA code escapes / in EA name requests from the client, these therefor wont work.
+ */
+ if ((eaname_safe = strrchr(eaname, '/'))) {
+ LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
+ eaname = eaname_safe;
+ }
+ if ((eaname = ea_path(&ea, eaname)) == NULL) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ if ((setfilmode(eaname, ea_mode(mode), NULL, vol->v_umask)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", eaname, strerror(errno));
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = AFPERR_ACCESS;
+ goto exit;
+ default:
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ continue;
+ }
+
+ count++;
+ }
+
+exit:
+ if (seteuid(uid) < 0) {
+ LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
+ exit(EXITERR_SYS);
+ }
+
+ if ((ea_close(&ea)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): error closing ea handle", name);
+ return AFPERR_MISC;
+ }
+
+ return ret;
+}