]> arthur.barton.de Git - netatalk.git/commitdiff
Cleaunup and refactor ad_open
authorFrank Lahm <franklahm@googlemail.com>
Thu, 25 Nov 2010 13:27:05 +0000 (14:27 +0100)
committerRalph <ralph@Inti.local>
Thu, 25 Nov 2010 13:27:43 +0000 (14:27 +0100)
configure.in
include/atalk/adouble.h
include/atalk/ea.h
libatalk/adouble/ad_open.c
libatalk/vfs/Makefile.am
libatalk/vfs/ea.c [deleted file]
libatalk/vfs/ea_ad.c [new file with mode: 0644]
libatalk/vfs/ea_sys.c
libatalk/vfs/extattr.c [new file with mode: 0644]
libatalk/vfs/sys_ea.c [deleted file]
sys/netatalk/at.h

index 2895163aa698c5041ae376337e3d2f941de6eb96..3d25dc56d83290fc66e2d005b59a64b777e08088 100644 (file)
@@ -23,9 +23,6 @@ AC_PROG_GREP
 AC_PROG_PS
 AM_PROG_CC_C_O
 
-dnl Request SUSv4 standard interfaces
-CFLAGS="$CFLAGS -D_XOPEN_SOURCE=700"
-
 dnl Checks for header files.
 AC_HEADER_STDC
 AC_HEADER_SYS_WAIT
@@ -1264,7 +1261,8 @@ AC_CHECK_HEADER([uuid/uuid.h],
                 AC_MSG_ERROR([missing header <uuid/uuid.> from libuuid required for TimeMachine]))
 
 dnl --------------------- last minute substitutions
-
+dnl Request SUSv4 standard interfaces
+CFLAGS="$CFLAGS -D_XOPEN_SOURCE=700"
 AC_SUBST(LIBS)
 AC_SUBST(CFLAGS)
 AC_SUBST(OVERWRITE_CONFIG)
index b125fc74784b1e86112fa2422512d664760b3c82..6e334e6bb9b2b57c5104681c63ebfc56363a409b 100644 (file)
@@ -33,6 +33,7 @@
 #include <config.h>
 #endif
 
+#include <inttypes.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -45,7 +46,6 @@
 
 /* version info */
 #define AD_VERSION2     0x00020000
-#define AD_VERSION2_OSX 0x00020001
 #define AD_VERSION2_EA  0x00020002
 #define AD_VERSION      AD_VERSION2_EA
 
 #define ADEDLEN_PRIVID          4
 
 #define ADEID_NUM_V2            13
-#define ADEID_NUM_OSX           2
 #define ADEID_NUM_EA            5
+
 #define AD_DATASZ2 (AD_HEADER_LEN + ADEDLEN_NAME + ADEDLEN_COMMENT + ADEDLEN_FILEI + \
                     ADEDLEN_FINDERI + ADEDLEN_DID + ADEDLEN_AFPFILEI + ADEDLEN_SHORTNAME + \
                     ADEDLEN_PRODOSFILEI + ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
@@ -190,23 +190,21 @@ struct adouble {
     char                *ad_m_name;   /* mac name for open fork */
     int                 ad_m_namelen;
     struct adouble_fops *ad_ops;
-    u_int16_t       ad_open_forks;      /* open forks (by others) */
+    uint16_t            ad_open_forks;      /* open forks (by others) */
 #ifdef USE_MMAPPED_HEADERS
     char                *ad_data;
 #else
-    char        ad_data[AD_DATASZ_MAX];
+    char                ad_data[AD_DATASZ_MAX];
 #endif
 };
 
 #define ADFLAGS_DF        (1<<0)
 #define ADFLAGS_HF        (1<<1)
 #define ADFLAGS_DIR       (1<<2)
-#define ADFLAGS_V1COMPAT  (1<<4)
 #define ADFLAGS_NOHF      (1<<5)  /* not an error if no ressource fork */
 #define ADFLAGS_RDONLY    (1<<6)  /* don't try readwrite */
 #define ADFLAGS_OPENFORKS (1<<7)  /* check for open fork in ad_metadata function */
 #define ADFLAGS_RF        (1<<8)
-#define ADFLAGS_MD        ADFLAGS_HF /* (1<<9) */
 #define ADFLAGS_CREATE    (1<<9)
 
 #define ADVOL_NODEV      (1 << 0)
@@ -377,9 +375,9 @@ extern int ad_metadata    (const char *, int, struct adouble *);
 extern int ad_metadataat  (int, const char *, int, struct adouble *);
 
 #define ad_open_metadata(name, flags, mode, adp)\
-   ad_open(name, ADFLAGS_MD|(flags), O_RDWR |(mode), 0666, (adp))
+   ad_open(name, ADFLAGS_HF | (flags), O_RDWR |(mode), 0666, (adp))
 
-#define ad_close_metadata(adp) ad_close( (adp), ADFLAGS_MD)
+#define ad_close_metadata(adp) ad_close( (adp), ADFLAGS_HF)
 
 /* build a resource fork mode from the data fork mode:
  * remove X mode and extend header to RW if R or W (W if R for locking),
@@ -406,66 +404,43 @@ static inline mode_t ad_hf_mode (mode_t mode)
     return mode;
 }
 
-/* ad_ea.c */
-ssize_t sys_getxattr (const char *path, const char *name, void *value, size_t size);
-ssize_t sys_lgetxattr (const char *path, const char *name, void *value, size_t size);
-ssize_t sys_fgetxattr (int filedes, const char *name, void *value, size_t size);
-ssize_t sys_listxattr (const char *path, char *list, size_t size);
-ssize_t sys_llistxattr (const char *path, char *list, size_t size);
-ssize_t sys_flistxattr (int filedes, char *list, size_t size);
-int sys_removexattr (const char *path, const char *name);
-int sys_lremovexattr (const char *path, const char *name);
-int sys_fremovexattr (int filedes, const char *name);
-int sys_setxattr (const char *path, const char *name, const void *value, size_t size, int flags);
-int sys_lsetxattr (const char *path, const char *name, const void *value, size_t size, int flags);
-int sys_fsetxattr (int filedes, const char *name, const void *value, size_t size, int flags);
-int sys_copyxattr (const char *src, const char *dst);
-
 /* ad_read.c/ad_write.c */
 extern int     sys_ftruncate(int fd, off_t length);
-
-extern ssize_t ad_read (struct adouble *, const u_int32_t,
-                            const off_t, char *, const size_t);
-extern ssize_t ad_pread (struct ad_fd *, void *, size_t, off_t);
-extern ssize_t ad_write (struct adouble *, const u_int32_t, off_t,
-                             const int, const char *, const size_t);
-extern ssize_t adf_pread  (struct ad_fd *, void *, size_t, off_t);
-extern ssize_t adf_pwrite (struct ad_fd *, const void *, size_t, off_t);
-extern int     ad_dtruncate (struct adouble *, const off_t);
-extern int     ad_rtruncate (struct adouble *, const off_t);
+extern ssize_t ad_read(struct adouble *, uint32_t, off_t, char *, size_t);
+extern ssize_t ad_pread(struct ad_fd *, void *, size_t, off_t);
+extern ssize_t ad_write(struct adouble *, uint32_t, off_t, int, const char *, size_t);
+extern ssize_t adf_pread(struct ad_fd *, void *, size_t, off_t);
+extern ssize_t adf_pwrite(struct ad_fd *, const void *, size_t, off_t);
+extern int     ad_dtruncate(struct adouble *, off_t);
+extern int     ad_rtruncate(struct adouble *, off_t);
 
 /* ad_size.c */
-extern off_t ad_size (const struct adouble *, const u_int32_t );
+extern off_t ad_size (const struct adouble *, uint32_t );
 
 /* ad_mmap.c */
-extern void *ad_mmapread (struct adouble *, const u_int32_t,
-                              const off_t, const size_t);
-extern void *ad_mmapwrite (struct adouble *, const u_int32_t,
-                               const off_t, const int, const size_t);
+extern void *ad_mmapread(struct adouble *, uint32_t, off_t, size_t);
+extern void *ad_mmapwrite(struct adouble *, uint32_t, off_t, int, size_t);
 #define ad_munmap(buf, len)  (munmap((buf), (len)))
 
 /* ad_date.c */
-extern int ad_setdate (struct adouble *, unsigned int, u_int32_t);
-extern int ad_getdate (const struct adouble *, unsigned int, u_int32_t *);
+extern int ad_setdate(struct adouble *, unsigned int, uint32_t);
+extern int ad_getdate(const struct adouble *, unsigned int, uint32_t *);
 
 /* ad_attr.c */
-extern int       ad_setattr (const struct adouble *, const u_int16_t);
-extern int       ad_getattr (const struct adouble *, u_int16_t *);
-extern int       ad_setname (struct adouble *, const char *);
-extern int       ad_setid (struct adouble *, const dev_t dev,const ino_t ino, const u_int32_t, const u_int32_t, const void *);
-extern u_int32_t ad_getid (struct adouble *, const dev_t, const ino_t, const cnid_t, const void *);
-extern u_int32_t ad_forcegetid (struct adouble *adp);
+extern int       ad_setattr(const struct adouble *, uint16_t);
+extern int       ad_getattr(const struct adouble *, uint16_t *);
+extern int       ad_setname(struct adouble *, const char *);
+extern int       ad_setid(struct adouble *, dev_t dev, ino_t ino, uint32_t, uint32_t, const void *);
+extern u_int32_t ad_getid(struct adouble *, dev_t, ino_t, cnid_t, const void *);
+extern u_int32_t ad_forcegetid(struct adouble *adp);
 
 #ifdef WITH_SENDFILE
-extern int ad_readfile_init(const struct adouble *ad, 
-                            const int eid, off_t *off,
-                            const int end);
+extern int ad_readfile_init(const struct adouble *ad, int eid, off_t *off, int end);
 #endif
 
 #if 0
 #ifdef HAVE_SENDFILE_WRITE
-extern ssize_t ad_writefile (struct adouble *, const int,
-                                 const int, off_t, const int, const size_t);
+extern ssize_t ad_writefile(struct adouble *, int, int, off_t, int, size_t);
 #endif /* HAVE_SENDFILE_WRITE */
 #endif /* 0 */
 
index c0165a1a257f210999055d8a271bc81dadf9e2a8..276e95e07bfec208001357c9fe931d7823ec9818 100644 (file)
@@ -1,5 +1,4 @@
 /*
-   $Id: ea.h,v 1.11 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
@@ -56,6 +55,22 @@ enum {
 #define XATTR_REPLACE 0x2       /* set value, fail if attr does not exist */
 #endif
 
+/****************************************************************************************
+ * Wrappers for native EA functions taken from Samba
+ ****************************************************************************************/
+ssize_t sys_getxattr (const char *path, const char *name, void *value, size_t size);
+ssize_t sys_lgetxattr (const char *path, const char *name, void *value, size_t size);
+ssize_t sys_fgetxattr (int filedes, const char *name, void *value, size_t size);
+ssize_t sys_listxattr (const char *path, char *list, size_t size);
+ssize_t sys_llistxattr (const char *path, char *list, size_t size);
+ssize_t sys_flistxattr (int filedes, char *list, size_t size);
+int sys_removexattr (const char *path, const char *name);
+int sys_lremovexattr (const char *path, const char *name);
+int sys_fremovexattr (int filedes, const char *name);
+int sys_setxattr (const char *path, const char *name, const void *value, size_t size, int flags);
+int sys_lsetxattr (const char *path, const char *name, const void *value, size_t size, int flags);
+int sys_fsetxattr (int filedes, const char *name, const void *value, size_t size, int flags);
+int sys_copyxattr (const char *src, const char *dst);
 
 /****************************************************************************************
  * Stuff for our implementation of storing EAs in files in .AppleDouble dirs
index bbd11376eacccd0027affcc881a8687db615064f..57737e700c9414ac8cee426c7467276bc93e2bb1 100644 (file)
@@ -162,13 +162,6 @@ static const struct entry entry_order2[ADEID_NUM_V2 + 1] = {
     {0, 0, 0}
 };
 
-/* OS X adouble finder info and resource fork only */
-static const struct entry entry_order_osx[ADEID_NUM_OSX + 1] = {
-    {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI},
-    {ADEID_RFORK, ADEDOFF_RFORK_OSX, ADEDLEN_INIT},
-    {0, 0, 0}
-};
-
 /* Using Extended Attributes */
 static const struct entry entry_order_ea[ADEID_NUM_EA + 1] = {
     {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI},
@@ -523,8 +516,7 @@ static int ad_header_read(struct adouble *ad, struct stat *hst)
     /* tag broken v1 headers. just assume they're all right.
      * we detect two cases: null magic/version
      *                      byte swapped magic/version
-     * XXX: in the future, you'll need the v1compat flag.
-     * (ad->ad_flags & ADFLAGS_V1COMPAT) */
+     * XXX: in the future, you'll need the v1compat flag. */
     if (!ad->ad_magic && !ad->ad_version) {
         if (!warning) {
             LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version.");
@@ -546,7 +538,7 @@ static int ad_header_read(struct adouble *ad, struct stat *hst)
 
     if ((ad->ad_magic != AD_MAGIC) ||
         ((ad->ad_version != AD_VERSION1) && (ad->ad_version != AD_VERSION2))) {
-        LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
+        LOG(log_error, logtype_default, "ad_open: can't parse AppleDouble header.");
         errno = EIO;
         return -1;
     }
@@ -564,7 +556,7 @@ static int ad_header_read(struct adouble *ad, struct stat *hst)
 
     buf += AD_HEADER_LEN;
     if (len > header_len - AD_HEADER_LEN) {
-        LOG(log_debug, logtype_default, "ad_header_read: can't read entry info.");
+        LOG(log_error, logtype_default, "ad_header_read: can't read entry info.");
         errno = EIO;
         return -1;
     }
@@ -576,13 +568,13 @@ static int ad_header_read(struct adouble *ad, struct stat *hst)
     if (!ad_getentryoff(ad, ADEID_RFORK)
         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
         ) {
-        LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset.");
+        LOG(log_error, logtype_default, "ad_header_read: problem with rfork entry offset.");
         errno = EIO;
         return -1;
     }
 
     if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
-        LOG(log_debug, logtype_default, "ad_header_read: can't read in entries.");
+        LOG(log_error, logtype_default, "ad_header_read: can't read in entries.");
         errno = EIO;
         return -1;
     }
@@ -635,98 +627,43 @@ static int ad_header_read_ea(struct adouble *ad, struct stat *hst)
     memcpy(&ad->ad_magic, buf, sizeof( ad->ad_magic ));
     memcpy(&ad->ad_version, buf + ADEDOFF_VERSION, sizeof( ad->ad_version ));
 
-    /* tag broken v1 headers. just assume they're all right.
-     * we detect two cases: null magic/version
-     *                      byte swapped magic/version
-     * XXX: in the future, you'll need the v1compat flag.
-     * (ad->ad_flags & ADFLAGS_V1COMPAT) */
-    if (!ad->ad_magic && !ad->ad_version) {
-        if (!warning) {
-            LOG(log_debug, logtype_default, "notice: fixing up null v1 magic/version.");
-            warning++;
-        }
-        ad->ad_magic = AD_MAGIC;
-        ad->ad_version = AD_VERSION1;
-
-    } else if (ad->ad_magic == AD_MAGIC && ad->ad_version == AD_VERSION1) {
-        if (!warning) {
-            LOG(log_debug, logtype_default, "notice: fixing up byte-swapped v1 magic/version.");
-            warning++;
-        }
-
-    } else {
-        ad->ad_magic = ntohl( ad->ad_magic );
-        ad->ad_version = ntohl( ad->ad_version );
-    }
-
-    if ((ad->ad_magic != AD_MAGIC) ||
-        ((ad->ad_version != AD_VERSION1) && (ad->ad_version != AD_VERSION2))) {
-        LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
-        errno = EIO;
-        return -1;
-    }
+    ad->ad_magic = ntohl( ad->ad_magic );
+    ad->ad_version = ntohl( ad->ad_version );
 
-    memcpy(ad->ad_filler, buf + ADEDOFF_FILLER, sizeof( ad->ad_filler ));
-    memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
-    nentries = ntohs( nentries );
+    if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION_EA))) {
+    LOG(log_error, logtype_default, "ad_open: can't parse AppleDouble header.");
+    errno = EIO;
+    return -1;
+}
 
