From: franklahm Date: Mon, 16 Feb 2009 13:49:19 +0000 (+0000) Subject: Extended Attributes Support on Solaris with ZFS X-Git-Tag: before_new_logger~7 X-Git-Url: https://arthur.barton.de/gitweb/?a=commitdiff_plain;h=ab7ff04bf54f03bf452c655d9fcd619e04a4ff26;p=netatalk.git Extended Attributes Support on Solaris with ZFS --- diff --git a/configure.in b/configure.in index 47336919..5fc23043 100644 --- a/configure.in +++ b/configure.in @@ -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) diff --git a/etc/afpd/Makefile.am b/etc/afpd/Makefile.am index ad641229..99cf63f0 100644 --- a/etc/afpd/Makefile.am +++ b/etc/afpd/Makefile.am @@ -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 diff --git a/etc/afpd/auth.c b/etc/afpd/auth.c index 3fb07961..35878b88 100644 --- a/etc/afpd/auth.c +++ b/etc/afpd/auth.c @@ -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 index 00000000..d4db85b6 --- /dev/null +++ b/etc/afpd/extattrs.c @@ -0,0 +1,705 @@ +/* + $Id: extattrs.c,v 1.1 2009-02-16 13:49:20 franklahm Exp $ + Copyright (c) 2009 Frank Lahm + + 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 index 00000000..82d9befa --- /dev/null +++ b/etc/afpd/extattrs.h @@ -0,0 +1,38 @@ +/* + $Id: extattrs.h,v 1.1 2009-02-16 13:49:20 franklahm Exp $ + Copyright (c) 2009 Frank Lahm + + 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 */ diff --git a/etc/afpd/status.c b/etc/afpd/status.c index 9180e661..e8f8862a 100644 --- a/etc/afpd/status.c +++ b/etc/afpd/status.c @@ -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; diff --git a/etc/afpd/volume.c b/etc/afpd/volume.c index b84ac2a1..c3c0c4ea 100644 --- a/etc/afpd/volume.c +++ b/etc/afpd/volume.c @@ -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; } diff --git a/etc/afpd/volume.h b/etc/afpd/volume.h index 52d24482..b1a80163 100644 --- a/etc/afpd/volume.h +++ b/etc/afpd/volume.h @@ -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 diff --git a/macros/summary.m4 b/macros/summary.m4 index 87eee7f2..ddcf2f00 100644 --- a/macros/summary.m4 +++ b/macros/summary.m4 @@ -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])