]> arthur.barton.de Git - netatalk.git/commitdiff
Extended Attributes Support on Solaris with ZFS
authorfranklahm <franklahm>
Mon, 16 Feb 2009 13:49:19 +0000 (13:49 +0000)
committerfranklahm <franklahm>
Mon, 16 Feb 2009 13:49:19 +0000 (13:49 +0000)
configure.in
etc/afpd/Makefile.am
etc/afpd/auth.c
etc/afpd/extattrs.c [new file with mode: 0644]
etc/afpd/extattrs.h [new file with mode: 0644]
etc/afpd/status.c
etc/afpd/volume.c
etc/afpd/volume.h
macros/summary.m4

index 47336919dd92acc9b3d79fc6783dc23d9773e24f..5fc230437e441845875da270271ae917f3ac956b 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: configure.in,v 1.210 2009-02-02 11:55:00 franklahm Exp $
+dnl $Id: configure.in,v 1.211 2009-02-16 13:49:19 franklahm Exp $
 dnl configure.in for netatalk
 
 AC_INIT(etc/afpd/main.c)
@@ -1041,6 +1041,28 @@ else
 fi
 AC_SUBST(LIBATALK_ACLS)
 
+dnl --------------------- check for Extended Attributes support
+neta_cv_extattrs="no"
+AC_MSG_CHECKING([if Extended Attribute Support should be enabled])
+AC_ARG_ENABLE(extattrs,
+       [  --enable-extattrs       enable Extended Attributes],[
+       if test x"$enableval" = x"yes"; then
+          AC_MSG_RESULT([yes])
+          neta_cv_extattrs="yes"
+       else
+          AC_MSG_RESULT([no])
+       fi],[
+          AC_MSG_RESULT([no])
+       ]
+)
+if test x$neta_cv_extattrs = xyes; then
+       AC_CHECK_LIB(c,attropen,neta_cv_extattrs=yes,neta_cv_extattrs=no)
+fi
+if test x$neta_cv_extattrs = xyes; then
+       AC_MSG_NOTICE([Enabling Extended Attributes support])
+       AC_DEFINE([HAVE_EXT_ATTRS], 1, [Enable Extended Attributes])
+fi
+
 dnl --------------------- last minute substitutions
 
 AC_SUBST(LIBS)
@@ -1053,6 +1075,7 @@ AM_CONDITIONAL(COMPILE_A2BOOT, test x$compile_a2boot = xyes)
 AM_CONDITIONAL(HAVE_LIBGCRYPT, test x$neta_cv_have_libgcrypt = xyes)
 AM_CONDITIONAL(HAVE_OPENSSL, test x$neta_cv_have_openssl = xyes)
 AM_CONDITIONAL(USE_NFSv4_ACLS, test x$neta_cv_nfsv4acl = xyes)
+AM_CONDITIONAL(USE_EXT_ATTRS, test x$neta_cv_extattrs = xyes)
 AM_CONDITIONAL(USE_DHX, test x$neta_cv_compile_dhx = xyes)
 AM_CONDITIONAL(USE_DHX2, test x$neta_cv_compile_dhx2 = xyes)
 AM_CONDITIONAL(USE_RANDNUM, test x$neta_cv_have_openssl = xyes)
index ad641229a94ef40626b8c7ec52ed576af137433c..99cf63f050a4753afe580d6a18ecf1db1915748b 100644 (file)
@@ -14,6 +14,10 @@ if USE_NFSv4_ACLS
 afpd_SOURCES += acls.c
 endif
 
+if USE_EXT_ATTRS
+afpd_SOURCES += extattrs.c
+endif
+
 afpd_LDADD =  $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la
 afpd_LDFLAGS = -export-dynamic
 
index 3fb07961506914b981b616b1d2eec29dcacd3994..35878b889d73f02959adb53c5722a773f5b8d7a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: auth.c,v 1.55 2009-02-03 14:20:44 franklahm Exp $
+ * $Id: auth.c,v 1.56 2009-02-16 13:49:20 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -50,6 +50,9 @@ extern void afp_get_cmdline( int *ac, char ***av );
 #ifdef HAVE_NFSv4_ACLS
 #include "acls.h"
 #endif