-    /* read in all the entry headers. if we have more than the
-     * maximum, just hope that the rfork is specified early on. */
-    len = nentries*AD_ENTRY_LEN;
+memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
+nentries = ntohs( nentries );
 
-    if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
-        len = sizeof(ad->ad_data) - AD_HEADER_LEN;
-
-    buf += AD_HEADER_LEN;
-    if (len > header_len - AD_HEADER_LEN) {
-        LOG(log_debug, logtype_default, "ad_header_read: can't read entry info.");
-        errno = EIO;
-        return -1;
-    }
+len = nentries * AD_ENTRY_LEN;
+if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
+    len = sizeof(ad->ad_data) - AD_HEADER_LEN;
 
-    /* figure out all of the entry offsets and lengths. if we aren't
-     * able to read a resource fork entry, bail. */
-    nentries = len / AD_ENTRY_LEN;
-    parse_entries(ad, buf, nentries);
-    if (!ad_getentryoff(ad, ADEID_RFORK)
-        || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
-        ) {
-        LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset.");
-        errno = EIO;
-        return -1;
-    }
+buf += AD_HEADER_LEN;
+if (len > header_len - AD_HEADER_LEN) {
+    LOG(log_error, logtype_default, "ad_header_read: can't read entry info.");
+    errno = EIO;
+    return -1;
+}
 
-    if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
-        LOG(log_debug, logtype_default, "ad_header_read: can't read in entries.");
-        errno = EIO;
-        return -1;
-    }
+/* figure out all of the entry offsets and lengths */
+nentries = len / AD_ENTRY_LEN;
+parse_entries(ad, buf, nentries);
 
-    if (hst == NULL) {
-        hst = &st;
-        if (fstat(ad->ad_md->adf_fd, &st) < 0) {
-            return 1; /* fail silently */
-        }
+if (hst == NULL) {
+    hst = &st;
+    if (fstat(ad->ad_md->adf_fd, &st) < 0) {
+        return 1; /* fail silently */
     }
-    ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
+}
 
-    /* fix up broken dates */
-    if (ad->ad_version == AD_VERSION1) {
-        u_int32_t aint;
+ad->ad_rlen = hst->st_size - ad_getentryoff(ad, ADEID_RFORK);
 
-        /* check to see if the ad date is wrong. just see if we have
-         * a modification date in the future. */
-        if (((ad_getdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, &aint)) == 0) &&
-            (aint > TIMEWARP_DELTA + hst->st_mtime)) {
-            ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, aint - AD_DATE_DELTA);
-            ad_getdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, &aint);
-            ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, aint - AD_DATE_DELTA);
-            ad_getdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, &aint);
-            ad_setdate(ad, AD_DATE_BACKUP | AD_DATE_UNIX, aint - AD_DATE_DELTA);
-        }
-    }
-
-    return 0;
+return 0;
 }
 
 /*
@@ -788,46 +725,6 @@ static int ad_mkrf(char *path)
     return 0;
 }
 
-
-/***********************************************************************
- * OS X adouble scheme funcs
- ***********************************************************************/
-
-/*
- * Put the resource fork where it needs to be: ._name
- */
-char *ad_path_osx(const char *path, int adflags _U_)
-{
-    static char pathbuf[ MAXPATHLEN + 1];
-    char    c, *slash, buf[MAXPATHLEN + 1];
-
-    if (!strcmp(path,".")) {
-        /* fixme */
-        getcwd(buf, MAXPATHLEN);
-    }
-    else {
-        strlcpy(buf, path, MAXPATHLEN +1);
-    }
-    if (NULL != ( slash = strrchr( buf, '/' )) ) {
-        c = *++slash;
-        *slash = '\0';
-        strlcpy( pathbuf, buf, MAXPATHLEN +1);
-        *slash = c;
-    } else {
-        pathbuf[ 0 ] = '\0';
-        slash = buf;
-    }
-    strlcat( pathbuf, "._", MAXPATHLEN  +1);
-    strlcat( pathbuf, slash, MAXPATHLEN +1);
-    return pathbuf;
-}
-
-/* -------------------- */
-static int ad_mkrf_osx(char *path _U_)
-{
-    return 0;
-}
-
 /* -------------------------
  * Support inherited protection modes for AppleDouble files.  The supplied
  * mode is ANDed with the parent directory's mask value in lieu of "umask",
@@ -957,8 +854,7 @@ static int ad_mode_st(const char *path, int *mode, struct stat *stbuf)
 /* ----------------
    return access right of path parent directory
 */
