/*
- $Id: ea.c,v 1.3 2009-10-14 15:04:01 franklahm Exp $
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
GNU General Public License for more details.
*/
-/* According to man fsattr.5 we must define _ATFILE_SOURCE */
-#ifdef HAVE_SOLARIS_EAS
-#define _ATFILE_SOURCE
-#endif
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#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 | CONV_ALLOW_COLON;
+
+ 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
*
*/
static int unpack_header(struct ea * restrict ea)
{
- int ret = 0, count = 0;
+ int ret = 0;
+ unsigned int count = 0;
uint32_t uint32;
char *buf;
*/
static int pack_header(struct ea * restrict ea)
{
- int count = 0, eacount = 0;
+ unsigned int count = 0, eacount = 0;
uint16_t uint16;
uint32_t uint32;
size_t bufsize = EA_HEADER_SIZE;
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
- *
- * 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->vfs->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);
- strlcat(pathbuf, eaname, MAXPATHLEN + 1);
- }
-
- return pathbuf;
-}
-
/*
* Function: ea_addentry
*
size_t attrsize,
int bitmap)
{
- int count = 0;
+ int ea_existed = 0;
+ unsigned 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);
+ ea_existed = 1;
+ LOG(log_debug, logtype_afpd, "ea_addentry('%s', bitmap:0x%x): exists", attruname, bitmap);
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;
+ (*(ea->ea_entries))[count].ea_size = attrsize;
+ return 0;
}
count++;
}
}
+ if ((bitmap & kXAttrReplace) && ! ea_existed)
+ /* replace was requested, but EA didn't exist */
+ return -1;
+
if (ea->ea_count == 0) {
ea->ea_entries = malloc(sizeof(struct ea_entry));
if ( ! ea->ea_entries) {
LOG(log_error, logtype_afpd, "ea_addentry: OOM");
return -1;
}
- } else {
+ } else if (! ea_existed) {
tmprealloc = realloc(ea->ea_entries, sizeof(struct ea_entry) * (ea->ea_count + 1));
if ( ! tmprealloc) {
LOG(log_error, logtype_afpd, "ea_addentry: OOM");
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, 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
*
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, 1)) == 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) {
goto exit;
}
- if ((write(fd, ibuf, attrsize)) != attrsize) {
- LOG(log_error, logtype_afpd, "write_ea: short write: %s", eaname);
+ if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) {
+ LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno));
ret = -1;
goto 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",
+ attruname);
+ return -1;
+ }
+
+ while (count < ea->ea_count) {
+ /* search matching EA */
+ if ((*ea->ea_entries)[count].ea_name &&
+ 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
*
*
* 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;
- eafile = ea_path(ea, eaname);
+ if ((eafile = ea_path(ea, eaname, 1)) == 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 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
*
* 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:
*
* 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;
memset(ea, 0, sizeof(struct ea));
ea->vol = vol; /* ea_close needs it */
-
ea->ea_flags = eaflags;
- if (S_ISDIR(st.st_mode))
+ 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;
if ( ! (ea->filename = strdup(uname))) {
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 */
if ( ! (eaflags & EA_CREATE)) {
/* creation was not requested, so return with error */
- ret = -1;
+ ret = -2;
goto exit;
}
/* 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) {
/* 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;
}
}
/* read it */
- if ((read(ea->ea_fd, ea->ea_data, ea->ea_size)) != ea->ea_size) {
+ if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
ret = -1;
goto exit;
}
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;
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;
+
}
/*
* 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, count = 0;
+ int ret = 0;
+ unsigned int count = 0;
char *eaname;
struct stat st;
} 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 ((statat(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;
goto exit;
}
- if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != ea->ea_size) {
+ if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
ret = -1;
}
*
* 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;
+ int ret = AFPERR_MISC;
+ unsigned int count = 0;
uint32_t uint32;
struct ea ea;
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) {
*
* 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;
+ int ret = AFPERR_MISC, fd = -1;
+ unsigned int count = 0;
uint32_t uint32;
size_t toread;
struct ea ea;
+ char *eafile;
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 ((fd = open(ea_path(&ea, attruname), O_RDONLY)) == -1) {
+ if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) {
+ ret = AFPERR_MISC;
+ break;
+ }
+
+ if ((fd = open(eafile, O_RDONLY)) == -1) {
+ LOG(log_error, logtype_afpd, "get_eacontent('%s'): open error: %s", uname, strerror(errno));
ret = AFPERR_MISC;
break;
}
rbuf += 4;
*rbuflen += 4;
- if ((read(fd, rbuf, toread)) != toread) {
+ if (read(fd, rbuf, toread) != (ssize_t)toread) {
LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
+ close(fd);
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;
+ unsigned int count = 0;
+ int attrbuflen = *buflen, ret = AFP_OK, len;
char *buf = attrnamebuf;
struct ea ea;
* Copies names of all EAs of uname as consecutive C strings into rbuf.
* Increments *rbuflen accordingly.
*/
-int set_ea(const struct vol * restrict vol,
- const char * restrict uname,
- const char * restrict attruname,
- const char * restrict ibuf,
- size_t attrsize,
- int oflag)
+int set_ea(VFS_FUNC_ARGS_EA_SET)
{
int ret = AFP_OK;
struct ea ea;
*
* Removes EA attruname from file uname.
*/
-int remove_ea(const struct vol * restrict vol,
- const char * restrict uname,
- const char * restrict attruname,
- int oflag)
+int remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
{
int ret = AFP_OK;
struct ea ea;
return ret;
}
-/**********************************************************************************
- * Solaris EA VFS funcs
- **********************************************************************************/
+/******************************************************************************************
+ * EA VFS funcs that deal with file/dir cp/mv/rm
+ ******************************************************************************************/
-/*
- * Function: sol_get_easize
- *
- * Purpose: get size of an EA on Solaris native EA
- *
- * Arguments:
- *
- * vol (r) current volume
- * rbuf (w) DSI reply buffer
- * rbuflen (rw) current length of data in reply buffer
- * uname (r) filename
- * oflag (r) link and create flag
- * attruname (r) name of attribute
- *
- * Returns: AFP code: AFP_OK on success or appropiate AFP error code
- *
- * Effects:
- *
- * Copies EA size into rbuf in network order. Increments *rbuflen +4.
- */
-#ifdef HAVE_SOLARIS_EAS
-int sol_get_easize(const struct vol * restrict vol,
- char * restrict rbuf,
- int * restrict rbuflen,
- const char * restrict uname,
- int oflag,
- cons char * restrict attruname)
+int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
{
- int ret, attrdirfd;
- uint32_t attrsize;
- struct stat st;
-
- LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
-
- if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
- if (errno == ELOOP) {
- /* its a symlink and client requested O_NOFOLLOW */
- LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
+ unsigned int count = 0;
+ int ret = AFP_OK;
+ int cwd = -1;
+ struct ea ea;
- memset(rbuf, 0, 4);
- *rbuflen += 4;
+ LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
+ /* Open EA stuff */
+ if ((ea_openat(vol, dirfd, 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;
}
- LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno));
- return AFPERR_MISC;
}
- if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
- LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
+ if (dirfd != -1) {
+ if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
}
- attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
-
- /* Start building reply packet */
- LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
+ 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++;
+ }
- /* length of attribute data */
- attrsize = htonl(attrsize);
- memcpy(rbuf, &attrsize, 4);
- *rbuflen += 4;
+ /* 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);
+ ret = AFPERR_MISC;
+ }
- ret = AFP_OK;
+ if (dirfd != -1 && fchdir(cwd) != 0) {
+ LOG(log_error, logtype_afpd, "ea_deletefile: cant chdir back. exit!");
+ exit(EXITERR_SYS);
+ }
exit:
- close(attrdirfd);
+ if (cwd != -1)
+ close(cwd);
+
return ret;
}
-#endif /* HAVE_SOLARIS_EAS */
-/*
- * Function: sol_get_eacontent
- *
- * Purpose: copy Solaris native EA into rbuf
- *
- * Arguments:
- *
- * vol (r) current volume
- * rbuf (w) DSI reply buffer
- * rbuflen (rw) current length of data in reply buffer
- * uname (r) filename
- * oflag (r) link and create flag
- * attruname (r) name of attribute
- * maxreply (r) maximum EA size as of current specs/real-life
- *
- * Returns: AFP code: AFP_OK on success or appropiate AFP error code
- *
- * Effects:
- *
- * Copies EA into rbuf. Increments *rbuflen accordingly.
- */
-#ifdef HAVE_SOLARIS_EAS
-int sol_get_eacontent(const struct vol * restrict vol,
- char * restrict rbuf,
- int * restrict rbuflen,
- const char * restrict uname,
- int oflag,
- char * restrict attruname,
- int maxreply)
+int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
{
- int ret, attrdirfd;
- size_t toread, okread = 0, len;
- char *datalength;
- struct stat st;
-
- if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
- if (errno == ELOOP) {
- /* its a symlink and client requested O_NOFOLLOW */
- LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
+ unsigned 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;
- memset(rbuf, 0, 4);
- *rbuflen += 4;
+ LOG(log_debug, logtype_afpd, "ea_renamefile('%s'/'%s')", src, dst);
+
+ /* Open EA stuff */
+ if ((ea_openat(vol, dirfd, 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;
}
- LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
- return AFPERR_MISC;
}
- if ( -1 == (fstat(attrdirfd, &st))) {
- LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
+ 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;
+ }
+ }
}
- /* Start building reply packet */
-
- maxreply -= MAX_REPLY_EXTRA_BYTES;
- if (maxreply > MAX_EA_SIZE)
- maxreply = MAX_EA_SIZE;
-
- /* But never send more than the client requested */
- toread = (maxreply < st.st_size) ? maxreply : st.st_size;
-
- LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
-
- /* remember where we must store length of attribute data in rbuf */
- datalength = rbuf;
- rbuf += 4;
- *rbuflen += 4;
+ /* 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;
- while (1) {
- len = read(attrdirfd, rbuf, toread);
- if (len == -1) {
- LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno));
+ /* Build src and dst paths for rename() */
+ if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
ret = AFPERR_MISC;
goto exit;
}
- okread += len;
- rbuf += len;
- *rbuflen += len;
- if ((len == 0) || (okread == toread))
- break;
- }
-
- okread = htonl((uint32_t)okread);
- memcpy(datalength, &okread, 4);
-
- ret = AFP_OK;
-
-exit:
- close(attrdirfd);
- return ret;
-}
-#endif /* HAVE_SOLARIS_EAS */
-
-/*
- * Function: sol_list_eas
- *
- * Purpose: copy names of Solaris native EA into attrnamebuf
- *
- * Arguments:
- *
- * vol (r) current volume
- * attrnamebuf (w) store names a consecutive C strings here
- * buflen (rw) length of names in attrnamebuf
- * uname (r) filename
- * oflag (r) link and create flag
- *
- * Returns: AFP code: AFP_OK on success or appropiate AFP error code
- *
- * Effects:
- *
- * Copies names of all EAs of uname as consecutive C strings into rbuf.
- * Increments *rbuflen accordingly.
- */
-#ifdef HAVE_SOLARIS_EAS
-int sol_list_eas(const struct vol * restrict vol,
- char * restrict attrnamebuf,
- int * restrict buflen,
- const char * restrict uname,
- int oflag)
-{
- int ret, attrbuflen = *buflen, len, attrdirfd = 0;
- struct dirent *dp;
- DIR *dirp = NULL;
-
- /* Now list file attribute dir */
- if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
- if (errno == ELOOP) {
- /* its a symlink and client requested O_NOFOLLOW */
- ret = AFPERR_BADTYPE;
+ strcpy(srceapath, eapath);
+ if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
+ ret = AFPERR_MISC;
goto exit;
}
- LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
- }
-
- if (NULL == (dirp = fdopendir(attrdirfd))) {
- LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
- }
- while ((dp = readdir(dirp))) {
- /* check if its "." or ".." */
- if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
- (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
- continue;
-
- len = strlen(dp->d_name);
+ LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
+ src, dst, srceapath, eapath);
- /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
- if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
+ /* 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;
}
- if (len == 255)
- /* convert_string didn't 0-terminate */
- attrnamebuf[attrbuflen + 255] = 0;
- LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_name);
+ /* 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;
+ }
- attrbuflen += len + 1;
- if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
- /* Next EA name could overflow, so bail out with error.
- FIXME: evantually malloc/memcpy/realloc whatever.
- Is it worth it ? */
- LOG(log_warning, logtype_afpd, "sol_list_extattr(%s): running out of buffer for EA names", uname);
+ /* Now rename the EA */
+ 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;
goto exit;
}
+
+ count++;
}
- ret = AFP_OK;
exit:
- if (dirp)
- closedir(dirp);
-
- if (attrdirfd > 0)
- close(attrdirfd);
-
- *buflen = attrbuflen;
- return ret;
+ ea_close(&srcea);
+ ea_close(&dstea);
+ return ret;
}
-#endif /* HAVE_SOLARIS_EAS */
-/*
- * Function: sol_set_ea
- *
- * Purpose: set a Solaris native EA
- *
- * Arguments:
- *
- * vol (r) current volume
- * uname (r) filename
- * attruname (r) EA name
- * ibuf (r) buffer with EA content
- * attrsize (r) length EA in ibuf
- * oflag (r) link and create flag
- *
- * Returns: AFP code: AFP_OK on success or appropiate AFP error code
- *
- * Effects:
- *
- * Copies names of all EAs of uname as consecutive C strings into rbuf.
- * Increments *rbuflen accordingly.
- */
-#ifdef HAVE_SOLARIS_EAS
-int sol_set_ea(const struct vol * restrict vol,
- const char * restrict u_name,
- const char * restrict attruname,
- const char * restrict ibuf,
- size_t attrsize,
- int oflag)
+int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
{
- int attrdirfd;
+ unsigned 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);
- if ( -1 == (attrdirfd = attropen(u_name, attruname, oflag, 0666))) {
- if (errno == ELOOP) {
- /* its a symlink and client requested O_NOFOLLOW */
- LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
+ /* Open EA stuff */
+ if ((ea_openat(vol, sfd, 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;
}
- LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
- return AFPERR_MISC;
}
- if ((write(attrdirfd, ibuf, attrsize)) != attrsize) {
- LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
- 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;
+ }
+ }
}
- return AFP_OK;
-}
-#endif /* HAVE_SOLARIS_EAS */
+ /* 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;
-/*
- * Function: sol_remove_ea
- *
- * Purpose: remove a Solaris native EA
- *
- * Arguments:
- *
- * vol (r) current volume
- * uname (r) filename
- * attruname (r) EA name
- * oflag (r) link and create flag
- *
- * Returns: AFP code: AFP_OK on success or appropiate AFP error code
- *
- * Effects:
- *
- * Removes EA attruname from file uname.
- */
-#ifdef HAVE_SOLARIS_EAS
-int sol_remove_ea(const struct vol * restrict vol,
- const char * restrict uname,
- const char * restrict attruname,
- int oflag)
-{
- int attrdirfd;
+ /* Build src and dst paths for copy_file() */
+ if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ strcpy(srceapath, eapath);
+ if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
- if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) {
- switch (errno) {
- case ELOOP:
- /* its a symlink and client requested O_NOFOLLOW */
- LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
- return AFP_OK;
- case EACCES:
- LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
- return AFPERR_ACCESS;
- default:
- LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
- return AFPERR_MISC;
+ 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;
}
- }
- if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
- if (errno == EACCES) {
- LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
- return AFPERR_ACCESS;
+ /* Now copy the EA */
+ 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;
+ goto exit;
}
- LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
- return AFPERR_MISC;
+
+ count++;
}
+exit:
+ ea_close(&srcea);
+ ea_close(&dstea);
+ return ret;
}
-#endif /* HAVE_SOLARIS_EAS */
-
-/******************************************************************************************
- * EA VFS funcs that deal with file/dir cp/mv/rm
- ******************************************************************************************/
-int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
+int ea_chown(VFS_FUNC_ARGS_CHOWN)
{
- LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
- int count = 0, ret = AFP_OK;
+ unsigned int count = 0;
+ int ret = AFP_OK;
+ char *eaname;
struct ea ea;
+ LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
/* Open EA stuff */
- if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
+ 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_deletefile('%s'): error calling ea_open", file);
+ LOG(log_error, logtype_afpd, "ea_chown('%s'): error calling ea_open", path);
return AFPERR_MISC;
}
}
+ if ((ochown(ea_path(&ea, NULL, 0), uid, gid, vol_syml_opt(vol))) != 0) {
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = AFPERR_ACCESS;
+ goto exit;
+ default:
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ }
+
while (count < ea.ea_count) {
- if ((delete_ea_file(&ea, (*ea.ea_entries)[count].ea_name)) != 0) {
+ if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
ret = AFPERR_MISC;
+ goto exit;
+ }
+ if ((ochown(eaname, uid, gid, vol_syml_opt(vol))) != 0) {
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = AFPERR_ACCESS;
+ goto exit;
+ default:
+ ret = AFPERR_MISC;
+ goto exit;
+ }
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 */
+exit:
if ((ea_close(&ea)) != 0) {
- LOG(log_error, logtype_afpd, "ea_deletefile('%s'): error closing ea handle", file);
+ LOG(log_error, logtype_afpd, "ea_chown('%s'): error closing ea handle", path);
return AFPERR_MISC;
}
return ret;
}
-int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
+int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
{
- 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);
-
+ unsigned int count = 0;
+ int ret = AFP_OK;
+ const char *eaname;
+ struct ea ea;
+ LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
/* Open EA stuff */
- if ((ea_open(vol, src, EA_RDWR, &srcea)) != 0) {
+ if ((ea_open(vol, name, EA_RDWR, &ea)) != 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);
+ else
return AFPERR_MISC;
+ }
+
+ /* Set mode on EA header file */
+ if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = AFPERR_ACCESS;
+ goto exit;
+ default:
+ ret = AFPERR_MISC;
+ goto exit;
}
}
- 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;
+ /* Set mode on EA files */
+ while (count < ea.ea_count) {
+ if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
+ ret = AFPERR_MISC;
+ goto exit;
+ }
+ if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 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;
- }
- ad_close(&ad, ADFLAGS_HF);
- if ((ea_open(vol, dst, EA_RDWR | EA_CREATE, &dstea)) != 0) {
+ default:
ret = AFPERR_MISC;
goto exit;
}
+ continue;
}
+
+ count++;
}
- /* 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;
+exit:
+ if ((ea_close(&ea)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): error closing ea handle", name);
+ return AFPERR_MISC;
+ }
- /* Build src and dst paths for rename() */
- eapath = ea_path(&srcea, eaname);
- strcpy(srceapath, eapath);
- eapath = ea_path(&dstea, eaname);
+ return ret;
+}
- LOG(log_maxdebug, logtype_afpd, "ea_renamefile('%s/%s'): moving EA '%s' to '%s'",
- src, dst, srceapath, eapath);
+int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
+{
- /* 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);
+ int ret = AFP_OK;
+ unsigned int count = 0;
+ uid_t uid;
+ const char *eaname;
+ const char *eaname_safe = NULL;
+ struct ea ea;
+
+ LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
+ /* .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));
+ return AFPERR_MISC;
+ }
+
+ /* Open EA stuff */
+ if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
+ /* ENOENT --> no EA files, nothing to do */
+ if (errno != ENOENT)
ret = AFPERR_MISC;
- goto exit;
+ if (seteuid(uid) < 0) {
+ LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
+ exit(EXITERR_SYS);
}
+ return ret;
+ }
- /* 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);
+ /* Set mode on EA header */
+ if ((setfilmode(vol, ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
+ switch (errno) {
+ case EPERM:
+ case EACCES:
+ ret = AFPERR_ACCESS;
+ goto exit;
+ default:
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);
+ /* Set mode on EA files */
+ 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, 1)) == NULL) {
ret = AFPERR_MISC;
goto exit;
}
+ if ((setfilmode(vol, eaname, ea_mode(mode), NULL)) != 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:
- ea_close(&srcea);
- ea_close(&dstea);
- return ret;
+ 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;
}