+#ifdef HAVE_EXT_ATTRS
+#include "extattrs.h"
+#endif
 
 int    afp_version = 11;
 static int afp_version_index;
@@ -195,6 +198,12 @@ static int set_auth_switch(int expired)
            uam_afpserver_action(73, UAM_AFPSERVER_POSTAUTH, afp_getacl, NULL);
            uam_afpserver_action(74, UAM_AFPSERVER_POSTAUTH, afp_setacl, NULL);
            uam_afpserver_action(75, UAM_AFPSERVER_POSTAUTH, afp_access, NULL);
+#endif
+#ifdef HAVE_EXT_ATTRS
+           uam_afpserver_action(69, UAM_AFPSERVER_POSTAUTH, afp_getextattr, NULL);
+           uam_afpserver_action(70, UAM_AFPSERVER_POSTAUTH, afp_setextattr, NULL);
+           uam_afpserver_action(71, UAM_AFPSERVER_POSTAUTH, afp_remextattr, NULL);
+           uam_afpserver_action(72, UAM_AFPSERVER_POSTAUTH, afp_listextattr, NULL);
 #endif
         case 31:
            uam_afpserver_action(AFP_ENUMERATE_EXT2, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext2, NULL); 
diff --git a/etc/afpd/extattrs.c b/etc/afpd/extattrs.c
new file mode 100644 (file)
index 0000000..d4db85b
--- /dev/null
@@ -0,0 +1,705 @@
+/*
+  $Id: extattrs.c,v 1.1 2009-02-16 13:49:20 franklahm Exp $
+  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.
+*/
+
+/* According to man fsattr.5 we must define _ATFILE_SOURCE */
+#define _ATFILE_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_EXT_ATTRS
+
+#include <errno.h>
+#include <unistd.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/afp.h>
+#include <atalk/logger.h>
+
+#include "globals.h"
+#include "volume.h"
+#include "desktop.h"
+#include "directory.h"
+#include "fork.h"
+#include "extattrs.h"
+
+char *ea_finderinfo = "com.apple.FinderInfo";
+char *ea_resourcefork = "com.apple.ResourceFork";
+
+static void hexdump(void *m, size_t l) {
+    char *p = m;
+    int count = 0, len;
+    char buf[100];
+    char *bufp = buf;
+
+    while (l--) {
+        len = sprintf(bufp, "%02x ", *p++);
+        bufp += len;
+        count++;
+
+        if ((count % 16) == 0) {
+            LOG(log_debug9, logtype_afpd, "%s", buf);
+            bufp = buf;
+        }
+    }
+}
+
+static int getextattr_size(char *rbuf, int *rbuflen, char *uname, int oflag, char *attruname)
+{
+    int                 ret, attrdirfd;
+    uint32_t            attrsize;
+    struct stat         st;
+
+    LOG(log_debug7, logtype_afpd, "getextattr_size(%s): attribute: \"%s\"", uname, attruname);
+
+    if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
+        if (errno == ELOOP) {
+            /* its a symlink and client requested O_NOFOLLOW  */
+            LOG(log_debug, logtype_afpd, "getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
+
+            *(uint32_t *)rbuf = 0;
+            *rbuflen += 4;
+
+            return AFP_OK;
+        }
+        LOG(log_error, logtype_afpd, "getextattr_size: attropen error: %s", strerror(errno));
+        return AFPERR_MISC;
+    }
+
+    if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
+        LOG(log_error, logtype_afpd, "getextattr_size: fstatat error: %s", strerror(errno));
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+    attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
+    
+    /* Start building reply packet */
+
+    LOG(log_debug7, logtype_afpd, "getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
+
+    /* length of attribute data */
+    *(uint32_t *)rbuf = htonl((uint32_t)attrsize);
+    *rbuflen += 4;
+
+    ret = AFP_OK;
+
+exit:
+    close(attrdirfd);
+    return ret;
+}
+
+static int getextattr_content(char *rbuf, int *rbuflen, char *uname, int oflag, char *attruname, int maxreply)
+{
+    int                 ret, attrdirfd;
+    size_t              toread, okread = 0, len;
+    uint32_t            *datalength;
+    struct stat         st;
+
+    if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
+        if (errno == ELOOP) {
+            /* its a symlink and client requested O_NOFOLLOW  */
+            LOG(log_debug, logtype_afpd, "getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
+
+            *(uint32_t *)rbuf = 0;
+            *rbuflen += 4;
+
+            return AFP_OK;
+        }
+        LOG(log_error, logtype_afpd, "getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
+        return AFPERR_MISC;
+    }
+
+    if ( -1 == (fstat(attrdirfd, &st))) {
+        LOG(log_error, logtype_afpd, "getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+
+    /* Start building reply packet */
+
+    maxreply -= MAX_REPLY_EXTRA_BYTES;
+    if (maxreply > MAX_EA_SIZE)
+        maxreply = MAX_EA_SIZE;
+
+    /* But never send more than the client requested */
+    toread = (maxreply < st.st_size) ? maxreply : st.st_size;
+
+    LOG(log_debug7, logtype_afpd, "getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
+
+    /* remember where we must store length of attribute data in rbuf */
+    datalength = (uint32_t *)rbuf;
+    rbuf += 4;
+    *rbuflen += 4;
+
+    while (1) {
+        len = read(attrdirfd, rbuf, toread);
+        if (len == -1) {
+            LOG(log_error, logtype_afpd, "getextattr_content(%s): read error: %s", attruname, strerror(errno));
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+        okread += len;
+        rbuf += len;
+        *rbuflen += len;
+        if ((len == 0) || (okread == toread))
+            break;
+    }
+
+    *datalength = htonl((uint32_t)okread);
+
+    ret = AFP_OK;
+
+exit:
+    close(attrdirfd);
+    return ret;
+}
+
+
+/***************************************
+ * Interface
+ ****************************************/
+
+/* 
+   Note: we're being called twice. Firstly the client only want the size of all
+   EA names, secondly it wants these names. In order to avoid scanning EAs twice
+   we cache them in a static buffer.
+ */
+int afp_listextattr(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
+{
+    int                 count, attrdirfd = 0, ret, len, oflag = 0;
+    uint16_t            vid, bitmap;
+    uint32_t            did, maxreply;
+    struct vol          *vol;
+    struct dir          *dir;
+    struct path         *s_path;
+    struct adouble      ad, *adp = NULL;
+    struct ofork        *of;
+    struct dirent       *dp;
+    char                *uname, *FinderInfo;
+    DIR                 *dirp = NULL;
+
+    static int          buf_valid = 0, attrbuflen;
+    /* This should be big enough to consecutively store the names of all attributes */
+#define ATTRNAMEBUFSIZ 4096
+    static char         attrnamebuf[ATTRNAMEBUFSIZ];
+
+#ifdef DEBUG
+    LOG(log_debug9, logtype_afpd, "afp_listextattr: BEGIN");
+#endif
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    /* Get MaxReplySize first */
+    memcpy( &maxreply, ibuf + 14, 4);
+    maxreply = ntohl( maxreply );
+
+    /*
+      If its the first request with maxreply=0 or if we didn't mark our buffers valid for
+      whatever reason (just a safety check, it should be valid), then scan for attributes
+    */
+    if ((maxreply == 0) || (buf_valid == 0)) {
+
+        attrbuflen = 0;
+
+        memcpy( &vid, ibuf, 2);
+        ibuf += 2;
+        if (NULL == ( vol = getvolbyvid( vid )) ) {
+            LOG(log_error, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
+            return AFPERR_ACCESS;
+        }
+
+        memcpy( &did, ibuf, 4);
+        ibuf += 4;
+        if (NULL == ( dir = dirlookup( vol, did )) ) {
+            LOG(log_error, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
+            return afp_errno;
+        }
+
+        memcpy( &bitmap, ibuf, 2);
+        bitmap = ntohs( bitmap );
+        ibuf += 2;
+
+       if (bitmap & kXAttrNoFollow)
+           oflag = O_NOFOLLOW;
+
+        /* Skip ReqCount, StartIndex and maxreply*/
+        ibuf += 10;
+
+        /* get name */
+        if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
+            LOG(log_error, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
+            return AFPERR_NOOBJ;
+        }
+        uname = s_path->u_name;
+
+        /*
+          We have to check the FinderInfo for the file, because if they aren't all 0
+          we must return the synthetic attribute "com.apple.FinderInfo".
+          Note: the client will never (never seen in traces) request that attribute
+          via FPGetExtAttr !
+        */
+        if ((of = of_findname(s_path))) {
+            adp = of->of_ad;
+        } else {
+            ad_init(&ad, vol->v_adouble, vol->v_ad_options);
+            adp = &ad;
+        }
+
+        if ( ad_metadata( uname, 0, adp) < 0 ) {
+            switch (errno) {
+            case EACCES:
+                LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
+                    uname, strerror(errno));
+                return AFPERR_ACCESS;
+            default:
+                LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
+                return AFPERR_MISC;
+            }
+        }
+
+        FinderInfo = ad_entry(adp, ADEID_FINDERI);
+#ifdef DEBUG
+        LOG(log_debug9, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname);
+        hexdump( FinderInfo, 32);
+#endif
+
+        /* Now scan FinderInfo if its all 0 */
+        count = 32;
+        while (count--) {
+            if (*FinderInfo++) {
+                /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
+                strcpy(attrnamebuf, ea_finderinfo);
+                attrbuflen += strlen(ea_finderinfo) + 1;
+                LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
+                break;
+            }
+        }
+
+       /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
+       LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %u", uname, adp->ad_eid[ADEID_RFORK].ade_len);
+       if (adp->ad_eid[ADEID_RFORK].ade_len > 0) {
+           LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
+           strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
+           attrbuflen += strlen(ea_resourcefork) + 1;      
+       }
+
+        /* Now list file attribute dir */
+        if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
+            if (errno == ELOOP) {
+                /* its a symlink and client requested O_NOFOLLOW */
+                LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
+
+                *(uint16_t *)rbuf = htons(bitmap);
+                rbuf += 2;
+                *rbuflen += 2;
+
+                *(uint32_t *)rbuf = 0;
+                *rbuflen += 4;
+
+                ret = AFP_OK;
+                goto exit;
+            }
+            LOG(log_error, logtype_afpd, "afp_listextattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+        if (NULL == (dirp = fdopendir(attrdirfd))) {
+            LOG(log_error, logtype_afpd, "afp_listextattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+
+        while ((dp = readdir(dirp)))  {
+            /* check if its "." or ".." */
+            if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
+                (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
+                continue;
+
+            len = strlen(dp->d_name);
+
+           if ( 0 >= ( len = convert_string(obj->options.unixcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
+               ret = AFPERR_MISC;
+               goto exit;
+           }
+           if (len == 255)
+               /* convert_string didn't 0-terminate */
+               attrnamebuf[attrbuflen + 255] = 0;
+               
+            LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): attribute: %s", uname, dp->d_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 ? */
+               ret = AFPERR_MISC;
+               goto exit;
+           }
+        }
+        buf_valid = 1;
+    }
+
+    /* Start building reply packet */
+    *(uint16_t *)rbuf = htons(bitmap);
+    rbuf += 2;
+    *rbuflen += 2;
+
+    *(uint32_t *)rbuf = htonl(attrbuflen);
+    rbuf += 4;
+    *rbuflen += 4;
+
+    if (maxreply && buf_valid) {
+        memcpy( rbuf, attrnamebuf, attrbuflen);
+        *rbuflen += attrbuflen;
+        buf_valid = 0;
+    }
+
+    ret = AFP_OK;
+
+exit:
+    if (ret != AFP_OK)
+       buf_valid = 0;
+    if (adp)
+        ad_close_metadata( adp);
+    if (dirp) {
+        closedir(dirp);
+        dirp = NULL;
+    }
+    if (attrdirfd) {
+        close(attrdirfd);
+        attrdirfd = 0;
+    }
+
+    LOG(log_debug9, logtype_afpd, "afp_listextattr: END");
+    return ret;
+}
+
+int afp_getextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
+{
+    int                 ret, oflag = 0;
+    uint16_t            vid, bitmap;
+    uint32_t            did, maxreply, attrnamelen;
+    char                attrmname[256], attruname[256];
+    struct vol          *vol;
+    struct dir          *dir;
+    struct path         *s_path;
+
+
+#ifdef DEBUG
+    LOG(log_debug9, logtype_afpd, "afp_getextattr: BEGIN");
+#endif
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, 2);
+    ibuf += 2;
+    if (NULL == ( vol = getvolbyvid( vid )) ) {
+        LOG(log_error, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
+        return AFPERR_ACCESS;
+    }
+
+    memcpy( &did, ibuf, 4);
+    ibuf += 4;
+    if (NULL == ( dir = dirlookup( vol, did )) ) {
+        LOG(log_error, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
+        return afp_errno;
+    }
+
+    memcpy( &bitmap, ibuf, 2);
+    bitmap = ntohs( bitmap );
+    ibuf += 2;
+    if (bitmap & kXAttrNoFollow)
+        oflag = AT_SYMLINK_NOFOLLOW;
+
+    /* Skip Offset and ReqCount */
+    ibuf += 16;
+
+    /* Get MaxReply */
+    maxreply = ntohl(*((uint32_t *)ibuf));
+    ibuf += 4;
+
+    /* get name */
+    if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
+        LOG(log_error, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
+        return AFPERR_NOOBJ;
+    }
+
+    if ((unsigned long)ibuf & 1)
+        ibuf++;
+
+    /* get length of EA name */
+    attrnamelen = ntohs(*((uint16_t *)ibuf));
+    ibuf += 2;
+    if (attrnamelen > 255)
+       /* dont fool with us */
+       attrnamelen = 255;
+
+    /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
+    strncpy(attrmname, ibuf, attrnamelen);
+    attrmname[attrnamelen] = 0;
+
+    LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, attrmname);
+
+    /* Convert EA name in utf8 to unix charset */
+    if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
+       return AFPERR_MISC;
+
+    if (attrnamelen == 255)
+       /* convert_string didn't 0-terminate */
+       attruname[255] = 0;
+    
+    /* write bitmap now */
+    *(uint16_t *)rbuf = htons(bitmap);
+    rbuf += 2;
+    *rbuflen += 2;
+    
+    /*
+      Switch on maxreply:
+      if its 0 we must return the size of the requested attribute,
+      if its non 0 we must return the attribute.
+    */
+    if (maxreply == 0)
+        ret = getextattr_size(rbuf, rbuflen, s_path->u_name, oflag, attruname);
+    else
+        ret = getextattr_content(rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
+
+#ifdef DEBUG
+    LOG(log_debug9, logtype_afpd, "afp_getextattr: END");
+#endif
+
+    return ret;
+}
+
+int afp_setextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
+{
+    int                 len, oflag = O_CREAT | O_WRONLY, attrdirfd;
+    uint16_t            vid, bitmap;
+    uint32_t            did, attrnamelen, attrsize;
+    char                attrmname[256], attruname[256];
+    struct vol          *vol;
+    struct dir          *dir;
+    struct path         *s_path;
+
+#ifdef DEBUG
+    LOG(log_debug9, logtype_afpd, "afp_setextattr: BEGIN");
+#endif
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, 2);
+    ibuf += 2;
+    if (NULL == ( vol = getvolbyvid( vid )) ) {
+        LOG(log_error, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
+        return AFPERR_ACCESS;
+    }
+
+    memcpy( &did, ibuf, 4);
+    ibuf += 4;
+    if (NULL == ( dir = dirlookup( vol, did )) ) {
+        LOG(log_error, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
+        return afp_errno;
+    }
+
+    memcpy( &bitmap, ibuf, 2);
+    bitmap = ntohs( bitmap );
+    ibuf += 2;
+    if (bitmap & kXAttrNoFollow)
+        oflag |= AT_SYMLINK_NOFOLLOW;
+    if (bitmap & kXAttrCreate)
+        oflag |= O_EXCL;
+    else if (bitmap & kXAttrReplace)
+        oflag |= O_TRUNC;
+
+    /* Skip Offset */
+    ibuf += 8;
+
+    /* get name */
+    if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
+        LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
+        return AFPERR_NOOBJ;
+    }
+
+    if ((unsigned long)ibuf & 1)
+        ibuf++;
+
+    /* get length of EA name */
+    attrnamelen = ntohs(*((uint16_t *)ibuf));
+    ibuf += 2;
+    if (attrnamelen > 255)
+       return AFPERR_PARAM;
+
+    /* we must copy the name as its not 0-terminated and we cant write to ibuf */
+    strncpy(attrmname, ibuf, attrnamelen);
+    attrmname[attrnamelen] = 0;
+    ibuf += attrnamelen;
+
+    /* Convert EA name in utf8 to unix charset */
+    if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
+       return AFPERR_MISC;
+
+    if (attrnamelen == 255)
+       /* convert_string didn't 0-terminate */
+       attruname[255] = 0;
+
+    /* get EA size */
+    attrsize = ntohl(*((uint32_t *)ibuf));
+    ibuf += 4;
+    if (attrsize > MAX_EA_SIZE)
+       /* we arbitrarily make this fatal */
+       return AFPERR_PARAM;
+    
+    LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, attrmname, attrsize);
+
+    if ( -1 == (attrdirfd = attropen(s_path->u_name, attruname, oflag, 0666))) {
+        if (errno == ELOOP) {
+            /* its a symlink and client requested O_NOFOLLOW  */
+            LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
+            return AFP_OK;
+        }
+        LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
+        return AFPERR_MISC;
+    }
+    
+    while (1) {
+        len = write(attrdirfd, ibuf, attrsize);
+        if (len == -1) {
+            LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
+            return AFPERR_MISC;
+        }
+        attrsize -= len;
+        ibuf += len;
+        if (attrsize == 0)
+            break;
+    }
+
+#ifdef DEBUG
+    LOG(log_debug9, logtype_afpd, "afp_setextattr: END");
+#endif
+
+    return AFP_OK;
+}
+
+int afp_remextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
+{
+    int                 oflag = O_RDONLY, attrdirfd;
+    uint16_t            vid, bitmap;
+    uint32_t            did, attrnamelen;
+    char                attrmname[256], attruname[256];
+    struct vol          *vol;
+    struct dir          *dir;
+    struct path         *s_path;
+
+#ifdef DEBUG
+    LOG(log_debug9, logtype_afpd, "afp_remextattr: BEGIN");
+#endif
+
+    *rbuflen = 0;
+    ibuf += 2;
+
+    memcpy( &vid, ibuf, 2);
+    ibuf += 2;
+    if (NULL == ( vol = getvolbyvid( vid )) ) {
+        LOG(log_error, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
+        return AFPERR_ACCESS;
+    }
+
+    memcpy( &did, ibuf, 4);
+    ibuf += 4;
+    if (NULL == ( dir = dirlookup( vol, did )) ) {
+        LOG(log_error, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
+        return afp_errno;
+    }
+
+    memcpy( &bitmap, ibuf, 2);
+    bitmap = ntohs( bitmap );
+    ibuf += 2;
+    if (bitmap & kXAttrNoFollow)
+        oflag |= AT_SYMLINK_NOFOLLOW;
+
+    /* get name */
+    if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
+        LOG(log_error, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
+        return AFPERR_NOOBJ;
+    }
+
+    if ((unsigned long)ibuf & 1)
+        ibuf++;
+
+    /* get length of EA name */
+    attrnamelen = ntohs(*((uint16_t *)ibuf));
+    ibuf += 2;
+    if (attrnamelen > 255)
+       return AFPERR_PARAM;
+
+    /* we must copy the name as its not 0-terminated and we cant write to ibuf */
+    strncpy(attrmname, ibuf, attrnamelen);
+    attrmname[attrnamelen] = 0;
+    ibuf += attrnamelen;
+
+    /* Convert EA name in utf8 to unix charset */
+    if ( 0 >= ( attrnamelen = convert_string(CH_UTF8_MAC, obj->options.unixcharset,attrmname, attrnamelen, attruname, 255)) )
+       return AFPERR_MISC;
+
+    if (attrnamelen == 255)
+       /* convert_string didn't 0-terminate */
+       attruname[255] = 0;
+
+    LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, attrmname);
+
+    if ( -1 == (attrdirfd = attropen(s_path->u_name, ".", oflag))) {
+       switch (errno) {
+       case ELOOP:
+            /* its a symlink and client requested O_NOFOLLOW  */
+            LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
+            return AFP_OK;
+       case EACCES:
+           LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
+           return AFPERR_ACCESS;
+       default:
+           LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
+           return AFPERR_MISC;
+       }
+    }
+    
+    if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
+       if (errno == EACCES) {
+           LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
+           return AFPERR_ACCESS;
+       }
+        LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
+       return AFPERR_MISC;
+    }
+
+#ifdef DEBUG
+    LOG(log_debug9, logtype_afpd, "afp_remextattr: END");
+#endif
+
+    return AFP_OK;
+}
+
+
+
+#endif /* HAVE_EXT_ATTRS */
+
diff --git a/etc/afpd/extattrs.h b/etc/afpd/extattrs.h
new file mode 100644 (file)
index 0000000..82d9bef
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+   $Id: extattrs.h,v 1.1 2009-02-16 13:49:20 franklahm Exp $
+   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.
+ */
+
+#ifndef AFPD_EXT_ATTRS_H 
+#define AFPD_EXT_ATTRS_H
+
+/* This seems to be the current limit fo HFS+, we arbitrarily force that
+   which also safes us from buffer overflows */
+#define MAX_EA_SIZE 3802
+
+/* At time of writing the 10.5.6 client adds 8 bytes to the
+   length of the EA that we send him */
+#define MAX_REPLY_EXTRA_BYTES 8
+
+enum {
+    kXAttrNoFollow = 0x1,
+    kXAttrCreate = 0x2,
+    kXAttrReplace = 0x4
+};
+
+extern int afp_listextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen);
+extern int afp_getextattr(AFPObj *obj _U_ , char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen);
+extern int afp_setextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen);
+extern int afp_remextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen);
+
+#endif /* AFPD_EXT_ATTRS_H */
index 9180e661668ed366b2a876195f0deac18b521759..e8f8862affc515f8197c9f29cb43155e28e6097f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: status.c,v 1.20 2009-02-02 12:35:40 franklahm Exp $
+ * $Id: status.c,v 1.21 2009-02-16 13:49:20 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -581,6 +581,8 @@ int ibuflen _U_, *rbuflen;
 {
     AFPConfig *config = obj->config;
 
+    LOG(log_error, logtype_afpd, "afp_getsrvrinfo: options->flags: %04x", obj->options.flags);
+
     memcpy(rbuf, config->status, config->statuslen);
     *rbuflen = config->statuslen;
     return AFP_OK;
index b84ac2a1fc12be3ed73bb3fedd8ff81e1f2771b1..c3c0c4eac60e8c717e586b1de444774ea3a572da 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: volume.c,v 1.79 2009-02-02 11:55:01 franklahm Exp $
+ * $Id: volume.c,v 1.80 2009-02-16 13:49:20 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -174,6 +174,7 @@ static const _vol_opt_name vol_opt_names[] = {
     {AFPVOL_NODEV,      "NODEV"},       /* always use 0 for device number in cnid calls */
     {AFPVOL_EILSEQ,     "ILLEGALSEQ"},     /* encode illegal sequence */
     {AFPVOL_CACHE,      "CACHEID"},     /* Use adouble v2 CNID caching, default don't use it */
+    {AFPVOL_EXT_ATTRS,  "EXT_ATTRS"},   /* Vol supports Extened Attributes */
     {AFPVOL_ACLS,       "ACLS"},        /* Vol supports ACLs */
     {0, NULL}
 };
@@ -483,6 +484,8 @@ static void volset(struct vol_option *options, struct vol_option *save,
                options[VOLOPT_ROOTPREEXEC].i_value = 1;
             else if (strcasecmp(p, "upriv") == 0)
                 options[VOLOPT_FLAGS].i_value |= AFPVOL_UNIX_PRIV;
+            else if (strcasecmp(p, "extattrs") == 0)
+                options[VOLOPT_FLAGS].i_value |= AFPVOL_EXT_ATTRS;
             else if (strcasecmp(p, "acls") == 0)
                 options[VOLOPT_FLAGS].i_value |= AFPVOL_ACLS;
             else if (strcasecmp(p, "nodev") == 0)
@@ -1322,6 +1325,8 @@ int               *buflen;
                    ashort |= VOLPBIT_ATTR_UNIXPRIV;
             }
             if (afp_version >= 32) {
+               if (vol->v_flags & AFPVOL_EXT_ATTRS)
+                   ashort |= VOLPBIT_ATTR_EXT_ATTRS;
                if (vol->v_flags & AFPVOL_ACLS)
                    ashort |= VOLPBIT_ATTR_ACLS;
             }
index 52d244828a31dea1027e2ad072a279ca2f28bacd..b1a801636d5425e4e34769f5a3c4a93302a88fed 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: volume.h,v 1.27 2009-02-02 11:55:01 franklahm Exp $
+ * $Id: volume.h,v 1.28 2009-02-16 13:49:20 franklahm Exp $
  *
  * Copyright (c) 1990,1994 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -130,6 +130,7 @@ this is going away. */
 
 #define AFPVOL_CACHE     (1 << 21)   /* Use adouble v2 CNID caching, default don't use it */
 #define AFPVOL_INV_DOTS  (1 << 22)   /* dots files are invisible */
+#define AFPVOL_EXT_ATTRS (1 << 23)   /* Volume supports Extended Attributes */
 #define AFPVOL_ACLS      (1 << 25)   /* Volume supports ACLS */
 
 /* FPGetSrvrParms options */
@@ -164,6 +165,7 @@ int wincheck(const struct vol *vol, const char *path);
 #define VOLPBIT_ATTR_UNIXPRIV     (1 << 5)
 #define VOLPBIT_ATTR_UTF8         (1 << 6)
 #define VOLPBIT_ATTR_NONETUID     (1 << 7)
+#define VOLPBIT_ATTR_EXT_ATTRS    (1 << 10)
 #define VOLPBIT_ATTR_ACLS         (1 << 11)
 #define VOLPBIT_ATTR   0
 #define VOLPBIT_SIG    1
index 87eee7f2cc4811bb618aadac302dd0edde91e4be..ddcf2f003489fecab3499fa087e00119ddb9678c 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: summary.m4,v 1.4 2009-02-02 11:55:01 franklahm Exp $
+dnl $Id: summary.m4,v 1.5 2009-02-16 13:49:20 franklahm Exp $
 dnl Autoconf macros, display configure summary
 
 AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
@@ -61,6 +61,7 @@ dnl   fi
        AC_MSG_RESULT([         force volume uid/gid:   $netatalk_cv_force_uidgid])
        AC_MSG_RESULT([         Apple 2 boot support:   $compile_a2boot])
        AC_MSG_RESULT([         ACL support:            $neta_cv_nfsv4acl])
+       AC_MSG_RESULT([         Extended Attributes:    $neta_cv_extattrs])
        if test x"$use_pam_so" = x"yes" -a x"$netatalk_cv_install_pam" = x"no"; then
                AC_MSG_RESULT([])
                AC_MSG_WARN([ PAM support was configured for your system, but the netatalk PAM configuration file])