-int
-ad_mode( const char *path, int mode)
+int ad_mode( const char *path, int mode)
 {
     struct stat     stbuf;
     ad_mode_st(path, &mode, &stbuf);
@@ -968,8 +864,7 @@ ad_mode( const char *path, int mode)
 /*
  * Use mkdir() with mode bits taken from ad_mode().
  */
-int
-ad_mkdir( const char *path, int mode)
+int ad_mkdir( const char *path, int mode)
 {
     int ret;
     int st_invalid;
@@ -1043,15 +938,6 @@ static int ad_header_upgrade_none(struct adouble *ad _U_, char *name _U_)
 }
 
 /* --------------------------- */
-static struct adouble_fops ad_osx = {
-    &ad_path_osx,
-    &ad_mkrf_osx,
-    &ad_rebuild_adouble_header,
-    &ad_check_size,
-    &ad_header_read,
-    &ad_header_upgrade,
-};
-
 static struct adouble_fops ad_adouble = {
     &ad_path,
     &ad_mkrf,
@@ -1074,64 +960,32 @@ void ad_init(struct adouble *ad, int flags, int options)
 {
     ad->ad_inited = 0;
     ad->ad_flags = flags;
-    if (flags == AD_VERSION2_OSX) {
-        ad->ad_ops = &ad_osx;
-        ad->ad_md = &ad->ad_resource_fork;
-    }
-    else if (flags == AD_VERSION2) {
+    if (flags == AD_VERSION2) {
         ad->ad_ops = &ad_adouble;
         ad->ad_md = &ad->ad_resource_fork;
     }
-    else {
+    else if (flags == AD_VERSION_EA) {
         ad->ad_ops = &ad_adouble_ea;
         ad->ad_md = &ad->ad_metadata_fork;
+    } else {
+        LOG(log_error, logtype_default, "ad_init: unknown AD version");
+        errno = EIO;
+        return;
     }
+
     ad->ad_options = options;
 
     ad_data_fileno(ad) = -1;
     ad_reso_fileno(ad) = -1;
     ad_meta_fileno(ad) = -1;
+
     /* following can be read even if there's no meda data. */
     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
     ad->ad_rlen = 0;
 }
 
-/*!
- * Open data-, metadata(header)- or ressource fork
- *
- * You must call ad_init() before ad_open, usually you'll just call it like this: \n
- * @code
- *      struct adoube ad;
- *      ad_init(&ad, vol->v_adouble, vol->v_ad_options);
- * @endcode
- *
- * @param path    Path to file or directory
- * @param adflags ADFLAGS_DF: open data file/fork\n
- *                ADFLAGS_HF: open header (metadata) file\n
- *                ADFLAGS_RF: open ressource fork *** FIXME: not used ?! *** \n
- *                ADFLAGS_CREATE: indicate creation\n
- *                ADFLAGS_NOHF: it's not an error if header file couldn't be created\n
- *                ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags\n
- *                ADFLAGS_NOADOUBLE: dont create adouble files if not necessary\n
- *                ADFLAGS_RDONLY: open read only\n
- *                ADFLAGS_OPENFORKS: check for open forks from other processes\n
- *                ADFLAGS_MD: alias for ADFLAGS_HF\n
- *                ADFLAGS_V1COMPAT: obsolete
- * @param oflags  flags passed through to open syscall: \n
- *                O_RDONLY: *** FIXME *** \n
- *                O_RDWR: *** FIXME *** \n
- *                O_CREAT: create fork\n
- *                O_EXCL: fail if exists with O_CREAT
- * @param mode    passed to open with O_CREAT
- * @param ad      pointer to struct adouble
- *
- * @returns 0 on success
- *
- * @note It's not possible to open the header file O_RDONLY -- the read
- *       will fail and return an error. this refcounts things now.\n
- *       metadata(ressource)-fork only gets created with O_CREAT.
- */
-int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble  *ad)
+
+static int ad_open_df(const char *path, int adflags, int oflags, int mode, struct adouble *ad)
 {
     struct stat         st_dir;
     struct stat         st_meta;
@@ -1141,89 +995,78 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
     int                 st_invalid = -1;
     int                 open_df = 0;
 
-    if (ad->ad_inited != AD_INITED) {
-        ad->ad_inited = AD_INITED;
-        ad->ad_refcount = 1;
-        ad->ad_open_forks = 0;
-        ad->ad_adflags = adflags;
-        ad->ad_resource_fork.adf_refcount = 0;
-        ad->ad_data_fork.adf_refcount = 0;
-        ad->ad_data_fork.adf_syml=0;
-    }
-    else {
-        ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
-        /* XXX not true if we have a meta data fork ? */
-        if ((ad->ad_resource_fork.adf_refcount > ad->ad_data_fork.adf_refcount))
-            ad->ad_open_forks |= ATTRBIT_ROPEN;
-    }
+    if (ad_data_fileno(ad) == -1) {
+        hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
+        admode = mode;
+        if ((oflags & O_CREAT)) {
+            st_invalid = ad_mode_st(path, &admode, &st_dir);
+            if ((ad->ad_options & ADVOL_UNIXPRIV)) {
+                admode = mode;
+            }
+        }
 
-    if ((adflags & ADFLAGS_DF)) {
-        if (ad_data_fileno(ad) == -1) {
-            hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
-            admode = mode;
-            if ((oflags & O_CREAT)) {
-                st_invalid = ad_mode_st(path, &admode, &st_dir);
-                if ((ad->ad_options & ADVOL_UNIXPRIV)) {
-                    admode = mode;
-                }
+        ad->ad_data_fork.adf_fd =open( path, hoflags | O_NOFOLLOW, admode );
+
+        if (ad->ad_data_fork.adf_fd == -1) {
+            if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
+                hoflags = oflags;
+                ad->ad_data_fork.adf_fd = open( path, hoflags | O_NOFOLLOW, admode );
             }
-                
-            ad->ad_data_fork.adf_fd =open( path, hoflags | O_NOFOLLOW, admode );
-            
-            if (ad->ad_data_fork.adf_fd == -1) {
-                if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
-                    hoflags = oflags;
-                    ad->ad_data_fork.adf_fd = open( path, hoflags | O_NOFOLLOW, admode );
-                }
-                if (ad->ad_data_fork.adf_fd == -1 && errno == OPEN_NOFOLLOW_ERRNO) {
-                    int lsz;
-
-                    ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1);
-                    lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN);
-                    if (lsz <= 0) {
-                        free(ad->ad_data_fork.adf_syml);
-                        return -1;
-                    }
-                    ad->ad_data_fork.adf_syml[lsz] = 0;
-                    ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */
+            if (ad->ad_data_fork.adf_fd == -1 && errno == OPEN_NOFOLLOW_ERRNO) {
+                int lsz;
+
+                ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1);
+                lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN);
+                if (lsz <= 0) {
+                    free(ad->ad_data_fork.adf_syml);
+                    return -1;
                 }
+                ad->ad_data_fork.adf_syml[lsz] = 0;
+                ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */
             }
+        }
 
-            if ( ad->ad_data_fork.adf_fd == -1 )
-                return -1;
+        if ( ad->ad_data_fork.adf_fd == -1 )
+            return -1;
 
-            AD_SET(ad->ad_data_fork.adf_off);
-            ad->ad_data_fork.adf_flags = hoflags;
-            if (!st_invalid) {
-                /* just created, set owner if admin (root) */
-                ad_chown(path, &st_dir);
-            }
-            adf_lock_init(&ad->ad_data_fork);
+        AD_SET(ad->ad_data_fork.adf_off);
+        ad->ad_data_fork.adf_flags = hoflags;
+        if (!st_invalid) {
+            /* just created, set owner if admin (root) */
+            ad_chown(path, &st_dir);
         }
-        else {
-            /* the file is already open... but */
-            if ((oflags & ( O_RDWR | O_WRONLY)) &&             /* we want write access */
-                !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) /* and it was denied the first time */
-            {
-                errno = EACCES;
-                return -1;
-            }
-            /* FIXME
-             * for now ad_open is never called with O_TRUNC or O_EXCL if the file is
-             * already open. Should we check for it? ie
-             * O_EXCL --> error
-             * O_TRUNC --> truncate the fork.
-             * idem for ressource fork.
-             */
+        adf_lock_init(&ad->ad_data_fork);
+    } else {
+        /* the file is already open... but */
+        if ((oflags & ( O_RDWR | O_WRONLY)) /* we want write access */
+            &&
+            !(ad->ad_data_fork.adf_flags & ( O_RDWR | O_WRONLY))) { /* and it was denied the first time */
+            errno = EACCES;
+            return -1;
         }
-        open_df = ADFLAGS_DF;
-        ad->ad_data_fork.adf_refcount++;
+        /* FIXME
+         * for now ad_open is never called with O_TRUNC or O_EXCL if the file is
+         * already open. Should we check for it? ie
+         * O_EXCL --> error
+         * O_TRUNC --> truncate the fork.
+         * idem for ressource fork.
+         */
     }
+    open_df = ADFLAGS_DF;
+    ad->ad_data_fork.adf_refcount++;
 
-    if (!(adflags & ADFLAGS_HF))
-        return 0;
+    return 0;
+}
 
-    /* ****************************************** */
+static int ad_open_hf(const char *path, int adflags, int oflags, int mode, struct adouble *ad)
+{
+    struct stat st_dir;
+    struct stat st_meta;
+    struct stat *pst = NULL;
+    char        *ad_p;
+    int         hoflags, admode;
+    int         st_invalid = -1;
+    int         open_df = 0;
 
     if (ad_meta_fileno(ad) != -1) { /* the file is already open */
         if ((oflags & ( O_RDWR | O_WRONLY)) &&
@@ -1239,7 +1082,7 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
         /* it's not new anymore */
         ad->ad_md->adf_flags &= ~( O_TRUNC | O_CREAT );
         ad->ad_md->adf_refcount++;
-        goto sfm;
+        return 0;
     }
 
     memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
@@ -1295,8 +1138,7 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
             if (!st_invalid) {
                 ad_chown(ad_p, &st_dir);
             }
-        }
-        else {
+        } else {
             return ad_error(ad, adflags);
         }
     } else {
@@ -1304,11 +1146,8 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
         if (fstat(ad->ad_md->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) {
             /* for 0 length files, treat them as new. */
             ad->ad_md->adf_flags |= O_TRUNC;
-        } 
-        else {
-            /* we have valid data in st_meta stat structure, reused it
-               in ad_header_read
-            */
+        } else {
+            /* we have valid data in st_meta stat structure, reused it in ad_header_read */
             pst = &st_meta;
         }
     }
@@ -1342,13 +1181,85 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble
         }
     }
 
-    /* ****************************************** */
-    /* open the resource fork if SFM */
-sfm:
-    if (ad->ad_flags != AD_VERSION1_SFM) {
-        return 0;
+    return 0;
+}
+
+/*!
+ * Open data-, metadata(header)- or ressource fork
+ *
+ * You must call ad_init() before ad_open, usually you'll just call it like this: \n
+ * @code
+ *      struct adoube ad;
+ *      ad_init(&ad, vol->v_adouble, vol->v_ad_options);
+ * @endcode
+ *
+ * @param path    Path to file or directory
+ *
+ * @param adflags ADFLAGS_DF:        open data file/fork \n
+ *                ADFLAGS_HF:        open header (metadata) file \n
+ *                ADFLAGS_CREATE:    indicate creation \n
+ *                ADFLAGS_NOHF:      it's not an error if header file couldn't be created \n
+ *                ADFLAGS_DIR:       if path is a directory you MUST or ADFLAGS_DIR to adflags \n
+ *                ADFLAGS_NOADOUBLE: dont create adouble files if not necessary \n
+ *                ADFLAGS_RDONLY:    open read only \n
+ *                ADFLAGS_OPENFORKS: check for open forks from other processes
+ *
+ * @param oflags  flags passed through to open syscall: \n
+ *                O_RDONLY: *** FIXME *** \n
+ *                O_RDWR: *** FIXME *** \n
+ *                O_CREAT: create fork \n
+ *                O_EXCL: fail if exists with O_CREAT
+ *
+ * @param mode    passed to open with O_CREAT
+ * @param ad      pointer to struct adouble
+ *
+ * @returns 0 on success
+ *
+ * @note It's not possible to open the header file O_RDONLY -- the read
+ *       will fail and return an error. this refcounts things now.\n
+ *       metadata(ressource)-fork only gets created with O_CREAT.
+ */
+int ad_open(const char *path, int adflags, int oflags, int mode, struct adouble  *ad)
+{
+    struct stat st_dir;
+    struct stat st_meta;
+    struct stat *pst = NULL;
+    char        *ad_p;
+    int         hoflags, admode;
+    int         st_invalid = -1;
+    int         open_df = 0;
+
+    if (ad->ad_inited != AD_INITED) {
+        ad->ad_inited = AD_INITED;
+        ad->ad_refcount = 1;
+        ad->ad_open_forks = 0;
+        ad->ad_adflags = adflags;
+        ad->ad_resource_fork.adf_refcount = 0;
+        ad->ad_data_fork.adf_refcount = 0;
+        ad->ad_data_fork.adf_syml = 0;
+    } else {
+        ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
+        /* XXX not true if we have a meta data fork ? */
+        if ((ad->ad_resource_fork.adf_refcount > ad->ad_data_fork.adf_refcount))
+            ad->ad_open_forks |= ATTRBIT_ROPEN;
+    }
+
+    if ((adflags & ADFLAGS_DF)) {
+        if (ad_open_df(path, adflags, oflags, mode, ad) != 0)
+            return -1;
     }
 
+    if ((adflags & ADFLAGS_HF)) {
+        if (ad_open_hf(path, adflags, oflags, mode, ad) != 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+#if 0
+void sfm(void)
+{
     if ((adflags & ADFLAGS_DIR)) {
         /* no resource fork for directories / volumes XXX it's false! */
         return 0;
@@ -1406,6 +1317,7 @@ sfm:
     }
     return 0 ;
 }
+#endif
 
 /*!
  * @brief open metadata, possibly as root
index 9b8d14374c8e4fbb6aeaa86716ce7aff2af73c09..639f51b3873cb3ecbb35833be72f312ee071e5a9 100644 (file)
@@ -2,7 +2,7 @@
 
 noinst_LTLIBRARIES = libvfs.la
 
-libvfs_la_SOURCES = vfs.c unix.c ea.c sys_ea.c ea_sys.c
+libvfs_la_SOURCES = vfs.c unix.c extattr.c ea_ad.c ea_sys.c
 
 if HAVE_ACLS
 libvfs_la_SOURCES += acl.c
diff --git a/libatalk/vfs/ea.c b/libatalk/vfs/ea.c
deleted file mode 100644 (file)
index fa3a010..0000000
+++ /dev/null
@@ -1,1768 +0,0 @@
-/*
-  Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <unistd.h>
-#include <stdint.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-
-#include <atalk/adouble.h>
-#include <atalk/ea.h>
-#include <atalk/afp.h>
-#include <atalk/logger.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:
- *
- * filename "fileWithEAs" with EAs "testEA1" and "testEA2"
- *
- * - create header with with the format struct adouble_ea_ondisk, the file is written to
- *   ".AppleDouble/fileWithEAs::EA"
- * - 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
- *
- * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea
- *
- * Arguments:
- *
- *    ea      (rw) handle to struct ea
- *
- * Returns: 0 on success, -1 on error
- *
- * Effects:
- *
- * Verifies magic and version.
- */
-static int unpack_header(struct ea * restrict ea)
-{
-    int ret = 0;
-    unsigned int count = 0;
-    uint32_t uint32;
-    char *buf;
-
-    /* Check magic and version */
-    buf = ea->ea_data;
-    if (*(uint32_t *)buf != htonl(EA_MAGIC)) {
-        LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf);
-        ret = -1;
-        goto exit;
-    }
-    buf += 4;
-    if (*(uint16_t *)buf != htons(EA_VERSION)) {
-        LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf);
-        ret = -1;
-        goto exit;
-    }
-    buf += 2;
-
-    /* Get EA count */
-    ea->ea_count = ntohs(*(uint16_t *)buf);
-    LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count);
-    buf += 2;
-
-    if (ea->ea_count == 0)
-        return 0;
-
-    /* Allocate storage for the ea_entries array */
-    ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count);
-    if ( ! ea->ea_entries) {
-        LOG(log_error, logtype_afpd, "unpack_header: OOM");
-        ret = -1;
-        goto exit;
-    }
-
-    buf = ea->ea_data + EA_HEADER_SIZE;
-    while (count < ea->ea_count) {
-        memcpy(&uint32, buf, 4); /* EA size */
-        buf += 4;
-        (*(ea->ea_entries))[count].ea_size = ntohl(uint32);
-        (*(ea->ea_entries))[count].ea_name = strdup(buf);
-        if (! (*(ea->ea_entries))[count].ea_name) {
-            LOG(log_error, logtype_afpd, "unpack_header: OOM");
-            ret = -1;
-            goto exit;
-        }
-        (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name);
-        buf += (*(ea->ea_entries))[count].ea_namelen + 1;
-
-        LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
-            (*(ea->ea_entries))[count].ea_name,
-            (*(ea->ea_entries))[count].ea_size,
-            (*(ea->ea_entries))[count].ea_namelen);
-
-        count++;
-    }
-
-exit:
-    return ret;
-}
-
-/*
- * Function: pack_header
- *
- * Purpose: pack everything from struct ea into buffer at ea->ea_data
- *
- * Arguments:
- *
- *    ea      (rw) handle to struct ea
- *
- * Returns: 0 on success, -1 on error
- *
- * Effects:
- *
- * adjust ea->ea_count in case an ea entry deletetion is detected
- */
-static int pack_header(struct ea * restrict ea)
-{
-    unsigned int count = 0, eacount = 0;
-    uint16_t uint16;
-    uint32_t uint32;
-    size_t bufsize = EA_HEADER_SIZE;
-
-    char *buf = ea->ea_data + EA_HEADER_SIZE;
-
-    LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
-        ea->filename, ea->ea_count, ea->ea_size);
-
-    if (ea->ea_count == 0)
-        /* nothing to do, magic, version and count are still valid in buffer */
-        return 0;
-
-    while(count < ea->ea_count) { /* the names */
-        /* Check if its a deleted entry */
-        if ( ! ((*ea->ea_entries)[count].ea_name)) {
-            count++;
-            continue;
-        }
-
-        bufsize += (*(ea->ea_entries))[count].ea_namelen + 1;
-        count++;
-        eacount++;
-    }
-
-    bufsize += (eacount * 4); /* header + ea_size for each EA */
-    if (bufsize > ea->ea_size) {
-        /* we must realloc */
-        if ( ! (buf = realloc(ea->ea_data, bufsize)) ) {
-            LOG(log_error, logtype_afpd, "pack_header: OOM");
-            return -1;
-        }
-        ea->ea_data = buf;
-    }
-    ea->ea_size = bufsize;
-
-    /* copy count */
-    uint16 = htons(eacount);
-    memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2);
-
-    count = 0;
-    buf = ea->ea_data + EA_HEADER_SIZE;
-    while (count < ea->ea_count) {
-        /* Check if its a deleted entry */
-        if ( ! ((*ea->ea_entries)[count].ea_name)) {
-            count++;
-            continue;
-        }
-
-        /* First: EA size */
-        uint32 = htonl((*(ea->ea_entries))[count].ea_size);
-        memcpy(buf, &uint32, 4);
-        buf += 4;
-
-        /* Second: EA name as C-string */
-        strcpy(buf, (*(ea->ea_entries))[count].ea_name);
-        buf += (*(ea->ea_entries))[count].ea_namelen + 1;
-
-        LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
-            (*(ea->ea_entries))[count].ea_name,
-            (*(ea->ea_entries))[count].ea_size,
-            (*(ea->ea_entries))[count].ea_namelen);
-
-        count++;
-    }
-
-    ea->ea_count = eacount;
-
-    LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
-        ea->filename, ea->ea_count, ea->ea_size);
-    
-    return 0;
-}
-
-/*
- * Function: ea_addentry
- *
- * Purpose: add one EA into ea->ea_entries[]
- *
- * Arguments:
- *
- *    ea            (rw) pointer to struct ea
- *    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
- *
- * Effects:
- *
- * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating.
- * Otherwise realloc and put entry at the end. Increments ea->ea_count.
- */
-static int ea_addentry(struct ea * restrict ea,
-                       const char * restrict attruname,
-                       size_t attrsize,
-                       int bitmap)
-{
-    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) {
-                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;
-                (*(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 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;
-        }
-        ea->ea_entries = tmprealloc;
-    }
-
-    /* We've grown the array, now store the entry */
-    (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
-    (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
-    if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
-        LOG(log_error, logtype_afpd, "ea_addentry: OOM");
-        goto error;
-    }
-    (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
-
-    ea->ea_count++;
-    return ea->ea_count;
-
-error:
-    if (ea->ea_count == 0 && ea->ea_entries) {
-        /* We just allocated storage but had an error somewhere -> free storage*/
-        free(ea->ea_entries);
-        ea->ea_entries = NULL;
-    }
-    ea->ea_count = 0;
-    return -1;
-}
-
-/*
- * Function: create_ea_header
- *
- * Purpose: create EA header file, only called from ea_open
- *
- * Arguments:
- *
- *    uname       (r)  filename for which we have to create a header
- *    ea          (rw) ea handle with already allocated storage pointed to
- *                     by ea->ea_data
- *
- * Returns: fd of open header file on success, -1 on error, errno semantics:
- *          EEXIST: open with O_CREAT | O_EXCL failed
- *
- * Effects:
- *
- * Creates EA header file and initialize ea->ea_data buffer.
- * Possibe race condition with other afpd processes:
- * we were called because header file didn't exist in eg. ea_open. We then
- * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
- * What do we do then? Someone else is in the process of creating the header too, but
- * it might not have finished it. That means we cant just open, read and use it!
- * We therefor currently just break with an error.
- * On return the header file is still r/w locked.
- */
-static int create_ea_header(const char * restrict uname,
-                            struct ea * restrict ea)
-{
-    int fd = -1, err = 0;
-    char *ptr;
-
-    if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
-        LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
-        return -1;
-    }
-
-    /* lock it */
-    if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
-        LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
-        err = -1;
-        goto exit;
-    }
-
-    /* Now init it */
-    ptr = ea->ea_data;
-    *(uint32_t *)ptr = htonl(EA_MAGIC);
-    ptr += EA_MAGIC_LEN;
-    *(uint16_t *)ptr = htons(EA_VERSION);
-    ptr += EA_VERSION_LEN;
-    *(uint16_t *)ptr = 0;       /* count */
-
-    ea->ea_size = EA_HEADER_SIZE;
-    ea->ea_inited = EA_INITED;
-
-exit:
-    if (err != 0) {
-        close(fd);
-        fd = -1;
-    }
-    return fd;
-}
-
-/*
- * Function: write_ea
- *
- * Purpose: write an EA to disk
- *
- * Arguments:
- *
- *    ea         (r) struct ea handle
- *    attruname  (r) EA name
- *    ibuf       (r) buffer with EA content
- *    attrsize   (r) size of EA
- *
- * Returns: 0 on success, -1 on error
- *
- * Effects:
- *
- * Creates/overwrites EA file.
- *
- */
-static int write_ea(const struct ea * restrict ea,
-                    const char * restrict attruname,
-                    const char * restrict ibuf,
-                    size_t attrsize)
-{
-    int fd = -1, ret = AFP_OK;
-    struct stat st;
-    char *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) {
-        if ((unlink(eaname)) != 0) {
-            if (errno == EACCES)
-                return AFPERR_ACCESS;
-            else
-                return AFPERR_MISC;
-        }
-    }
-
-    if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
-        LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
-        return -1;
-    }
-
-    /* lock it */
-    if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
-        LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
-        ret = -1;
-        goto exit;
-    }
-
-    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;
-    }
-
-exit:
-    if (fd != -1)
-        close(fd); /* and unlock */
-    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
- *
- * Purpose: delete EA file from disk
- *
- * Arguments:
- *
- *    ea         (r) struct ea handle
- *    attruname  (r) EA name
- *
- * Returns: 0 on success, -1 on error
- */
-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, 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) {
-        if ((unlink(eafile)) != 0) {
-            LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
-                eafile, strerror(errno));
-            ret = -1;
-        } else
-            LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
-    }
-
-    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
- *
- * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
- *
- * Arguments:
- *
- *    vol         (r) current volume
- *    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_open(const struct vol * restrict vol,
-            const char * restrict uname,
-            eaflags_t eaflags,
-            struct ea * restrict ea)
-{
-    int ret = 0;
-    char *eaname;
-    struct stat st;
-
-    /* Enforce usage rules! */
-    if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
-        LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
-        return -1;
-    }
-
-    /* Set it all to 0 */
-    memset(ea, 0, sizeof(struct ea));
-
-    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;
-
-    if ( ! (ea->filename = strdup(uname))) {
-        LOG(log_error, logtype_afpd, "ea_open: OOM");
-        return -1;
-    }
-
-    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 ((stat(eaname, &st)) != 0) {
-        if (errno == ENOENT) {
-
-            /* It doesnt exist */
-
-            if ( ! (eaflags & EA_CREATE)) {
-                /* creation was not requested, so return with error */
-                ret = -2;
-                goto exit;
-            }
-
-            /* Now create a header file */
-
-            /* malloc buffer for minimal on disk data */
-            ea->ea_data = malloc(EA_HEADER_SIZE);
-            if (! ea->ea_data) {
-                LOG(log_error, logtype_afpd, "ea_open: OOM");
-                ret = -1;
-                goto exit;
-            }
-
-            /* create it */
-            ea->ea_fd = create_ea_header(eaname, ea);
-            if (ea->ea_fd == -1) {
-                ret = -1;
-                goto exit;
-            }
-
-            return 0;
-
-        } else {/* errno != ENOENT */
-            ret = -1;
-            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) {
-        LOG(log_error, logtype_afpd, "ea_open: OOM");
-        ret = -1;
-        goto exit;
-    }
-
-    /* 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('%s'): error: %s", eaname, strerror(errno));
-        ret = -1;
-        goto exit;
-    }
-
-    /* lock it */
-    if (ea->ea_flags & EA_RDONLY) {
-        /* read lock */
-        if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
-            LOG(log_error, logtype_afpd, "ea_open: lock error on  header: %s", eaname);
-            ret = -1;
-            goto exit;
-        }
-    } else {  /* EA_RDWR */
-        /* write lock */
-        if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
-            LOG(log_error, logtype_afpd, "ea_open: lock error on  header: %s", eaname);
-            ret = -1;
-            goto exit;
-        }
-    }
-
-    /* read it */
-    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;
-    }
-
-    if ((unpack_header(ea)) != 0) {
-        LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
-        ret = -1;
-        goto exit;
-    }
-
-exit:
-    switch (ret) {
-    case 0:
-        ea->ea_inited = EA_INITED;
-        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;
-        }
-        if (ea->ea_fd) {
-            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;
-
-}
-
-/*
- * Function: ea_close
- *
- * Purpose: flushes and closes an ea handle
- *
- * Arguments:
- *
- *    ea          (rw) pointer to ea handle
- *
- * Returns: 0 on success, -1 on error
- *
- * Effects:
- *
- * 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
- */
-int ea_close(struct ea * restrict ea)
-{
-    int ret = 0; 
-    unsigned int count = 0;
-    char *eaname;
-    struct stat st;
-
-    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) {
-            LOG(log_error, logtype_afpd, "ea_close: pack header");
-            ret = -1;
-        } else {
-            if (ea->ea_count == 0) {
-                /* Check if EA header exists and remove it */
-                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;
-                    }
-                    else
-                        LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
-                } else {
-                    /* stat error */
-                    if (errno != ENOENT) {
-                        LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
-                            eaname, strerror(errno));
-                        ret = -1;
-                    }
-                }
-            } else { /* ea->ea_count > 0 */
-                if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
-                    LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
-                    ret = -1;
-                    goto exit;
-                }
-
-                if ((ftruncate(ea->ea_fd, 0)) == -1) {
-                    LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
-                    ret = -1;
-                    goto exit;
-                }
-
-                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;
-                }
-            }
-        }
-    }
-
-exit:
-    /* free names */
-    while(count < ea->ea_count) {
-        if ( (*ea->ea_entries)[count].ea_name ) {
-            free((*ea->ea_entries)[count].ea_name);
-            (*ea->ea_entries)[count].ea_name = NULL;
-        }
-        count++;
-    }
-    ea->ea_count = 0;
-
-    if (ea->filename) {
-        free(ea->filename);
-        ea->filename = NULL;
-    }
-
-    if (ea->ea_entries) {
-        free(ea->ea_entries);
-        ea->ea_entries = NULL;
-    }
-
-    if (ea->ea_data) {
-        free(ea->ea_data);
-        ea->ea_data = NULL;
-    }
-    if (ea->ea_fd != -1) {
-        close(ea->ea_fd);       /* also releases the fcntl lock */
-        ea->ea_fd = -1;
-    }
-
-    return 0;
-}
-
-
-
-/************************************************************************************
- * VFS funcs called from afp_ea* funcs
- ************************************************************************************/
-
-/*
- * Function: get_easize
- *
- * Purpose: get size of an 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.
- */
-int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
-{
-    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) {
-        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) {
-        if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
-            uint32 = htonl((*ea.ea_entries)[count].ea_size);
-            memcpy(rbuf, &uint32, 4);
-            *rbuflen += 4;
-            ret = AFP_OK;
-
-            LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
-                attruname, (*ea.ea_entries)[count].ea_size);
-            break;
-        }
-        count++;
-    }
-
-    if ((ea_close(&ea)) != 0) {
-        LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
-        return AFPERR_MISC;
-    }
-
-    return ret;
-}
-
-/*
- * Function: get_eacontent
- *
- * Purpose: copy 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.
- */
-int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
-{
-    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) {
-        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, 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;
-            }
-
-            /* Check how much the client wants, give him what we think is right */
-            maxreply -= MAX_REPLY_EXTRA_BYTES;
-            if (maxreply > MAX_EA_SIZE)
-                maxreply = MAX_EA_SIZE;
-            toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
-            LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
-
-            /* Put length of EA data in reply buffer */
-            uint32 = htonl(toread);
-            memcpy(rbuf, &uint32, 4);
-            rbuf += 4;
-            *rbuflen += 4;
-
-            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;
-            }
-            *rbuflen += toread;
-            close(fd);
-
-            ret = AFP_OK;
-            break;
-        }
-        count++;
-    }
-
-    if ((ea_close(&ea)) != 0) {
-        LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
-        return AFPERR_MISC;
-    }
-
-    return ret;
-
-}
-
-/*
- * Function: list_eas
- *
- * Purpose: copy names of EAs 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 *buflen accordingly.
- */
-int list_eas(VFS_FUNC_ARGS_EA_LIST)
-{
-    unsigned int count = 0;
-    int attrbuflen = *buflen, ret = AFP_OK, len;
-    char *buf = attrnamebuf;
-    struct ea ea;
-
-    LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
-
-    if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
-        if (errno != ENOENT) {
-            LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
-            return AFPERR_MISC;
-        }
-        else
-            return AFP_OK;
-    }
-
-    while (count < ea.ea_count) {
-        /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
-        if ( ( len = convert_string(vol->v_volcharset,
-                                    CH_UTF8_MAC, 
-                                    (*ea.ea_entries)[count].ea_name,
-                                    (*ea.ea_entries)[count].ea_namelen,
-                                    buf + attrbuflen,
-                                    255))
-             <= 0 ) {
-            ret = AFPERR_MISC;
-            goto exit;
-        }
-        if (len == 255)
-            /* convert_string didn't 0-terminate */
-            attrnamebuf[attrbuflen + 255] = 0;
-
-        LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
-            uname, (*ea.ea_entries)[count].ea_name);
-
-        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, "list_eas(%s): running out of buffer for EA names", uname);
-            ret = AFPERR_MISC;
-            goto exit;
-        }
-        count++;
-    }
-
-exit:
-    *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;
-    }
-
-    return ret;
-}
-
-/*
- * Function: 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.
- */
-int set_ea(VFS_FUNC_ARGS_EA_SET)
-{
-    int ret = AFP_OK;
-    struct ea ea;
-
-    LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
-
-    if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
-        LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
-        return AFPERR_MISC;
-    }
-
-    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;
-    }
-
-    if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
-        LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
-        ret = AFPERR_MISC;
-        goto exit;
-    }
-
-exit:
-    if ((ea_close(&ea)) != 0) {
-        LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
-        ret = AFPERR_MISC;
-        goto exit;
-    }
-
-    return ret;
-}
-
-/*
- * Function: remove_ea
- *
- * Purpose: remove a EA from a file
- *
- * 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.
- */
-int remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
-{
-    int ret = AFP_OK;
-    struct ea ea;
-
-    LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
-
-    if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
-        LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
-        return AFPERR_MISC;
-    }
-
-    if ((ea_delentry(&ea, attruname)) == -1) {
-        LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
-        ret = AFPERR_MISC;
-        goto exit;
-    }
-
-    if ((delete_ea_file(&ea, attruname)) != 0) {
-        LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
-        ret = AFPERR_MISC;
-        goto exit;
-    }
-
-exit:
-    if ((ea_close(&ea)) != 0) {
-        LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
-        ret = AFPERR_MISC;
-        goto exit;
-    }
-
-    return ret;
-}
-
-/******************************************************************************************
- * EA VFS funcs that deal with file/dir cp/mv/rm
- ******************************************************************************************/
-
-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_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;
-        }
-    }
-
-    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;
-            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);
-        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;
-}
-
-int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
-{
-    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_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;
-        }
-    }
-
-    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, 1)) == NULL) {
-            ret = AFPERR_MISC;
-            goto exit;
-        }
-        strcpy(srceapath, eapath);
-        if ((eapath = ea_path(&dstea, eaname, 1)) == 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 ((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++;
-    }
-
-
-exit:
-    ea_close(&srcea);
-    ea_close(&dstea);
-       return ret;
-}
-
-int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
-{
-    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);
-
-    /* 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;
-        }
-    }
-
-    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, 1)) == NULL) {
-            ret = AFPERR_MISC;
-            goto exit;
-        }
-        strcpy(srceapath, eapath);
-        if ((eapath = ea_path(&dstea, eaname, 1)) == 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(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;
-        }
-
-        count++;
-    }
-
-exit:
-    ea_close(&srcea);
-    ea_close(&dstea);
-       return ret;
-}
-
-int ea_chown(VFS_FUNC_ARGS_CHOWN)
-{
-
-    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, 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 ((lchown(ea_path(&ea, NULL, 0), 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, 1)) == NULL) {
-            ret = AFPERR_MISC;
-            goto exit;
-        }
-        if ((lchown(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)
-{
-
-    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, 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, 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:
-            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, 1)) == 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)
-{
-
-    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;
-        if (seteuid(uid) < 0) {
-            LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
-            exit(EXITERR_SYS);
-        }
-        return ret;
-    }
-
-    /* Set mode on EA header */
-    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:
-            ret = AFPERR_ACCESS;
-            goto exit;
-        default:
-            ret = AFPERR_MISC;
-            goto exit;
-        }
-    }
-
-    /* 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(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;
-}
diff --git a/libatalk/vfs/ea_ad.c b/libatalk/vfs/ea_ad.c
new file mode 100644 (file)
index 0000000..fa3a010
--- /dev/null
@@ -0,0 +1,1768 @@
+/*
+  Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include <atalk/adouble.h>
+#include <atalk/ea.h>
+#include <atalk/afp.h>
+#include <atalk/logger.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:
+ *
+ * filename "fileWithEAs" with EAs "testEA1" and "testEA2"
+ *
+ * - create header with with the format struct adouble_ea_ondisk, the file is written to
+ *   ".AppleDouble/fileWithEAs::EA"
+ * - 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
+ *
+ * Purpose: unpack and verify header file data buffer at ea->ea_data into struct ea
+ *
+ * Arguments:
+ *
+ *    ea      (rw) handle to struct ea
+ *
+ * Returns: 0 on success, -1 on error
+ *
+ * Effects:
+ *
+ * Verifies magic and version.
+ */
+static int unpack_header(struct ea * restrict ea)
+{
+    int ret = 0;
+    unsigned int count = 0;
+    uint32_t uint32;
+    char *buf;
+
+    /* Check magic and version */
+    buf = ea->ea_data;
+    if (*(uint32_t *)buf != htonl(EA_MAGIC)) {
+        LOG(log_error, logtype_afpd, "unpack_header: wrong magic 0x%08x", *(uint32_t *)buf);
+        ret = -1;
+        goto exit;
+    }
+    buf += 4;
+    if (*(uint16_t *)buf != htons(EA_VERSION)) {
+        LOG(log_error, logtype_afpd, "unpack_header: wrong version 0x%04x", *(uint16_t *)buf);
+        ret = -1;
+        goto exit;
+    }
+    buf += 2;
+
+    /* Get EA count */
+    ea->ea_count = ntohs(*(uint16_t *)buf);
+    LOG(log_debug, logtype_afpd, "unpack_header: number of EAs: %u", ea->ea_count);
+    buf += 2;
+
+    if (ea->ea_count == 0)
+        return 0;
+
+    /* Allocate storage for the ea_entries array */
+    ea->ea_entries = malloc(sizeof(struct ea_entry) * ea->ea_count);
+    if ( ! ea->ea_entries) {
+        LOG(log_error, logtype_afpd, "unpack_header: OOM");
+        ret = -1;
+        goto exit;
+    }
+
+    buf = ea->ea_data + EA_HEADER_SIZE;
+    while (count < ea->ea_count) {
+        memcpy(&uint32, buf, 4); /* EA size */
+        buf += 4;
+        (*(ea->ea_entries))[count].ea_size = ntohl(uint32);
+        (*(ea->ea_entries))[count].ea_name = strdup(buf);
+        if (! (*(ea->ea_entries))[count].ea_name) {
+            LOG(log_error, logtype_afpd, "unpack_header: OOM");
+            ret = -1;
+            goto exit;
+        }
+        (*(ea->ea_entries))[count].ea_namelen = strlen((*(ea->ea_entries))[count].ea_name);
+        buf += (*(ea->ea_entries))[count].ea_namelen + 1;
+
+        LOG(log_maxdebug, logtype_afpd, "unpack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
+            (*(ea->ea_entries))[count].ea_name,
+            (*(ea->ea_entries))[count].ea_size,
+            (*(ea->ea_entries))[count].ea_namelen);
+
+        count++;
+    }
+
+exit:
+    return ret;
+}
+
+/*
+ * Function: pack_header
+ *
+ * Purpose: pack everything from struct ea into buffer at ea->ea_data
+ *
+ * Arguments:
+ *
+ *    ea      (rw) handle to struct ea
+ *
+ * Returns: 0 on success, -1 on error
+ *
+ * Effects:
+ *
+ * adjust ea->ea_count in case an ea entry deletetion is detected
+ */
+static int pack_header(struct ea * restrict ea)
+{
+    unsigned int count = 0, eacount = 0;
+    uint16_t uint16;
+    uint32_t uint32;
+    size_t bufsize = EA_HEADER_SIZE;
+
+    char *buf = ea->ea_data + EA_HEADER_SIZE;
+
+    LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
+        ea->filename, ea->ea_count, ea->ea_size);
+
+    if (ea->ea_count == 0)
+        /* nothing to do, magic, version and count are still valid in buffer */
+        return 0;
+
+    while(count < ea->ea_count) { /* the names */
+        /* Check if its a deleted entry */
+        if ( ! ((*ea->ea_entries)[count].ea_name)) {
+            count++;
+            continue;
+        }
+
+        bufsize += (*(ea->ea_entries))[count].ea_namelen + 1;
+        count++;
+        eacount++;
+    }
+
+    bufsize += (eacount * 4); /* header + ea_size for each EA */
+    if (bufsize > ea->ea_size) {
+        /* we must realloc */
+        if ( ! (buf = realloc(ea->ea_data, bufsize)) ) {
+            LOG(log_error, logtype_afpd, "pack_header: OOM");
+            return -1;
+        }
+        ea->ea_data = buf;
+    }
+    ea->ea_size = bufsize;
+
+    /* copy count */
+    uint16 = htons(eacount);
+    memcpy(ea->ea_data + EA_COUNT_OFF, &uint16, 2);
+
+    count = 0;
+    buf = ea->ea_data + EA_HEADER_SIZE;
+    while (count < ea->ea_count) {
+        /* Check if its a deleted entry */
+        if ( ! ((*ea->ea_entries)[count].ea_name)) {
+            count++;
+            continue;
+        }
+
+        /* First: EA size */
+        uint32 = htonl((*(ea->ea_entries))[count].ea_size);
+        memcpy(buf, &uint32, 4);
+        buf += 4;
+
+        /* Second: EA name as C-string */
+        strcpy(buf, (*(ea->ea_entries))[count].ea_name);
+        buf += (*(ea->ea_entries))[count].ea_namelen + 1;
+
+        LOG(log_maxdebug, logtype_afpd, "pack_header: entry no:%u,\"%s\", size: %u, namelen: %u", count,
+            (*(ea->ea_entries))[count].ea_name,
+            (*(ea->ea_entries))[count].ea_size,
+            (*(ea->ea_entries))[count].ea_namelen);
+
+        count++;
+    }
+
+    ea->ea_count = eacount;
+
+    LOG(log_debug, logtype_afpd, "pack_header('%s'): ea_count: %u, ea_size: %u",
+        ea->filename, ea->ea_count, ea->ea_size);
+    
+    return 0;
+}
+
+/*
+ * Function: ea_addentry
+ *
+ * Purpose: add one EA into ea->ea_entries[]
+ *
+ * Arguments:
+ *
+ *    ea            (rw) pointer to struct ea
+ *    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
+ *
+ * Effects:
+ *
+ * Grow array ea->ea_entries[]. If ea->ea_entries is still NULL, start allocating.
+ * Otherwise realloc and put entry at the end. Increments ea->ea_count.
+ */
+static int ea_addentry(struct ea * restrict ea,
+                       const char * restrict attruname,
+                       size_t attrsize,
+                       int bitmap)
+{
+    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) {
+                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;
+                (*(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 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;
+        }
+        ea->ea_entries = tmprealloc;
+    }
+
+    /* We've grown the array, now store the entry */
+    (*(ea->ea_entries))[ea->ea_count].ea_size = attrsize;
+    (*(ea->ea_entries))[ea->ea_count].ea_name = strdup(attruname);
+    if ( ! (*(ea->ea_entries))[ea->ea_count].ea_name) {
+        LOG(log_error, logtype_afpd, "ea_addentry: OOM");
+        goto error;
+    }
+    (*(ea->ea_entries))[ea->ea_count].ea_namelen = strlen(attruname);
+
+    ea->ea_count++;
+    return ea->ea_count;
+
+error:
+    if (ea->ea_count == 0 && ea->ea_entries) {
+        /* We just allocated storage but had an error somewhere -> free storage*/
+        free(ea->ea_entries);
+        ea->ea_entries = NULL;
+    }
+    ea->ea_count = 0;
+    return -1;
+}
+
+/*
+ * Function: create_ea_header
+ *
+ * Purpose: create EA header file, only called from ea_open
+ *
+ * Arguments:
+ *
+ *    uname       (r)  filename for which we have to create a header
+ *    ea          (rw) ea handle with already allocated storage pointed to
+ *                     by ea->ea_data
+ *
+ * Returns: fd of open header file on success, -1 on error, errno semantics:
+ *          EEXIST: open with O_CREAT | O_EXCL failed
+ *
+ * Effects:
+ *
+ * Creates EA header file and initialize ea->ea_data buffer.
+ * Possibe race condition with other afpd processes:
+ * we were called because header file didn't exist in eg. ea_open. We then
+ * try to create a file with O_CREAT | O_EXCL, but the whole process in not atomic.
+ * What do we do then? Someone else is in the process of creating the header too, but
+ * it might not have finished it. That means we cant just open, read and use it!
+ * We therefor currently just break with an error.
+ * On return the header file is still r/w locked.
+ */
+static int create_ea_header(const char * restrict uname,
+                            struct ea * restrict ea)
+{
+    int fd = -1, err = 0;
+    char *ptr;
+
+    if ((fd = open(uname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
+        LOG(log_error, logtype_afpd, "ea_create: open race condition with ea header for file: %s", uname);
+        return -1;
+    }
+
+    /* lock it */
+    if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
+        LOG(log_error, logtype_afpd, "ea_create: lock race condition with ea header for file: %s", uname);
+        err = -1;
+        goto exit;
+    }
+
+    /* Now init it */
+    ptr = ea->ea_data;
+    *(uint32_t *)ptr = htonl(EA_MAGIC);
+    ptr += EA_MAGIC_LEN;
+    *(uint16_t *)ptr = htons(EA_VERSION);
+    ptr += EA_VERSION_LEN;
+    *(uint16_t *)ptr = 0;       /* count */
+
+    ea->ea_size = EA_HEADER_SIZE;
+    ea->ea_inited = EA_INITED;
+
+exit:
+    if (err != 0) {
+        close(fd);
+        fd = -1;
+    }
+    return fd;
+}
+
+/*
+ * Function: write_ea
+ *
+ * Purpose: write an EA to disk
+ *
+ * Arguments:
+ *
+ *    ea         (r) struct ea handle
+ *    attruname  (r) EA name
+ *    ibuf       (r) buffer with EA content
+ *    attrsize   (r) size of EA
+ *
+ * Returns: 0 on success, -1 on error
+ *
+ * Effects:
+ *
+ * Creates/overwrites EA file.
+ *
+ */
+static int write_ea(const struct ea * restrict ea,
+                    const char * restrict attruname,
+                    const char * restrict ibuf,
+                    size_t attrsize)
+{
+    int fd = -1, ret = AFP_OK;
+    struct stat st;
+    char *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) {
+        if ((unlink(eaname)) != 0) {
+            if (errno == EACCES)
+                return AFPERR_ACCESS;
+            else
+                return AFPERR_MISC;
+        }
+    }
+
+    if ((fd = open(eaname, O_RDWR | O_CREAT | O_EXCL, 0666 & ~ea->vol->v_umask)) == -1) {
+        LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
+        return -1;
+    }
+
+    /* lock it */
+    if ((write_lock(fd, 0, SEEK_SET, 0)) != 0) {
+        LOG(log_error, logtype_afpd, "write_ea: open race condition: %s", eaname);
+        ret = -1;
+        goto exit;
+    }
+
+    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;
+    }
+
+exit:
+    if (fd != -1)
+        close(fd); /* and unlock */
+    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
+ *
+ * Purpose: delete EA file from disk
+ *
+ * Arguments:
+ *
+ *    ea         (r) struct ea handle
+ *    attruname  (r) EA name
+ *
+ * Returns: 0 on success, -1 on error
+ */
+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, 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) {
+        if ((unlink(eafile)) != 0) {
+            LOG(log_error, logtype_afpd, "delete_ea_file('%s'): unlink: %s",
+                eafile, strerror(errno));
+            ret = -1;
+        } else
+            LOG(log_debug, logtype_afpd, "delete_ea_file('%s'): success", eafile);
+    }
+
+    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
+ *
+ * Purpose: open EA header file, create if it doesnt exits and called with O_CREATE
+ *
+ * Arguments:
+ *
+ *    vol         (r) current volume
+ *    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_open(const struct vol * restrict vol,
+            const char * restrict uname,
+            eaflags_t eaflags,
+            struct ea * restrict ea)
+{
+    int ret = 0;
+    char *eaname;
+    struct stat st;
+
+    /* Enforce usage rules! */
+    if ( ! (eaflags & (EA_RDONLY | EA_RDWR))) {
+        LOG(log_error, logtype_afpd, "ea_open: called without EA_RDONLY | EA_RDWR", uname);
+        return -1;
+    }
+
+    /* Set it all to 0 */
+    memset(ea, 0, sizeof(struct ea));
+
+    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;
+
+    if ( ! (ea->filename = strdup(uname))) {
+        LOG(log_error, logtype_afpd, "ea_open: OOM");
+        return -1;
+    }
+
+    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 ((stat(eaname, &st)) != 0) {
+        if (errno == ENOENT) {
+
+            /* It doesnt exist */
+
+            if ( ! (eaflags & EA_CREATE)) {
+                /* creation was not requested, so return with error */
+                ret = -2;
+                goto exit;
+            }
+
+            /* Now create a header file */
+
+            /* malloc buffer for minimal on disk data */
+            ea->ea_data = malloc(EA_HEADER_SIZE);
+            if (! ea->ea_data) {
+                LOG(log_error, logtype_afpd, "ea_open: OOM");
+                ret = -1;
+                goto exit;
+            }
+
+            /* create it */
+            ea->ea_fd = create_ea_header(eaname, ea);
+            if (ea->ea_fd == -1) {
+                ret = -1;
+                goto exit;
+            }
+
+            return 0;
+
+        } else {/* errno != ENOENT */
+            ret = -1;
+            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) {
+        LOG(log_error, logtype_afpd, "ea_open: OOM");
+        ret = -1;
+        goto exit;
+    }
+
+    /* 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('%s'): error: %s", eaname, strerror(errno));
+        ret = -1;
+        goto exit;
+    }
+
+    /* lock it */
+    if (ea->ea_flags & EA_RDONLY) {
+        /* read lock */
+        if ((read_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
+            LOG(log_error, logtype_afpd, "ea_open: lock error on  header: %s", eaname);
+            ret = -1;
+            goto exit;
+        }
+    } else {  /* EA_RDWR */
+        /* write lock */
+        if ((write_lock(ea->ea_fd, 0, SEEK_SET, 0)) != 0) {
+            LOG(log_error, logtype_afpd, "ea_open: lock error on  header: %s", eaname);
+            ret = -1;
+            goto exit;
+        }
+    }
+
+    /* read it */
+    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;
+    }
+
+    if ((unpack_header(ea)) != 0) {
+        LOG(log_error, logtype_afpd, "ea_open: error unpacking header for: %s", eaname);
+        ret = -1;
+        goto exit;
+    }
+
+exit:
+    switch (ret) {
+    case 0:
+        ea->ea_inited = EA_INITED;
+        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;
+        }
+        if (ea->ea_fd) {
+            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;
+
+}
+
+/*
+ * Function: ea_close
+ *
+ * Purpose: flushes and closes an ea handle
+ *
+ * Arguments:
+ *
+ *    ea          (rw) pointer to ea handle
+ *
+ * Returns: 0 on success, -1 on error
+ *
+ * Effects:
+ *
+ * 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
+ */
+int ea_close(struct ea * restrict ea)
+{
+    int ret = 0; 
+    unsigned int count = 0;
+    char *eaname;
+    struct stat st;
+
+    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) {
+            LOG(log_error, logtype_afpd, "ea_close: pack header");
+            ret = -1;
+        } else {
+            if (ea->ea_count == 0) {
+                /* Check if EA header exists and remove it */
+                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;
+                    }
+                    else
+                        LOG(log_debug, logtype_afpd, "ea_close(unlink '%s'): success", eaname);
+                } else {
+                    /* stat error */
+                    if (errno != ENOENT) {
+                        LOG(log_error, logtype_afpd, "ea_close('%s'): stat: %s",
+                            eaname, strerror(errno));
+                        ret = -1;
+                    }
+                }
+            } else { /* ea->ea_count > 0 */
+                if ((lseek(ea->ea_fd, 0, SEEK_SET)) == -1) {
+                    LOG(log_error, logtype_afpd, "ea_close: lseek: %s", strerror(errno));
+                    ret = -1;
+                    goto exit;
+                }
+
+                if ((ftruncate(ea->ea_fd, 0)) == -1) {
+                    LOG(log_error, logtype_afpd, "ea_close: ftruncate: %s", strerror(errno));
+                    ret = -1;
+                    goto exit;
+                }
+
+                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;
+                }
+            }
+        }
+    }
+
+exit:
+    /* free names */
+    while(count < ea->ea_count) {
+        if ( (*ea->ea_entries)[count].ea_name ) {
+            free((*ea->ea_entries)[count].ea_name);
+            (*ea->ea_entries)[count].ea_name = NULL;
+        }
+        count++;
+    }
+    ea->ea_count = 0;
+
+    if (ea->filename) {
+        free(ea->filename);
+        ea->filename = NULL;
+    }
+
+    if (ea->ea_entries) {
+        free(ea->ea_entries);
+        ea->ea_entries = NULL;
+    }
+
+    if (ea->ea_data) {
+        free(ea->ea_data);
+        ea->ea_data = NULL;
+    }
+    if (ea->ea_fd != -1) {
+        close(ea->ea_fd);       /* also releases the fcntl lock */
+        ea->ea_fd = -1;
+    }
+
+    return 0;
+}
+
+
+
+/************************************************************************************
+ * VFS funcs called from afp_ea* funcs
+ ************************************************************************************/
+
+/*
+ * Function: get_easize
+ *
+ * Purpose: get size of an 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.
+ */
+int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
+{
+    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) {
+        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) {
+        if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
+            uint32 = htonl((*ea.ea_entries)[count].ea_size);
+            memcpy(rbuf, &uint32, 4);
+            *rbuflen += 4;
+            ret = AFP_OK;
+
+            LOG(log_debug, logtype_afpd, "get_easize(\"%s\"): size: %u",
+                attruname, (*ea.ea_entries)[count].ea_size);
+            break;
+        }
+        count++;
+    }
+
+    if ((ea_close(&ea)) != 0) {
+        LOG(log_error, logtype_afpd, "get_easize: error closing ea handle for file: %s", uname);
+        return AFPERR_MISC;
+    }
+
+    return ret;
+}
+
+/*
+ * Function: get_eacontent
+ *
+ * Purpose: copy 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.
+ */
+int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
+{
+    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) {
+        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, 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;
+            }
+
+            /* Check how much the client wants, give him what we think is right */
+            maxreply -= MAX_REPLY_EXTRA_BYTES;
+            if (maxreply > MAX_EA_SIZE)
+                maxreply = MAX_EA_SIZE;
+            toread = (maxreply < (*ea.ea_entries)[count].ea_size) ? maxreply : (*ea.ea_entries)[count].ea_size;
+            LOG(log_debug, logtype_afpd, "get_eacontent('%s'): sending %u bytes", attruname, toread);
+
+            /* Put length of EA data in reply buffer */
+            uint32 = htonl(toread);
+            memcpy(rbuf, &uint32, 4);
+            rbuf += 4;
+            *rbuflen += 4;
+
+            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;
+            }
+            *rbuflen += toread;
+            close(fd);
+
+            ret = AFP_OK;
+            break;
+        }
+        count++;
+    }
+
+    if ((ea_close(&ea)) != 0) {
+        LOG(log_error, logtype_afpd, "get_eacontent('%s'): error closing ea handle", uname);
+        return AFPERR_MISC;
+    }
+
+    return ret;
+
+}
+
+/*
+ * Function: list_eas
+ *
+ * Purpose: copy names of EAs 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 *buflen accordingly.
+ */
+int list_eas(VFS_FUNC_ARGS_EA_LIST)
+{
+    unsigned int count = 0;
+    int attrbuflen = *buflen, ret = AFP_OK, len;
+    char *buf = attrnamebuf;
+    struct ea ea;
+
+    LOG(log_debug, logtype_afpd, "list_eas: file: %s", uname);
+
+    if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
+        if (errno != ENOENT) {
+            LOG(log_error, logtype_afpd, "list_eas: error calling ea_open for file: %s", uname);
+            return AFPERR_MISC;
+        }
+        else
+            return AFP_OK;
+    }
+
+    while (count < ea.ea_count) {
+        /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
+        if ( ( len = convert_string(vol->v_volcharset,
+                                    CH_UTF8_MAC, 
+                                    (*ea.ea_entries)[count].ea_name,
+                                    (*ea.ea_entries)[count].ea_namelen,
+                                    buf + attrbuflen,
+                                    255))
+             <= 0 ) {
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+        if (len == 255)
+            /* convert_string didn't 0-terminate */
+            attrnamebuf[attrbuflen + 255] = 0;
+
+        LOG(log_debug7, logtype_afpd, "list_eas(%s): EA: %s",
+            uname, (*ea.ea_entries)[count].ea_name);
+
+        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, "list_eas(%s): running out of buffer for EA names", uname);
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+        count++;
+    }
+
+exit:
+    *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;
+    }
+
+    return ret;
+}
+
+/*
+ * Function: 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.
+ */
+int set_ea(VFS_FUNC_ARGS_EA_SET)
+{
+    int ret = AFP_OK;
+    struct ea ea;
+
+    LOG(log_debug, logtype_afpd, "set_ea: file: %s", uname);
+
+    if ((ea_open(vol, uname, EA_CREATE | EA_RDWR, &ea)) != 0) {
+        LOG(log_error, logtype_afpd, "set_ea('%s'): ea_open error", uname);
+        return AFPERR_MISC;
+    }
+
+    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;
+    }
+
+    if ((write_ea(&ea, attruname, ibuf, attrsize)) != 0) {
+        LOG(log_error, logtype_afpd, "set_ea('%s'): write_ea error", uname);
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+
+exit:
+    if ((ea_close(&ea)) != 0) {
+        LOG(log_error, logtype_afpd, "set_ea('%s'): ea_close error", uname);
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+
+    return ret;
+}
+
+/*
+ * Function: remove_ea
+ *
+ * Purpose: remove a EA from a file
+ *
+ * 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.
+ */
+int remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
+{
+    int ret = AFP_OK;
+    struct ea ea;
+
+    LOG(log_debug, logtype_afpd, "remove_ea('%s/%s')", uname, attruname);
+
+    if ((ea_open(vol, uname, EA_RDWR, &ea)) != 0) {
+        LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_open error", uname);
+        return AFPERR_MISC;
+    }
+
+    if ((ea_delentry(&ea, attruname)) == -1) {
+        LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_delentry error", uname);
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+
+    if ((delete_ea_file(&ea, attruname)) != 0) {
+        LOG(log_error, logtype_afpd, "remove_ea('%s'): delete_ea error", uname);
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+
+exit:
+    if ((ea_close(&ea)) != 0) {
+        LOG(log_error, logtype_afpd, "remove_ea('%s'): ea_close error", uname);
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+
+    return ret;
+}
+
+/******************************************************************************************
+ * EA VFS funcs that deal with file/dir cp/mv/rm
+ ******************************************************************************************/
+
+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_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;
+        }
+    }
+
+    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;
+            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);
+        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;
+}
+
+int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
+{
+    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_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;
+        }
+    }
+
+    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, 1)) == NULL) {
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+        strcpy(srceapath, eapath);
+        if ((eapath = ea_path(&dstea, eaname, 1)) == 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 ((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++;
+    }
+
+
+exit:
+    ea_close(&srcea);
+    ea_close(&dstea);
+       return ret;
+}
+
+int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
+{
+    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);
+
+    /* 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;
+        }
+    }
+
+    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, 1)) == NULL) {
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+        strcpy(srceapath, eapath);
+        if ((eapath = ea_path(&dstea, eaname, 1)) == 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(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;
+        }
+
+        count++;
+    }
+
+exit:
+    ea_close(&srcea);
+    ea_close(&dstea);
+       return ret;
+}
+
+int ea_chown(VFS_FUNC_ARGS_CHOWN)
+{
+
+    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, 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 ((lchown(ea_path(&ea, NULL, 0), 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, 1)) == NULL) {
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+        if ((lchown(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)
+{
+
+    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, 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, 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:
+            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, 1)) == 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)
+{
+
+    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;
+        if (seteuid(uid) < 0) {
+            LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
+            exit(EXITERR_SYS);
+        }
+        return ret;
+    }
+
+    /* Set mode on EA header */
+    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:
+            ret = AFPERR_ACCESS;
+            goto exit;
+        default:
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+    }
+
+    /* 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(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;
+}
index a4e35a90540d0454c8e2810429d338cca96da46c..49d6b2837c15ba80bb6da4c30559127d1b30fe27 100644 (file)
@@ -1,5 +1,4 @@
 /*
-  $Id: ea_sys.c,v 1.8 2010-04-13 08:05:06 franklahm Exp $
   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
 #include <fcntl.h>
 #include <dirent.h>
 
-#if HAVE_ATTR_XATTR_H
-#include <attr/xattr.h>
-#elif HAVE_SYS_XATTR_H
-#include <sys/xattr.h>
-#endif
-
-#ifdef HAVE_SYS_EA_H
-#include <sys/ea.h>
-#endif
-
-#ifdef HAVE_SYS_EXTATTR_H
-#include <sys/extattr.h>
-#endif
-
 #include <atalk/adouble.h>
 #include <atalk/ea.h>
 #include <atalk/afp.h>
diff --git a/libatalk/vfs/extattr.c b/libatalk/vfs/extattr.c
new file mode 100644 (file)
index 0000000..76b435c
--- /dev/null
@@ -0,0 +1,752 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba system utilities
+   Copyright (C) Andrew Tridgell 1992-1998
+   Copyright (C) Jeremy Allison  1998-2005
+   Copyright (C) Timur Bakeyev        2005
+   Copyright (C) Bjoern Jacke    2006-2007
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   
+   sys_copyxattr modified from LGPL2.1 libattr copyright
+   Copyright (C) 2001-2002 Silicon Graphics, Inc.  All Rights Reserved.
+   Copyright (C) 2001 Andreas Gruenbacher.
+      
+   Samba 3.0.28, modified for netatalk.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#if HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#elif HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
+
+#ifdef HAVE_SYS_EA_H
+#include <sys/ea.h>
+#endif
+
+#ifdef HAVE_ATTROPEN
+
+#include <dirent.h>
+#endif
+
+#ifdef HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+
+#include <atalk/adouble.h>
+#include <atalk/util.h>
+#include <atalk/logger.h>
+#include <atalk/ea.h>
+
+#ifndef ENOATTR
+#define ENOATTR ENODATA
+#endif
+
+/******** Solaris EA helper function prototypes ********/
+#ifdef HAVE_ATTROPEN
+#define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
+static int solaris_write_xattr(int attrfd, const char *value, size_t size);
+static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
+static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
+static int solaris_unlinkat(int attrdirfd, const char *name);
+static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
+static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
+#endif
+
+/**************************************************************************
+ Wrappers for extented attribute calls. Based on the Linux package with
+ support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
+****************************************************************************/
+static char attr_name[256 +5] = "user.";
+
+static const char *prefix(const char *uname)
+{
+#if defined(HAVE_ATTROPEN)
+       return uname;
+#else
+       strlcpy(attr_name +5, uname, 256);
+       return attr_name;
+#endif
+}
+
+ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
+{
+       const char *name = prefix(uname);
+
+#if defined(HAVE_GETXATTR)
+#ifndef XATTR_ADD_OPT
+       return getxattr(path, name, value, size);
+#else
+       int options = 0;
+       return getxattr(path, name, value, size, 0, options);
+#endif
+#elif defined(HAVE_GETEA)
+       return getea(path, name, value, size);
+#elif defined(HAVE_EXTATTR_GET_FILE)
+       ssize_t retval;
+       /*
+        * The BSD implementation has a nasty habit of silently truncating
+        * the returned value to the size of the buffer, so we have to check
+        * that the buffer is large enough to fit the returned value.
+        */
+       if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
+        if (size == 0)
+            /* size == 0 means only return size */
+            return retval;
+               if (retval > size) {
+                       errno = ERANGE;
+                       return -1;
+               }
+               if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
+                       return retval;
+       }
+
+       LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
+       return -1;
+#elif defined(HAVE_ATTR_GET)
+       int retval, flags = 0;
+       int valuelength = (int)size;
+       char *attrname = strchr(name,'.') + 1;
+       
+       if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+       retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
+
+       return retval ? retval : valuelength;
+#elif defined(HAVE_ATTROPEN)
+       ssize_t ret = -1;
+       int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
+       if (attrfd >= 0) {
+               ret = solaris_read_xattr(attrfd, value, size);
+               close(attrfd);
+       }
+       return ret;
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
+{
+       const char *name = prefix(uname);
+
+#if defined(HAVE_LGETXATTR)
+       return lgetxattr(path, name, value, size);
+#elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
+       int options = XATTR_NOFOLLOW;
+       return getxattr(path, name, value, size, 0, options);
+#elif defined(HAVE_LGETEA)
+       return lgetea(path, name, value, size);
+#elif defined(HAVE_EXTATTR_GET_LINK)
+       ssize_t retval;
+       if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
+               if(retval > size) {
+                       errno = ERANGE;
+                       return -1;
+               }
+               if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
+                       return retval;
+       }
+       
+       LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
+       return -1;
+#elif defined(HAVE_ATTR_GET)
+       int retval, flags = ATTR_DONTFOLLOW;
+       int valuelength = (int)size;
+       char *attrname = strchr(name,'.') + 1;
+       
+       if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+       retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
+
+       return retval ? retval : valuelength;
+#elif defined(HAVE_ATTROPEN)
+       ssize_t ret = -1;
+       int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
+       if (attrfd >= 0) {
+               ret = solaris_read_xattr(attrfd, value, size);
+               close(attrfd);
+       }
+       return ret;
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+#if defined(HAVE_EXTATTR_LIST_FILE)
+
+#define EXTATTR_PREFIX(s)      (s), (sizeof((s))-1)
+
+static struct {
+        int space;
+       const char *name;
+       size_t len;
+} 
+extattr[] = {
+       { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
+        { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
+};
+
+typedef union {
+       const char *path;
+       int filedes;
+} extattr_arg;
+
+static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
+{
+       ssize_t list_size;
+       int i, len;
+
+    switch(type) {
+#if defined(HAVE_EXTATTR_LIST_FILE)
+    case 0:
+        list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
+        break;
+#endif
+#if defined(HAVE_EXTATTR_LIST_LINK)
+    case 1:
+        list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
+        break;
+#endif
+#if defined(HAVE_EXTATTR_LIST_FD)
+    case 2:
+        list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
+        break;
+#endif
+    default:
+        errno = ENOSYS;
+        return -1;
+    }
+
+    /* Some error happend. Errno should be set by the previous call */
+    if(list_size < 0)
+        return -1;
+
+    /* No attributes */
+    if(list_size == 0)
+        return 0;
+
+    /* XXX: Call with an empty buffer may be used to calculate
+       necessary buffer size. Unfortunately, we can't say, how
+       many attributes were returned, so here is the potential
+       problem with the emulation.
+    */
+    if(list == NULL)
+        return list_size;
+
+    /* Buffer is too small to fit the results */
+    if(list_size > size) {
+        errno = ERANGE;
+        return -1;
+    }
+
+    /* Convert from pascal strings to C strings */
+    len = list[0];
+    memmove(list, list + 1, list_size);
+
+    for(i = len; i < list_size; ) {
+        LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
+
+        len = list[i];
+        list[i] = '\0';
+        i += len + 1;
+    }
+
+       return list_size;
+}
+
+#endif
+
+#if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
+static char attr_buffer[ATTR_MAX_VALUELEN];
+
+static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
+{
+       int retval = 0, index;
+       attrlist_cursor_t *cursor = 0;
+       int total_size = 0;
+       attrlist_t * al = (attrlist_t *)attr_buffer;
+       attrlist_ent_t *ae;
+       size_t ent_size, left = size;
+       char *bp = list;
+
+       while (True) {
+           if (filedes)
+               retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+           else
+               retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+           if (retval) break;
+           for (index = 0; index < al->al_count; index++) {
+               ae = ATTR_ENTRY(attr_buffer, index);
+               ent_size = strlen(ae->a_name) + sizeof("user.");
+               if (left >= ent_size) {
+                   strncpy(bp, "user.", sizeof("user."));
+                   strncat(bp, ae->a_name, ent_size - sizeof("user."));
+                   bp += ent_size;
+                   left -= ent_size;
+               } else if (size) {
+                   errno = ERANGE;
+                   retval = -1;
+                   break;
+               }
+               total_size += ent_size;
+           }
+           if (al->al_more == 0) break;
+       }
+       if (retval == 0) {
+           flags |= ATTR_ROOT;
+           cursor = 0;
+           while (True) {
+               if (filedes)
+                   retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+               else
+                   retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
+               if (retval) break;
+               for (index = 0; index < al->al_count; index++) {
+                   ae = ATTR_ENTRY(attr_buffer, index);
+                   ent_size = strlen(ae->a_name) + sizeof("system.");
+                   if (left >= ent_size) {
+                       strncpy(bp, "system.", sizeof("system."));
+                       strncat(bp, ae->a_name, ent_size - sizeof("system."));
+                       bp += ent_size;
+                       left -= ent_size;
+                   } else if (size) {
+                       errno = ERANGE;
+                       retval = -1;
+                       break;
+                   }
+                   total_size += ent_size;
+               }
+               if (al->al_more == 0) break;
+           }
+       }
+       return (ssize_t)(retval ? retval : total_size);
+}
+
+#endif
+
+#if defined(HAVE_LISTXATTR)
+static ssize_t remove_user(ssize_t ret, char *list, size_t size)
+{
+       size_t len;
+       char *ptr;
+       char *ptr1;
+       ssize_t ptrsize;
+       
+       if (ret <= 0 || size == 0)
+               return ret;
+       ptrsize = ret;
+       ptr = ptr1 = list;
+       while (ptrsize > 0) {
+               len = strlen(ptr1) +1;
+               ptrsize -= len;
+               if (strncmp(ptr1, "user.",5)) {
+                       ptr1 += len;
+                       continue;
+               }
+               memmove(ptr, ptr1 +5, len -5);
+               ptr += len -5;
+               ptr1 += len;
+       }
+       return ptr -list;
+}
+#endif
+
+ssize_t sys_listxattr (const char *path, char *list, size_t size)
+{
+#if defined(HAVE_LISTXATTR)
+       ssize_t ret;
+
+#ifndef XATTR_ADD_OPT
+       ret = listxattr(path, list, size);
+#else
+       int options = 0;
+       ret = listxattr(path, list, size, options);
+#endif
+       return remove_user(ret, list, size);
+
+#elif defined(HAVE_LISTEA)
+       return listea(path, list, size);
+#elif defined(HAVE_EXTATTR_LIST_FILE)
+       extattr_arg arg;
+       arg.path = path;
+       return bsd_attr_list(0, arg, list, size);
+#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
+       return irix_attr_list(path, 0, list, size, 0);
+#elif defined(HAVE_ATTROPEN)
+       ssize_t ret = -1;
+       int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
+       if (attrdirfd >= 0) {
+               ret = solaris_list_xattr(attrdirfd, list, size);
+               close(attrdirfd);
+       }
+       return ret;
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+ssize_t sys_llistxattr (const char *path, char *list, size_t size)
+{
+#if defined(HAVE_LLISTXATTR)
+       ssize_t ret;
+
+       ret = llistxattr(path, list, size);
+       return remove_user(ret, list, size);
+#elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
+       ssize_t ret;
+       int options = XATTR_NOFOLLOW;
+
+       ret = listxattr(path, list, size, options);
+       return remove_user(ret, list, size);
+
+#elif defined(HAVE_LLISTEA)
+       return llistea(path, list, size);
+#elif defined(HAVE_EXTATTR_LIST_LINK)
+       extattr_arg arg;
+       arg.path = path;
+       return bsd_attr_list(1, arg, list, size);
+#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
+       return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
+#elif defined(HAVE_ATTROPEN)
+       ssize_t ret = -1;
+       int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
+       if (attrdirfd >= 0) {
+               ret = solaris_list_xattr(attrdirfd, list, size);
+               close(attrdirfd);
+       }
+       return ret;
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+int sys_removexattr (const char *path, const char *uname)
+{
+       const char *name = prefix(uname);
+#if defined(HAVE_REMOVEXATTR)
+#ifndef XATTR_ADD_OPT
+       return removexattr(path, name);
+#else
+       int options = 0;
+       return removexattr(path, name, options);
+#endif
+#elif defined(HAVE_REMOVEEA)
+       return removeea(path, name);
+#elif defined(HAVE_EXTATTR_DELETE_FILE)
+       return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
+#elif defined(HAVE_ATTR_REMOVE)
+       int flags = 0;
+       char *attrname = strchr(name,'.') + 1;
+       
+       if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+       return attr_remove(path, attrname, flags);
+#elif defined(HAVE_ATTROPEN)
+       int ret = -1;
+       int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
+       if (attrdirfd >= 0) {
+               ret = solaris_unlinkat(attrdirfd, name);
+               close(attrdirfd);
+       }
+       return ret;
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+int sys_lremovexattr (const char *path, const char *uname)
+{
+       const char *name = prefix(uname);
+#if defined(HAVE_LREMOVEXATTR)
+       return lremovexattr(path, name);
+#elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
+       int options = XATTR_NOFOLLOW;
+       return removexattr(path, name, options);
+#elif defined(HAVE_LREMOVEEA)
+       return lremoveea(path, name);
+#elif defined(HAVE_EXTATTR_DELETE_LINK)
+       return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
+#elif defined(HAVE_ATTR_REMOVE)
+       int flags = ATTR_DONTFOLLOW;
+       char *attrname = strchr(name,'.') + 1;
+       
+       if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
+
+       return attr_remove(path, attrname, flags);
+#elif defined(HAVE_ATTROPEN)
+       int ret = -1;
+       int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
+       if (attrdirfd >= 0) {
+               ret = solaris_unlinkat(attrdirfd, name);
+               close(attrdirfd);
+       }
+       return ret;
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
+{
+       const char *name = prefix(uname);
+#if defined(HAVE_SETXATTR)
+#ifndef XATTR_ADD_OPT
+       return setxattr(path, name, value, size, flags);
+#else
+       int options = 0;
+       return setxattr(path, name, value, size, 0, options);
+#endif
+#elif defined(HAVE_SETEA)
+       return setea(path, name, value, size, flags);
+#elif defined(HAVE_EXTATTR_SET_FILE)
+       int retval = 0;
+       if (flags) {
+               /* Check attribute existence */
+               retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
+               if (retval < 0) {
+                       /* REPLACE attribute, that doesn't exist */
+                       if (flags & XATTR_REPLACE && errno == ENOATTR) {
+                               errno = ENOATTR;
+                               return -1;
+                       }
+                       /* Ignore other errors */
+               }
+               else {
+                       /* CREATE attribute, that already exists */
+                       if (flags & XATTR_CREATE) {
+                               errno = EEXIST;
+                               return -1;
+                       }
+               }
+       }
+       retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
+       return (retval < 0) ? -1 : 0;
+#elif defined(HAVE_ATTR_SET)
+       int myflags = 0;
+       char *attrname = strchr(name,'.') + 1;
+       
+       if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
+       if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
+       if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
+
+       return attr_set(path, attrname, (const char *)value, size, myflags);
+#elif defined(HAVE_ATTROPEN)
+       int ret = -1;
+       int myflags = O_RDWR;
+       int attrfd;
+       if (flags & XATTR_CREATE) myflags |= O_EXCL;
+       if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
+       attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
+       if (attrfd >= 0) {
+               ret = solaris_write_xattr(attrfd, value, size);
+               close(attrfd);
+       }
+       return ret;
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
+{
+       const char *name = prefix(uname);
+#if defined(HAVE_LSETXATTR)
+       return lsetxattr(path, name, value, size, flags);
+#elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
+       int options = XATTR_NOFOLLOW;
+       return setxattr(path, name, value, size, 0, options);
+#elif defined(LSETEA)
+       return lsetea(path, name, value, size, flags);
+#elif defined(HAVE_EXTATTR_SET_LINK)
+       int retval = 0;
+       if (flags) {
+               /* Check attribute existence */
+               retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
+               if (retval < 0) {
+                       /* REPLACE attribute, that doesn't exist */
+                       if (flags & XATTR_REPLACE && errno == ENOATTR) {
+                               errno = ENOATTR;
+                               return -1;
+                       }
+                       /* Ignore other errors */
+               }
+               else {
+                       /* CREATE attribute, that already exists */
+                       if (flags & XATTR_CREATE) {
+                               errno = EEXIST;
+                               return -1;
+                       }
+               }
+       }
+
+       retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
+       return (retval < 0) ? -1 : 0;
+#elif defined(HAVE_ATTR_SET)
+       int myflags = ATTR_DONTFOLLOW;
+       char *attrname = strchr(name,'.') + 1;
+       
+       if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
+       if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
+       if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
+
+       return attr_set(path, attrname, (const char *)value, size, myflags);
+#elif defined(HAVE_ATTROPEN)
+       int ret = -1;
+       int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
+       int attrfd;
+       if (flags & XATTR_CREATE) myflags |= O_EXCL;
+       if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
+       attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
+       if (attrfd >= 0) {
+               ret = solaris_write_xattr(attrfd, value, size);
+               close(attrfd);
+       }
+       return ret;
+#else
+       errno = ENOSYS;
+       return -1;
+#endif
+}
+
+/**************************************************************************
+ helper functions for Solaris' EA support
+****************************************************************************/
+#ifdef HAVE_ATTROPEN
+static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
+{
+       struct stat sbuf;
+
+       if (fstat(attrfd, &sbuf) == -1) {
+               return -1;
+       }
+
+       /* This is to return the current size of the named extended attribute */
+       if (size == 0) {
+               return sbuf.st_size;
+       }
+
+       /* check size and read xattr */
+       if (sbuf.st_size > size) {
+               return -1;
+       }
+
+       return read(attrfd, value, sbuf.st_size);
+}
+
+static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
+{
+       ssize_t len = 0;
+       DIR *dirp;
+       struct dirent *de;
+       int newfd = dup(attrdirfd);
+       /* CAUTION: The originating file descriptor should not be
+                   used again following the call to fdopendir().
+                   For that reason we dup() the file descriptor
+                   here to make things more clear. */
+       dirp = fdopendir(newfd);
+
+       while ((de = readdir(dirp))) {
+               size_t listlen;
+               if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
+                    !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw")) 
+               {
+                       /* we don't want "." and ".." here: */
+                       LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
+                       continue;
+               }
+
+               listlen = strlen(de->d_name);
+               if (size == 0) {
+                       /* return the current size of the list of extended attribute names*/
+                       len += listlen + 1;
+               } else {
+                       /* check size and copy entry + nul into list. */
+                       if ((len + listlen + 1) > size) {
+                               errno = ERANGE;
+                               len = -1;
+                               break;
+                       } else {
+                               strcpy(list + len, de->d_name);
+                               len += listlen;
+                               list[len] = '\0';
+                               ++len;
+                       }
+               }
+       }
+
+       if (closedir(dirp) == -1) {
+               LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
+               return -1;
+       }
+       return len;
+}
+
+static int solaris_unlinkat(int attrdirfd, const char *name)
+{
+       if (unlinkat(attrdirfd, name, 0) == -1) {
+               return -1;
+       }
+       return 0;
+}
+
+static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
+{
+       int filedes = attropen(path, attrpath, oflag, mode);
+       if (filedes == -1) {
+               LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno));
+        errno = ENOATTR;
+       }
+       return filedes;
+}
+
+static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
+{
+       int filedes = openat(fildes, path, oflag, mode);
+       if (filedes == -1) {
+               LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno));
+       }
+       return filedes;
+}
+
+static int solaris_write_xattr(int attrfd, const char *value, size_t size)
+{
+       if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
+               return 0;
+       } else {
+               LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
+               return -1;
+       }
+}
+
+#endif /*HAVE_ATTROPEN*/
+
diff --git a/libatalk/vfs/sys_ea.c b/libatalk/vfs/sys_ea.c
deleted file mode 100644 (file)
index 900eb07..0000000
+++ /dev/null
@@ -1,754 +0,0 @@
-/* 
-   Unix SMB/CIFS implementation.
-   Samba system utilities
-   Copyright (C) Andrew Tridgell 1992-1998
-   Copyright (C) Jeremy Allison  1998-2005
-   Copyright (C) Timur Bakeyev        2005
-   Copyright (C) Bjoern Jacke    2006-2007
-   
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-   
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-   
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-   
-   sys_copyxattr modified from LGPL2.1 libattr copyright
-   Copyright (C) 2001-2002 Silicon Graphics, Inc.  All Rights Reserved.
-   Copyright (C) 2001 Andreas Gruenbacher.
-      
-   Samba 3.0.28, modified for netatalk.
-   $Id: sys_ea.c,v 1.6 2009-12-04 10:26:10 franklahm Exp $
-   
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <errno.h>
-
-#if HAVE_ATTR_XATTR_H
-#include <attr/xattr.h>
-#elif HAVE_SYS_XATTR_H
-#include <sys/xattr.h>
-#endif
-
-#ifdef HAVE_SYS_EA_H
-#include <sys/ea.h>
-#endif
-
-#ifdef HAVE_ATTROPEN
-
-#include <dirent.h>
-#endif
-
-#ifdef HAVE_SYS_EXTATTR_H
-#include <sys/extattr.h>
-#endif
-
-#include <atalk/adouble.h>
-#include <atalk/util.h>
-#include <atalk/logger.h>
-#include <atalk/ea.h>
-
-#ifndef ENOATTR
-#define ENOATTR ENODATA
-#endif
-
-/******** Solaris EA helper function prototypes ********/
-#ifdef HAVE_ATTROPEN
-#define SOLARIS_ATTRMODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
-static int solaris_write_xattr(int attrfd, const char *value, size_t size);
-static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size);
-static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size);
-static int solaris_unlinkat(int attrdirfd, const char *name);
-static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode);
-static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode);
-#endif
-
-/**************************************************************************
- Wrappers for extented attribute calls. Based on the Linux package with
- support for IRIX and (Net|Free)BSD also. Expand as other systems have them.
-****************************************************************************/
-static char attr_name[256 +5] = "user.";
-
-static const char *prefix(const char *uname)
-{
-#if defined(HAVE_ATTROPEN)
-       return uname;
-#else
-       strlcpy(attr_name +5, uname, 256);
-       return attr_name;
-#endif
-}
-
-ssize_t sys_getxattr (const char *path, const char *uname, void *value, size_t size)
-{
-       const char *name = prefix(uname);
-
-#if defined(HAVE_GETXATTR)
-#ifndef XATTR_ADD_OPT
-       return getxattr(path, name, value, size);
-#else
-       int options = 0;
-       return getxattr(path, name, value, size, 0, options);
-#endif
-#elif defined(HAVE_GETEA)
-       return getea(path, name, value, size);
-#elif defined(HAVE_EXTATTR_GET_FILE)
-       ssize_t retval;
-       /*
-        * The BSD implementation has a nasty habit of silently truncating
-        * the returned value to the size of the buffer, so we have to check
-        * that the buffer is large enough to fit the returned value.
-        */
-       if((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
-        if (size == 0)
-            /* size == 0 means only return size */
-            return retval;
-               if (retval > size) {
-                       errno = ERANGE;
-                       return -1;
-               }
-               if ((retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
-                       return retval;
-       }
-
-       LOG(log_maxdebug, logtype_default, "sys_getxattr: extattr_get_file() failed with: %s\n", strerror(errno));
-       return -1;
-#elif defined(HAVE_ATTR_GET)
-       int retval, flags = 0;
-       int valuelength = (int)size;
-       char *attrname = strchr(name,'.') + 1;
-       
-       if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
-
-       retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
-
-       return retval ? retval : valuelength;
-#elif defined(HAVE_ATTROPEN)
-       ssize_t ret = -1;
-       int attrfd = solaris_attropen(path, name, O_RDONLY, 0);
-       if (attrfd >= 0) {
-               ret = solaris_read_xattr(attrfd, value, size);
-               close(attrfd);
-       }
-       return ret;
-#else
-       errno = ENOSYS;
-       return -1;
-#endif
-}
-
-ssize_t sys_lgetxattr (const char *path, const char *uname, void *value, size_t size)
-{
-       const char *name = prefix(uname);
-
-#if defined(HAVE_LGETXATTR)
-       return lgetxattr(path, name, value, size);
-#elif defined(HAVE_GETXATTR) && defined(XATTR_ADD_OPT)
-       int options = XATTR_NOFOLLOW;
-       return getxattr(path, name, value, size, 0, options);
-#elif defined(HAVE_LGETEA)
-       return lgetea(path, name, value, size);
-#elif defined(HAVE_EXTATTR_GET_LINK)
-       ssize_t retval;
-       if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0)) >= 0) {
-               if(retval > size) {
-                       errno = ERANGE;
-                       return -1;
-               }
-               if((retval=extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, value, size)) >= 0)
-                       return retval;
-       }
-       
-       LOG(log_maxdebug, logtype_default, "sys_lgetxattr: extattr_get_link() failed with: %s\n", strerror(errno));
-       return -1;
-#elif defined(HAVE_ATTR_GET)
-       int retval, flags = ATTR_DONTFOLLOW;
-       int valuelength = (int)size;
-       char *attrname = strchr(name,'.') + 1;
-       
-       if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
-
-       retval = attr_get(path, attrname, (char *)value, &valuelength, flags);
-
-       return retval ? retval : valuelength;
-#elif defined(HAVE_ATTROPEN)
-       ssize_t ret = -1;
-       int attrfd = solaris_attropen(path, name, O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
-       if (attrfd >= 0) {
-               ret = solaris_read_xattr(attrfd, value, size);
-               close(attrfd);
-       }
-       return ret;
-#else
-       errno = ENOSYS;
-       return -1;
-#endif
-}
-
-#if defined(HAVE_EXTATTR_LIST_FILE)
-
-#define EXTATTR_PREFIX(s)      (s), (sizeof((s))-1)
-
-static struct {
-        int space;
-       const char *name;
-       size_t len;
-} 
-extattr[] = {
-       { EXTATTR_NAMESPACE_SYSTEM, EXTATTR_PREFIX("") },
-        { EXTATTR_NAMESPACE_USER, EXTATTR_PREFIX("") },
-};
-
-typedef union {
-       const char *path;
-       int filedes;
-} extattr_arg;
-
-static ssize_t bsd_attr_list (int type, extattr_arg arg, char *list, size_t size)
-{
-       ssize_t list_size;
-       int i, len;
-
-    switch(type) {
-#if defined(HAVE_EXTATTR_LIST_FILE)
-    case 0:
-        list_size = extattr_list_file(arg.path, EXTATTR_NAMESPACE_USER, list, size);
-        break;
-#endif
-#if defined(HAVE_EXTATTR_LIST_LINK)
-    case 1:
-        list_size = extattr_list_link(arg.path, EXTATTR_NAMESPACE_USER, list, size);
-        break;
-#endif
-#if defined(HAVE_EXTATTR_LIST_FD)
-    case 2:
-        list_size = extattr_list_fd(arg.filedes, EXTATTR_NAMESPACE_USER, list, size);
-        break;
-#endif
-    default:
-        errno = ENOSYS;
-        return -1;
-    }
-
-    /* Some error happend. Errno should be set by the previous call */
-    if(list_size < 0)
-        return -1;
-
-    /* No attributes */
-    if(list_size == 0)
-        return 0;
-
-    /* XXX: Call with an empty buffer may be used to calculate
-       necessary buffer size. Unfortunately, we can't say, how
-       many attributes were returned, so here is the potential
-       problem with the emulation.
-    */
-    if(list == NULL)
-        return list_size;
-
-    /* Buffer is too small to fit the results */
-    if(list_size > size) {
-        errno = ERANGE;
-        return -1;
-    }
-
-    /* Convert from pascal strings to C strings */
-    len = list[0];
-    memmove(list, list + 1, list_size);
-
-    for(i = len; i < list_size; ) {
-        LOG(log_maxdebug, logtype_afpd, "len: %d, i: %d", len, i);
-
-        len = list[i];
-        list[i] = '\0';
-        i += len + 1;
-    }
-
-       return list_size;
-}
-
-#endif
-
-#if defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
-static char attr_buffer[ATTR_MAX_VALUELEN];
-
-static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags)
-{
-       int retval = 0, index;
-       attrlist_cursor_t *cursor = 0;
-       int total_size = 0;
-       attrlist_t * al = (attrlist_t *)attr_buffer;
-       attrlist_ent_t *ae;
-       size_t ent_size, left = size;
-       char *bp = list;
-
-       while (True) {
-           if (filedes)
-               retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
-           else
-               retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
-           if (retval) break;
-           for (index = 0; index < al->al_count; index++) {
-               ae = ATTR_ENTRY(attr_buffer, index);
-               ent_size = strlen(ae->a_name) + sizeof("user.");
-               if (left >= ent_size) {
-                   strncpy(bp, "user.", sizeof("user."));
-                   strncat(bp, ae->a_name, ent_size - sizeof("user."));
-                   bp += ent_size;
-                   left -= ent_size;
-               } else if (size) {
-                   errno = ERANGE;
-                   retval = -1;
-                   break;
-               }
-               total_size += ent_size;
-           }
-           if (al->al_more == 0) break;
-       }
-       if (retval == 0) {
-           flags |= ATTR_ROOT;
-           cursor = 0;
-           while (True) {
-               if (filedes)
-                   retval = attr_listf(filedes, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
-               else
-                   retval = attr_list(path, attr_buffer, ATTR_MAX_VALUELEN, flags, cursor);
-               if (retval) break;
-               for (index = 0; index < al->al_count; index++) {
-                   ae = ATTR_ENTRY(attr_buffer, index);
-                   ent_size = strlen(ae->a_name) + sizeof("system.");
-                   if (left >= ent_size) {
-                       strncpy(bp, "system.", sizeof("system."));
-                       strncat(bp, ae->a_name, ent_size - sizeof("system."));
-                       bp += ent_size;
-                       left -= ent_size;
-                   } else if (size) {
-                       errno = ERANGE;
-                       retval = -1;
-                       break;
-                   }
-                   total_size += ent_size;
-               }
-               if (al->al_more == 0) break;
-           }
-       }
-       return (ssize_t)(retval ? retval : total_size);
-}
-
-#endif
-
-#if defined(HAVE_LISTXATTR)
-static ssize_t remove_user(ssize_t ret, char *list, size_t size)
-{
-       size_t len;
-       char *ptr;
-       char *ptr1;
-       ssize_t ptrsize;
-       
-       if (ret <= 0 || size == 0)
-               return ret;
-       ptrsize = ret;
-       ptr = ptr1 = list;
-       while (ptrsize > 0) {
-               len = strlen(ptr1) +1;
-               ptrsize -= len;
-               if (strncmp(ptr1, "user.",5)) {
-                       ptr1 += len;
-                       continue;
-               }
-               memmove(ptr, ptr1 +5, len -5);
-               ptr += len -5;
-               ptr1 += len;
-       }
-       return ptr -list;
-}
-#endif
-
-ssize_t sys_listxattr (const char *path, char *list, size_t size)
-{
-#if defined(HAVE_LISTXATTR)
-       ssize_t ret;
-
-#ifndef XATTR_ADD_OPT
-       ret = listxattr(path, list, size);
-#else
-       int options = 0;
-       ret = listxattr(path, list, size, options);
-#endif
-       return remove_user(ret, list, size);
-
-#elif defined(HAVE_LISTEA)
-       return listea(path, list, size);
-#elif defined(HAVE_EXTATTR_LIST_FILE)
-       extattr_arg arg;
-       arg.path = path;
-       return bsd_attr_list(0, arg, list, size);
-#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
-       return irix_attr_list(path, 0, list, size, 0);
-#elif defined(HAVE_ATTROPEN)
-       ssize_t ret = -1;
-       int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
-       if (attrdirfd >= 0) {
-               ret = solaris_list_xattr(attrdirfd, list, size);
-               close(attrdirfd);
-       }
-       return ret;
-#else
-       errno = ENOSYS;
-       return -1;
-#endif
-}
-
-ssize_t sys_llistxattr (const char *path, char *list, size_t size)
-{
-#if defined(HAVE_LLISTXATTR)
-       ssize_t ret;
-
-       ret = llistxattr(path, list, size);
-       return remove_user(ret, list, size);
-#elif defined(HAVE_LISTXATTR) && defined(XATTR_ADD_OPT)
-       ssize_t ret;
-       int options = XATTR_NOFOLLOW;
-
-       ret = listxattr(path, list, size, options);
-       return remove_user(ret, list, size);
-
-#elif defined(HAVE_LLISTEA)
-       return llistea(path, list, size);
-#elif defined(HAVE_EXTATTR_LIST_LINK)
-       extattr_arg arg;
-       arg.path = path;
-       return bsd_attr_list(1, arg, list, size);
-#elif defined(HAVE_ATTR_LIST) && defined(HAVE_SYS_ATTRIBUTES_H)
-       return irix_attr_list(path, 0, list, size, ATTR_DONTFOLLOW);
-#elif defined(HAVE_ATTROPEN)
-       ssize_t ret = -1;
-       int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
-       if (attrdirfd >= 0) {
-               ret = solaris_list_xattr(attrdirfd, list, size);
-               close(attrdirfd);
-       }
-       return ret;
-#else
-       errno = ENOSYS;
-       return -1;
-#endif
-}
-
-int sys_removexattr (const char *path, const char *uname)
-{
-       const char *name = prefix(uname);
-#if defined(HAVE_REMOVEXATTR)
-#ifndef XATTR_ADD_OPT
-       return removexattr(path, name);
-#else
-       int options = 0;
-       return removexattr(path, name, options);
-#endif
-#elif defined(HAVE_REMOVEEA)
-       return removeea(path, name);
-#elif defined(HAVE_EXTATTR_DELETE_FILE)
-       return extattr_delete_file(path, EXTATTR_NAMESPACE_USER, uname);
-#elif defined(HAVE_ATTR_REMOVE)
-       int flags = 0;
-       char *attrname = strchr(name,'.') + 1;
-       
-       if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
-
-       return attr_remove(path, attrname, flags);
-#elif defined(HAVE_ATTROPEN)
-       int ret = -1;
-       int attrdirfd = solaris_attropen(path, ".", O_RDONLY, 0);
-       if (attrdirfd >= 0) {
-               ret = solaris_unlinkat(attrdirfd, name);
-               close(attrdirfd);
-       }
-       return ret;
-#else
-       errno = ENOSYS;
-       return -1;
-#endif
-}
-
-int sys_lremovexattr (const char *path, const char *uname)
-{
-       const char *name = prefix(uname);
-#if defined(HAVE_LREMOVEXATTR)
-       return lremovexattr(path, name);
-#elif defined(HAVE_REMOVEXATTR) && defined(XATTR_ADD_OPT)
-       int options = XATTR_NOFOLLOW;
-       return removexattr(path, name, options);
-#elif defined(HAVE_LREMOVEEA)
-       return lremoveea(path, name);
-#elif defined(HAVE_EXTATTR_DELETE_LINK)
-       return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, uname);
-#elif defined(HAVE_ATTR_REMOVE)
-       int flags = ATTR_DONTFOLLOW;
-       char *attrname = strchr(name,'.') + 1;
-       
-       if (strncmp(name, "system", 6) == 0) flags |= ATTR_ROOT;
-
-       return attr_remove(path, attrname, flags);
-#elif defined(HAVE_ATTROPEN)
-       int ret = -1;
-       int attrdirfd = solaris_attropen(path, ".", O_RDONLY|AT_SYMLINK_NOFOLLOW, 0);
-       if (attrdirfd >= 0) {
-               ret = solaris_unlinkat(attrdirfd, name);
-               close(attrdirfd);
-       }
-       return ret;
-#else
-       errno = ENOSYS;
-       return -1;
-#endif
-}
-
-int sys_setxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
-{
-       const char *name = prefix(uname);
-#if defined(HAVE_SETXATTR)
-#ifndef XATTR_ADD_OPT
-       return setxattr(path, name, value, size, flags);
-#else
-       int options = 0;
-       return setxattr(path, name, value, size, 0, options);
-#endif
-#elif defined(HAVE_SETEA)
-       return setea(path, name, value, size, flags);
-#elif defined(HAVE_EXTATTR_SET_FILE)
-       int retval = 0;
-       if (flags) {
-               /* Check attribute existence */
-               retval = extattr_get_file(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
-               if (retval < 0) {
-                       /* REPLACE attribute, that doesn't exist */
-                       if (flags & XATTR_REPLACE && errno == ENOATTR) {
-                               errno = ENOATTR;
-                               return -1;
-                       }
-                       /* Ignore other errors */
-               }
-               else {
-                       /* CREATE attribute, that already exists */
-                       if (flags & XATTR_CREATE) {
-                               errno = EEXIST;
-                               return -1;
-                       }
-               }
-       }
-       retval = extattr_set_file(path, EXTATTR_NAMESPACE_USER, uname, value, size);
-       return (retval < 0) ? -1 : 0;
-#elif defined(HAVE_ATTR_SET)
-       int myflags = 0;
-       char *attrname = strchr(name,'.') + 1;
-       
-       if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
-       if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
-       if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
-
-       return attr_set(path, attrname, (const char *)value, size, myflags);
-#elif defined(HAVE_ATTROPEN)
-       int ret = -1;
-       int myflags = O_RDWR;
-       int attrfd;
-       if (flags & XATTR_CREATE) myflags |= O_EXCL;
-       if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
-       attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
-       if (attrfd >= 0) {
-               ret = solaris_write_xattr(attrfd, value, size);
-               close(attrfd);
-       }
-       return ret;
-#else
-       errno = ENOSYS;
-       return -1;
-#endif
-}
-
-int sys_lsetxattr (const char *path, const char *uname, const void *value, size_t size, int flags)
-{
-       const char *name = prefix(uname);
-#if defined(HAVE_LSETXATTR)
-       return lsetxattr(path, name, value, size, flags);
-#elif defined(HAVE_SETXATTR) && defined(XATTR_ADD_OPT)
-       int options = XATTR_NOFOLLOW;
-       return setxattr(path, name, value, size, 0, options);
-#elif defined(LSETEA)
-       return lsetea(path, name, value, size, flags);
-#elif defined(HAVE_EXTATTR_SET_LINK)
-       int retval = 0;
-       if (flags) {
-               /* Check attribute existence */
-               retval = extattr_get_link(path, EXTATTR_NAMESPACE_USER, uname, NULL, 0);
-               if (retval < 0) {
-                       /* REPLACE attribute, that doesn't exist */
-                       if (flags & XATTR_REPLACE && errno == ENOATTR) {
-                               errno = ENOATTR;
-                               return -1;
-                       }
-                       /* Ignore other errors */
-               }
-               else {
-                       /* CREATE attribute, that already exists */
-                       if (flags & XATTR_CREATE) {
-                               errno = EEXIST;
-                               return -1;
-                       }
-               }
-       }
-
-       retval = extattr_set_link(path, EXTATTR_NAMESPACE_USER, uname, value, size);
-       return (retval < 0) ? -1 : 0;
-#elif defined(HAVE_ATTR_SET)
-       int myflags = ATTR_DONTFOLLOW;
-       char *attrname = strchr(name,'.') + 1;
-       
-       if (strncmp(name, "system", 6) == 0) myflags |= ATTR_ROOT;
-       if (flags & XATTR_CREATE) myflags |= ATTR_CREATE;
-       if (flags & XATTR_REPLACE) myflags |= ATTR_REPLACE;
-
-       return attr_set(path, attrname, (const char *)value, size, myflags);
-#elif defined(HAVE_ATTROPEN)
-       int ret = -1;
-       int myflags = O_RDWR | AT_SYMLINK_NOFOLLOW;
-       int attrfd;
-       if (flags & XATTR_CREATE) myflags |= O_EXCL;
-       if (!(flags & XATTR_REPLACE)) myflags |= O_CREAT;
-       attrfd = solaris_attropen(path, name, myflags, (mode_t) SOLARIS_ATTRMODE);
-       if (attrfd >= 0) {
-               ret = solaris_write_xattr(attrfd, value, size);
-               close(attrfd);
-       }
-       return ret;
-#else
-       errno = ENOSYS;
-       return -1;
-#endif
-}
-
-/**************************************************************************
- helper functions for Solaris' EA support
-****************************************************************************/
-#ifdef HAVE_ATTROPEN
-static ssize_t solaris_read_xattr(int attrfd, void *value, size_t size)
-{
-       struct stat sbuf;
-
-       if (fstat(attrfd, &sbuf) == -1) {
-               return -1;
-       }
-
-       /* This is to return the current size of the named extended attribute */
-       if (size == 0) {
-               return sbuf.st_size;
-       }
-
-       /* check size and read xattr */
-       if (sbuf.st_size > size) {
-               return -1;
-       }
-
-       return read(attrfd, value, sbuf.st_size);
-}
-
-static ssize_t solaris_list_xattr(int attrdirfd, char *list, size_t size)
-{
-       ssize_t len = 0;
-       DIR *dirp;
-       struct dirent *de;
-       int newfd = dup(attrdirfd);
-       /* CAUTION: The originating file descriptor should not be
-                   used again following the call to fdopendir().
-                   For that reason we dup() the file descriptor
-                   here to make things more clear. */
-       dirp = fdopendir(newfd);
-
-       while ((de = readdir(dirp))) {
-               size_t listlen;
-               if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
-                    !strcmp(de->d_name, "SUNWattr_ro") || !strcmp(de->d_name, "SUNWattr_rw")) 
-               {
-                       /* we don't want "." and ".." here: */
-                       LOG(log_maxdebug, logtype_default, "skipped EA %s\n",de->d_name);
-                       continue;
-               }
-
-               listlen = strlen(de->d_name);
-               if (size == 0) {
-                       /* return the current size of the list of extended attribute names*/
-                       len += listlen + 1;
-               } else {
-                       /* check size and copy entry + nul into list. */
-                       if ((len + listlen + 1) > size) {
-                               errno = ERANGE;
-                               len = -1;
-                               break;
-                       } else {
-                               strcpy(list + len, de->d_name);
-                               len += listlen;
-                               list[len] = '\0';
-                               ++len;
-                       }
-               }
-       }
-
-       if (closedir(dirp) == -1) {
-               LOG(log_debug, logtype_default, "closedir dirp failed: %s\n",strerror(errno));
-               return -1;
-       }
-       return len;
-}
-
-static int solaris_unlinkat(int attrdirfd, const char *name)
-{
-       if (unlinkat(attrdirfd, name, 0) == -1) {
-               return -1;
-       }
-       return 0;
-}
-
-static int solaris_attropen(const char *path, const char *attrpath, int oflag, mode_t mode)
-{
-       int filedes = attropen(path, attrpath, oflag, mode);
-       if (filedes == -1) {
-               LOG(log_maxdebug, logtype_default, "attropen FAILED: path: %s, name: %s, errno: %s\n",path,attrpath,strerror(errno));
-        errno = ENOATTR;
-       }
-       return filedes;
-}
-
-static int solaris_openat(int fildes, const char *path, int oflag, mode_t mode)
-{
-       int filedes = openat(fildes, path, oflag, mode);
-       if (filedes == -1) {
-               LOG(log_maxdebug, logtype_default, "openat FAILED: fd: %s, path: %s, errno: %s\n",filedes,path,strerror(errno));
-       }
-       return filedes;
-}
-
-static int solaris_write_xattr(int attrfd, const char *value, size_t size)
-{
-       if ((ftruncate(attrfd, 0) == 0) && (write(attrfd, value, size) == size)) {
-               return 0;
-       } else {
-               LOG(log_maxdebug, logtype_default, "solaris_write_xattr FAILED!\n");
-               return -1;
-       }
-}
-
-#endif /*HAVE_ATTROPEN*/
-
index 9c9dceb70c0a1f003a5d9104563255f43fb66647..ddb2ab50bbd37181d57e3d11be57f4e48cc87967 100644 (file)
@@ -24,7 +24,7 @@
 #include <sys/types.h>
 #include <netinet/in.h> /* so that we can deal with sun's s_net #define */
 
-#ifdef MACOSX_SERVER
+#if defined(MACOSX_SERVER) && (!defined(NO_DDP))
 #include <netat/appletalk.h>
 #endif /* MACOSX_SERVER */