]> arthur.barton.de Git - netatalk.git/commitdiff
Extended Attributes support via files in .AppleDouble.
authorfranklahm <franklahm>
Fri, 2 Oct 2009 09:32:40 +0000 (09:32 +0000)
committerfranklahm <franklahm>
Fri, 2 Oct 2009 09:32:40 +0000 (09:32 +0000)
Extended VFS stack, so apps linking libatalk can also actually use adouble and EA code.
Missing: EA integration into AFP rm, cp, mv.

40 files changed:
configure.in
etc/afpd/Makefile.am
etc/afpd/acls.c
etc/afpd/auth.c
etc/afpd/directory.c
etc/afpd/directory.h
etc/afpd/enumerate.c
etc/afpd/extattrs.c
etc/afpd/extattrs.h
etc/afpd/file.c
etc/afpd/filedir.c
etc/afpd/hash.h
etc/afpd/unix.c
etc/afpd/unix.h
etc/afpd/volume.c
etc/afpd/volume.h
include/atalk/Makefile.am
include/atalk/adouble.h
include/atalk/afp.h
include/atalk/directory.h [new file with mode: 0644]
include/atalk/ea.h [new file with mode: 0644]
include/atalk/hash.h [new file with mode: 0644]
include/atalk/util.h
include/atalk/vfs.h [new file with mode: 0644]
include/atalk/volinfo.h
include/atalk/volume.h [new file with mode: 0644]
libatalk/Makefile.am
libatalk/adouble/Makefile.am
libatalk/adouble/ad_open.c
libatalk/util/Makefile.am
libatalk/util/locking.c [new file with mode: 0644]
libatalk/util/volinfo.c
libatalk/vfs/.cvsignore [new file with mode: 0644]
libatalk/vfs/Makefile.am [new file with mode: 0644]
libatalk/vfs/acl.c [new file with mode: 0644]
libatalk/vfs/ea.c [new file with mode: 0644]
libatalk/vfs/unix.c [new file with mode: 0644]
libatalk/vfs/vfs.c [new file with mode: 0644]
macros/summary.m4
man/man5/AppleVolumes.default.5.tmpl

index 5380f6d27937ddf7bd86b92bc7dbe861795c01cc..88857034dc4e8a866f9ac601dd6e656a9ff2e57a 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: configure.in,v 1.219 2009-09-18 12:36:39 franklahm Exp $
+dnl $Id: configure.in,v 1.220 2009-10-02 09:32:40 franklahm Exp $
 dnl configure.in for netatalk
 
 AC_INIT(etc/afpd/main.c)
@@ -1043,25 +1043,14 @@ 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])
+neta_cv_eas="adouble"
+if test "x$this_os" = "xsolaris"; then
+       AC_CHECK_LIB(c,attropen, [
+            neta_cv_eas="$neta_cv_eas Solaris"
+            AC_MSG_NOTICE([Enabling Solaris Extended Attributes support])
+            AC_DEFINE([HAVE_SOLARIS_EAS], 1, [Enable Extended Attributes])
+        ]
+    )
 fi
 
 dnl --------------------- last minute substitutions
@@ -1170,6 +1159,7 @@ AC_OUTPUT([Makefile
        libatalk/tdb/Makefile
        libatalk/unicode/Makefile
        libatalk/unicode/charsets/Makefile
+       libatalk/vfs/Makefile
        macros/Makefile
        man/Makefile
        man/man1/Makefile
index 99cf63f050a4753afe580d6a18ecf1db1915748b..872281098563553617d96089f95a01c7e2a3830b 100644 (file)
@@ -8,22 +8,19 @@ afpd_SOURCES = unix.c ofork.c main.c switch.c auth.c volume.c directory.c \
         file.c enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \
         mangle.c status.c afp_options.c afp_asp.c afp_dsi.c messages.c  \
         afp_config.c nfsquota.c quota.c uam.c afs.c uid.c afp_util.c \
-         catsearch.c afprun.c vfs_adouble.c hash.c
+       catsearch.c afprun.c hash.c extattrs.c
 
 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
 
 noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
         filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.h \
-        uam_auth.h uid.h unix.h volume.h afp_vfs.h hash.h acls.h acl_mappings.h
+        uam_auth.h uid.h unix.h volume.h hash.h acls.h acl_mappings.h
 
 LIBS = @LIBS@ @QUOTA_LIBS@ @SLP_LIBS@ @WRAP_LIBS@ @LIBADD_DL@
 
index fd408aa00fcff26ab08315583ec17c11eb5d9dee..2da2816c3ca3857b7ffc148507b78b25ea180c34 100644 (file)
@@ -1,5 +1,5 @@
 /*
-   $Id: acls.c,v 1.3 2009-02-26 14:00:18 franklahm Exp $
+   $Id: acls.c,v 1.4 2009-10-02 09:32:40 franklahm Exp $
    Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
@@ -27,6 +27,7 @@
 #include <sys/acl.h>
 
 #include <atalk/adouble.h>
+#include <atalk/vfs.h>
 #include <atalk/afp.h>
 #include <atalk/util.h>
 #include <atalk/cnid.h>
  * Basic and helper funcs
  ********************************************************/
 
-/* Get ACL. Allocates storage as needed. Caller must free.
- * Returns no of ACEs or -1 on error.  */
-static int get_nfsv4_acl(const char *name, ace_t **retAces)
-{
-    int ace_count = -1;
-    ace_t *aces;
-
-    *retAces = NULL;
-    ace_count = acl(name, ACE_GETACLCNT, 0, NULL);
-    if (ace_count <= 0) {
-       LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACLCNT) error");
-        return -1;
-    }
-
-    aces = malloc(ace_count * sizeof(ace_t));
-    if (aces == NULL) {
-       LOG(log_error, logtype_afpd, "get_nfsv4_acl: malloc error");
-       return -1;
-    }
-
-    if ( (acl(name, ACE_GETACL, ace_count, aces)) == -1 ) {
-       LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACL) error");
-       free(aces);
-       return -1;
-    }
-
-    LOG(log_debug9, logtype_afpd, "get_nfsv4_acl: file: %s -> No. of ACEs: %d", name, ace_count);
-    *retAces = aces;
-
-    return ace_count;
-}
-
 /*
   Takes a users name, uid and primary gid and checks if user is member of any group
     Returns -1 if no or error, 0 if yes
@@ -459,60 +428,6 @@ cleanup:
     return err;
 }
 
-/* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
-int remove_acl(const char *name)
-{
-    int ret,i, ace_count, trivial_aces, new_aces_count;
-    ace_t *old_aces = NULL;
-    ace_t *new_aces = NULL;
-
-    LOG(log_debug9, logtype_afpd, "remove_acl: BEGIN");
-
-    /* Get existing ACL and count trivial ACEs */
-    if ((ace_count = get_nfsv4_acl(name, &old_aces)) == -1)
-       return AFPERR_MISC;
-    trivial_aces = 0;
-    for ( i=0; i < ace_count; i++) {
-       if (old_aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))
-           trivial_aces++;
-    }
-
-    /* malloc buffer for new ACL */
-    if ((new_aces = malloc(trivial_aces * sizeof(ace_t))) == NULL) {
-       LOG(log_error, logtype_afpd, "remove_acl: malloc %s", strerror(errno));
-       ret = AFPERR_MISC;
-       goto exit;
-    }
-
-    /* Now copy the trivial ACEs */
-    new_aces_count = 0;
-    for (i=0; i < ace_count; i++) {
-       if (old_aces[i].a_flags  & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
-           memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
-           new_aces_count++;
-       }
-    }
-    
-    if ( (acl(name, ACE_SETACL, trivial_aces, new_aces)) == 0)
-       ret = AFP_OK;
-    else {
-       LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
-       if (errno == (EACCES | EPERM))
-           ret = AFPERR_ACCESS;
-       else if (errno == ENOENT)
-           ret = AFPERR_NOITEM;
-       else
-           ret = AFPERR_MISC;
-    }
-
-exit:
-    free(old_aces);
-    free(new_aces);
-
-    LOG(log_debug9, logtype_afpd, "remove_acl: END");
-    return ret;
-}
-
 /* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
 static int remove_acl_vfs(const struct vol *vol,const char *path, int dir)
 {
index ea09460ad4930b309ada3249b8cce26dee057fb1..e86e0ffd23f19799e5fd6a5445edbe49e1daaaa8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: auth.c,v 1.65 2009-09-28 12:04:51 franklahm Exp $
+ * $Id: auth.c,v 1.66 2009-10-02 09:32:40 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -47,12 +47,10 @@ extern void afp_get_cmdline( int *ac, char ***av );
 #include "switch.h"
 #include "status.h"
 #include "fork.h"
+#include "extattrs.h"
 #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;
@@ -202,21 +200,19 @@ static int set_auth_switch(int expired)
         afp_switch = postauth_switch;
         switch (afp_version) {
         case 32:
-            uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_null_nolog, NULL);
-            uam_afpserver_action(AFP_SYNCDIR, UAM_AFPSERVER_POSTAUTH, afp_syncdir, NULL);
-            uam_afpserver_action(AFP_SYNCFORK, UAM_AFPSERVER_POSTAUTH, afp_syncfork, NULL);
 #ifdef HAVE_NFSv4_ACLS
             uam_afpserver_action(AFP_GETACL, UAM_AFPSERVER_POSTAUTH, afp_getacl, NULL);
             uam_afpserver_action(AFP_SETACL, UAM_AFPSERVER_POSTAUTH, afp_setacl, NULL);
             uam_afpserver_action(AFP_ACCESS, UAM_AFPSERVER_POSTAUTH, afp_access, NULL);
 #endif
-#ifdef HAVE_EXT_ATTRS
             uam_afpserver_action(AFP_GETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_getextattr, NULL);
             uam_afpserver_action(AFP_SETEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_setextattr, NULL);
             uam_afpserver_action(AFP_REMOVEATTR, UAM_AFPSERVER_POSTAUTH, afp_remextattr, NULL);
             uam_afpserver_action(AFP_LISTEXTATTR, UAM_AFPSERVER_POSTAUTH, afp_listextattr, NULL);
-#endif
         case 31:
+            uam_afpserver_action(AFP_SYNCDIR, UAM_AFPSERVER_POSTAUTH, afp_syncdir, NULL);
+            uam_afpserver_action(AFP_SYNCFORK, UAM_AFPSERVER_POSTAUTH, afp_syncfork, NULL);
+            uam_afpserver_action(AFP_SPOTLIGHT_PRIVATE, UAM_AFPSERVER_POSTAUTH, afp_null_nolog, NULL);
             uam_afpserver_action(AFP_ENUMERATE_EXT2, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext2, NULL);
         case 30:
             uam_afpserver_action(AFP_ENUMERATE_EXT, UAM_AFPSERVER_POSTAUTH, afp_enumerate_ext, NULL);
index c5049a36f54857188f5f029b6c3caf450f15a1dd..e847a9d7d4c9f80447a8642f0535b49837b1b46e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: directory.c,v 1.104 2009-09-14 00:02:21 didg Exp $
+ * $Id: directory.c,v 1.105 2009-10-02 09:32:40 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -38,8 +38,9 @@ char *strchr (), *strrchr ();
 #include <sys/param.h>
 #include <errno.h>
 #include <utime.h>
-#include <atalk/adouble.h>
 
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
 #include <atalk/afp.h>
 #include <atalk/util.h>
 #include <atalk/cnid.h>
@@ -55,6 +56,7 @@ char *strchr (), *strrchr ();
 #include "globals.h"
 #include "unix.h"
 #include "mangle.h"
+#include "hash.h"
 
 #ifdef HAVE_NFSv4_ACLS
 extern void addir_inherit_acl(const struct vol *vol);
@@ -725,30 +727,6 @@ struct path *path;
     return( dir );
 }
 
-/* -------------------
-   system rmdir with afp error code.
-   ENOENT is not an error.
- */
-int netatalk_rmdir(const char *name)
-{
-    if (rmdir(name) < 0) {
-        switch ( errno ) {
-        case ENOENT :
-            break;
-        case ENOTEMPTY : 
-            return AFPERR_DIRNEMPT;
-        case EPERM:
-        case EACCES :
-            return AFPERR_ACCESS;
-        case EROFS:
-            return AFPERR_VLOCK;
-        default :
-            return AFPERR_PARAM;
-        }
-    }
-    return AFP_OK;
-}
-
 /* -------------------------
    appledouble mkdir afp error code.
 */
@@ -775,28 +753,6 @@ static int netatalk_mkdir(const char *name)
     return AFP_OK;
 }
 
-/* -------------------
-   system unlink with afp error code.
-   ENOENT is not an error.
- */
-int netatalk_unlink(const char *name)
-{
-    if (unlink(name) < 0) {
-        switch (errno) {
-        case ENOENT :
-            break;
-        case EROFS:
-            return AFPERR_VLOCK;
-        case EPERM:
-        case EACCES :
-            return AFPERR_ACCESS;
-        default :
-            return AFPERR_PARAM;
-        }
-    }
-    return AFP_OK;
-}
-
 /* ------------------- */
 static int deletedir(char *dir)
 {
index 577401d4be7ca0f5bd56abd5f1c07d57884696f5..5d0b75f49eeae21a62f6e67c130719cd77c8c48a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: directory.h,v 1.27 2009-09-14 00:02:21 didg Exp $
+ * $Id: directory.h,v 1.28 2009-10-02 09:32:40 franklahm Exp $
  *
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
 #include <sys/sysmacros.h>
 #endif
 
+#include <atalk/directory.h>
+
 #include "globals.h"
 #include "volume.h"
 
-/* the did tree is now a red-black tree while the parent/child
- * tree is a circular doubly-linked list. how exciting. */
-struct dir {
-    struct dir *d_left, *d_right, *d_back; /* for red-black tree */
-    int                d_color;
-    struct dir  *d_parent, *d_child; /* parent-child */
-    struct dir  *d_prev, *d_next;    /* siblings */
-    void        *d_ofork;            /* oforks using this directory. */
-    u_int32_t   d_did;
-    int                d_flags;
-
-    time_t      ctime;                /* inode ctime */
-    u_int32_t   offcnt;               /* offspring count */
-
-    char       *d_m_name;            /* mac name */
-    char        *d_u_name;            /* unix name */
-    ucs2_t     *d_m_name_ucs2;       /* mac name as UCS2 */
-};
-
-struct path {
-    int         m_type;             /* mac name type (long name, unicode */
-    char       *m_name;            /* mac name */
-    char        *u_name;            /* unix name */
-    cnid_t     id;                 /* file id (only for getmetadata) */
-    struct dir  *d_dir;             /* */
-    int         st_valid;           /* does st_errno and st set */
-    int         st_errno;
-    struct stat st;
-};
-
-static inline int path_isadir(struct path *o_path)
-{
-    return o_path->d_dir != NULL;
-#if 0
-    return o_path->m_name == '\0' || /* we are in a it */
-           !o_path->st_valid ||      /* in cache but we can't chdir in it */ 
-           (!o_path->st_errno && S_ISDIR(o_path->st.st_mode)); /* not in cache an can't chdir */
-#endif
-}
-
 #define DIRTREE_COLOR_RED    0
 #define DIRTREE_COLOR_BLACK  1
 
-/* setgid directories */
-#ifndef DIRBITS
-# ifdef AFS
-#  define DIRBITS 0
-# else /* AFS */
-#  define DIRBITS S_ISGID
-# endif /* AFS */
-#endif /* DIRBITS */
-
 #define DIRF_FSMASK    (3<<0)
 #define DIRF_NOFS      (0<<0)
 #define DIRF_AFS       (1<<0)
@@ -101,7 +54,6 @@ static inline int path_isadir(struct path *o_path)
 #define DIRF_OFFCNT     (1<<4) /* offsprings count is valid */
 #define DIRF_CNID      (1<<5)  /* renumerate id */
 
-
 #define AFPDIR_READ    (1<<0)
 
 /* directory bits */
@@ -194,7 +146,6 @@ extern int  for_each_dirent __P((const struct vol *, char *, dir_loop , void *))
 extern int  check_access __P((char *name , int mode));
 extern int file_access   __P((struct path *path, int mode));
 
-extern int netatalk_rmdir __P((const char *name));
 extern int netatalk_unlink __P((const char *name));
 
 extern int caseenumerate __P((const struct vol *, struct path *, struct dir *));
index 151394f5c238013e6b4a073c9599602ba9987b87..c994f03c7eab1371b329f51aec4153b6851991c3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: enumerate.c,v 1.43 2005-04-30 21:33:41 didg Exp $
+ * $Id: enumerate.c,v 1.44 2009-10-02 09:32:40 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-
-#include <atalk/logger.h>
 #include <sys/file.h>
 #include <sys/param.h>
 
+#include <atalk/logger.h>
 #include <atalk/afp.h>
 #include <atalk/adouble.h>
+#include <atalk/vfs.h>
 #include <atalk/cnid.h>
 #include "desktop.h"
 #include "directory.h"
index 9b20e2335feaeb1e147acb532d58003ad3c261b7..7748480aa3686801a2c4858453ba5cfd8c41161d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  $Id: extattrs.c,v 1.3 2009-08-31 14:50:46 franklahm Exp $
+  $Id: extattrs.c,v 1.4 2009-10-02 09:32:40 franklahm Exp $
   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
   This program is free software; you can redistribute it and/or modify
   GNU General Public License for more details.
 */
 
-/* According to man fsattr.5 we must define _ATFILE_SOURCE */
-#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 <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <dirent.h>
 
 #include <atalk/adouble.h>
+#include <atalk/vfs.h>
 #include <atalk/afp.h>
 #include <atalk/logger.h>
+#include <atalk/ea.h>
 
 #include "globals.h"
 #include "volume.h"
@@ -46,7 +43,6 @@ static char *ea_finderinfo = "com.apple.FinderInfo";
 static char *ea_resourcefork = "com.apple.ResourceFork";
 
 /* This should be big enough to consecutively store the names of all attributes */
-#define ATTRNAMEBUFSIZ 4096
 static char attrnamebuf[ATTRNAMEBUFSIZ];
 
 static void hexdump(void *m, size_t l) {
@@ -67,186 +63,8 @@ static void hexdump(void *m, size_t l) {
     }
 }
 
-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);
-
-            memset(rbuf, 0, 4);
-            *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 */
-    attrsize = htonl(attrsize);
-    memcpy(rbuf, &attrsize, 4);
-    *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;
-    char                *datalength;
-    struct stat         st;
-
-    if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
-        if (errno == ELOOP) {
-            /* its a symlink and client requested O_NOFOLLOW  */
-            LOG(log_debug, logtype_afpd, "getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
-
-            memset(rbuf, 0, 4);
-            *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 = 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;
-    }
-
-    okread = htonl((uint32_t)okread);
-    memcpy(datalength, &okread, 4);
-
-    ret = AFP_OK;
-
-exit:
-    close(attrdirfd);
-    return ret;
-}
-
-int list_extattr(AFPObj *obj, char *attrnamebuf, int *buflen, char *uname, int oflag)
-{
-    int ret, attrbuflen = *buflen, len, attrdirfd = 0;
-    struct dirent *dp;
-    DIR *dirp = NULL;
-
-    /* Now list file attribute dir */
-    if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
-        if (errno == ELOOP) {
-            /* its a symlink and client requested O_NOFOLLOW */
-            ret = AFPERR_BADTYPE;
-            goto exit;
-        }
-        LOG(log_error, logtype_afpd, "list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
-        ret = AFPERR_MISC;
-        goto exit;
-    }
-
-    if (NULL == (dirp = fdopendir(attrdirfd))) {
-        LOG(log_error, logtype_afpd, "list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
-        ret = AFPERR_MISC;
-        goto exit;
-    }
-
-    while ((dp = readdir(dirp)))  {
-        /* check if its "." or ".." */
-        if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
-            (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
-            continue;
-
-        len = strlen(dp->d_name);
-
-        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, "list_extattr(%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 ? */
-            LOG(log_warning, logtype_afpd, "list_extattr(%s): running out of buffer for EA names", uname);
-            ret = AFPERR_MISC;
-            goto exit;
-        }
-    }
-
-    ret = AFP_OK;
-
-exit:
-    if (dirp)
-        closedir(dirp);
-
-    if (attrdirfd > 0)
-        close(attrdirfd);
-
-    *buflen = attrbuflen;
-    return ret;
-}
-
 /***************************************
- * Interface
+ * AFP funcs
  ****************************************/
 
 /*
@@ -258,7 +76,7 @@ int afp_listextattr(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf, int *r
 {
     int                 count, ret, oflag = 0;
     uint16_t            vid, bitmap;
-    uint32_t            did, maxreply;
+    uint32_t            did, maxreply, tmpattr;
     struct vol          *vol;
     struct dir          *dir;
     struct path         *s_path;
@@ -267,10 +85,6 @@ int afp_listextattr(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf, int *r
     char                *uname, *FinderInfo;
     static int          buf_valid = 0, attrbuflen = 0;
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "afp_listextattr: BEGIN");
-#endif
-
     *rbuflen = 0;
     ibuf += 2;
 
@@ -304,9 +118,10 @@ int afp_listextattr(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf, int *r
         bitmap = ntohs( bitmap );
         ibuf += 2;
 
+#ifdef HAVE_SOLARIS_EAS
         if (bitmap & kXAttrNoFollow)
             oflag = O_NOFOLLOW;
-
+#endif
         /* Skip ReqCount, StartIndex and maxreply*/
         ibuf += 10;
 
@@ -368,7 +183,8 @@ int afp_listextattr(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf, int *r
             attrbuflen += strlen(ea_resourcefork) + 1;
         }
 
-        ret = list_extattr(obj, attrnamebuf, &attrbuflen, uname, oflag);
+        ret = vol->vfs->list_eas(vol, attrnamebuf, &attrbuflen, uname, oflag);
+
         switch (ret) {
         case AFPERR_BADTYPE:
             /* its a symlink and client requested O_NOFOLLOW */
@@ -391,8 +207,8 @@ int afp_listextattr(AFPObj *obj, char *ibuf, int ibuflen _U_, char *rbuf, int *r
     rbuf += 2;
     *rbuflen += 2;
 
-    attrbuflen = htonl(attrbuflen);
-    memcpy( rbuf, &attrbuflen, 4);
+    tmpattr = htonl(attrbuflen);
+    memcpy( rbuf, &tmpattr, 4);
     rbuf += 4;
     *rbuflen += 4;
 
@@ -412,7 +228,6 @@ exit:
     if (adp)
         ad_close_metadata( adp);
 
-    LOG(log_debug9, logtype_afpd, "afp_listextattr: END");
     return ret;
 }
 
@@ -427,10 +242,6 @@ int afp_getextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int
     struct path         *s_path;
 
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "afp_getextattr: BEGIN");
-#endif
-
     *rbuflen = 0;
     ibuf += 2;
 
@@ -451,8 +262,11 @@ int afp_getextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int
     memcpy( &bitmap, ibuf, 2);
     bitmap = ntohs( bitmap );
     ibuf += 2;
+
+#ifdef HAVE_SOLARIS_EAS
     if (bitmap & kXAttrNoFollow)
-        oflag = AT_SYMLINK_NOFOLLOW;
+        oflag = O_NOFOLLOW;
+#endif
 
     /* Skip Offset and ReqCount */
     ibuf += 16;
@@ -505,20 +319,16 @@ int afp_getextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int
       if its non 0 we must return the attribute.
     */
     if (maxreply == 0)
-        ret = getextattr_size(rbuf, rbuflen, s_path->u_name, oflag, attruname);
+        ret = vol->vfs->get_easize(vol, 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
+        ret = vol->vfs->get_eacontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply);
 
     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;
+    int                 oflag = O_CREAT | O_WRONLY, ret;
     uint16_t            vid, bitmap;
     uint32_t            did, attrnamelen, attrsize;
     char                attrmname[256], attruname[256];
@@ -526,10 +336,6 @@ int afp_setextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int
     struct dir          *dir;
     struct path         *s_path;
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "afp_setextattr: BEGIN");
-#endif
-
     *rbuflen = 0;
     ibuf += 2;
 
@@ -550,8 +356,12 @@ int afp_setextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int
     memcpy( &bitmap, ibuf, 2);
     bitmap = ntohs( bitmap );
     ibuf += 2;
+
+#ifdef HAVE_SOLARIS_EAS
     if (bitmap & kXAttrNoFollow)
         oflag |= AT_SYMLINK_NOFOLLOW;
+#endif
+
     if (bitmap & kXAttrCreate)
         oflag |= O_EXCL;
     else if (bitmap & kXAttrReplace)
@@ -599,38 +409,14 @@ int afp_setextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int
 
     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
+    ret = vol->vfs->set_ea(vol, s_path->u_name, attruname, ibuf, attrsize, oflag);
 
-    return AFP_OK;
+    return ret;
 }
 
 int afp_remextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int *rbuflen)
 {
-    int                 oflag = O_RDONLY, attrdirfd;
+    int                 oflag = O_RDONLY, ret;
     uint16_t            vid, bitmap;
     uint32_t            did, attrnamelen;
     char                attrmname[256], attruname[256];
@@ -638,10 +424,6 @@ int afp_remextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int
     struct dir          *dir;
     struct path         *s_path;
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "afp_remextattr: BEGIN");
-#endif
-
     *rbuflen = 0;
     ibuf += 2;
 
@@ -662,8 +444,11 @@ int afp_remextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int
     memcpy( &bitmap, ibuf, 2);
     bitmap = ntohs( bitmap );
     ibuf += 2;
+
+#ifdef HAVE_SOLARIS_EAS
     if (bitmap & kXAttrNoFollow)
         oflag |= AT_SYMLINK_NOFOLLOW;
+#endif
 
     /* get name */
     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
@@ -696,38 +481,8 @@ int afp_remextattr(AFPObj *obj _U_, char *ibuf, int ibuflen _U_, char *rbuf, int
 
     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
+    ret = vol->vfs->remove_ea(vol, s_path->u_name, attruname, oflag);
 
-    return AFP_OK;
+    return ret;
 }
 
-
-
-#endif /* HAVE_EXT_ATTRS */
-
index 82d9befa74fedc1c8c8fb481f5facbce2d4a83a2..90b034bd829813a25df4f98c9f3e944b88bc944b 100644 (file)
@@ -1,5 +1,5 @@
 /*
-   $Id: extattrs.h,v 1.1 2009-02-16 13:49:20 franklahm Exp $
+   $Id: extattrs.h,v 1.2 2009-10-02 09:32:40 franklahm Exp $
    Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
 
    This program is free software; you can redistribute it and/or modify
 #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
-};
-
+/* AFP funcs */
 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);
index 38672a738a4129d4247658edd5cf5365d7728d60..f2b97209cfb820a557687568295b6b768da7d2c9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: file.c,v 1.110 2009-09-21 12:35:05 franklahm Exp $
+ * $Id: file.c,v 1.111 2009-10-02 09:32:40 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -28,15 +28,14 @@ char *strchr (), *strrchr ();
 #endif /* ! HAVE_MEMCPY */
 #endif /* STDC_HEADERS */
 
-#include <atalk/adouble.h>
 #include <utime.h>
 #include <dirent.h>
 #include <errno.h>
-
-#include <atalk/logger.h>
 #include <sys/param.h>
 
-
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/logger.h>
 #include <atalk/afp.h>
 #include <atalk/util.h>
 #include <atalk/cnid.h>
index f9a84039a793e64e2d563d6a1b8e81c85fc5aa90..8c602b0875b285c3f0b4383ce488d9092ae35779 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: filedir.c,v 1.54 2009-09-01 13:15:13 franklahm Exp $
+ * $Id: filedir.c,v 1.55 2009-10-02 09:32:40 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -32,8 +32,9 @@ char *strchr (), *strrchr ();
 #include <dirent.h>
 #include <errno.h>
 #include <sys/param.h>
-#include <atalk/adouble.h>
 
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
 #include <atalk/afp.h>
 #include <atalk/util.h>
 #include <atalk/cnid.h>
@@ -755,7 +756,7 @@ int ibuflen  _U_, *rbuflen;
             if (!isdir && !vol_unix_priv(vol)) {
                 int  admode = ad_mode("", 0777) | vol->v_fperm;
 
-                setfilmode(upath, admode, NULL);
+                setfilmode(upath, admode, NULL, vol->v_umask);
                 vol->vfs->rf_setfilmode(vol, upath, admode, NULL);
             }
         setvoltime(obj, vol );
index 02adafbae080f3d7463b4acc370635e8fe4d3c57..dcac14b6bd40a7f4ae8ada703b2244721d5d5261 100644 (file)
@@ -14,7 +14,7 @@
  * into proprietary software; there is no requirement for such software to
  * contain a copyright notice related to this source.
  *
- * $Id: hash.h,v 1.1 2005-04-30 21:33:41 didg Exp $
+ * $Id: hash.h,v 1.2 2009-10-02 09:32:40 franklahm Exp $
  * $Name:  $
  */
 
 #define HASH_H
 
 #include <limits.h>
-#ifdef KAZLIB_SIDEEFFECT_DEBUG
-#include "sfx.h"
-#endif
-
-/*
- * Blurb for inclusion into C++ translation units
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef unsigned long hashcount_t;
-#define HASHCOUNT_T_MAX ULONG_MAX
-
-typedef unsigned long hash_val_t;
-#define HASH_VAL_T_MAX ULONG_MAX
-
-extern int hash_val_t_bit;
-
-#ifndef HASH_VAL_T_BIT
-#define HASH_VAL_T_BIT ((int) hash_val_t_bit)
-#endif
-
-/*
- * Hash chain node structure.
- * Notes:
- * 1. This preprocessing directive is for debugging purposes.  The effect is
- *    that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the
- *    inclusion of this header,  then the structure shall be declared as having
- *    the single member   int __OPAQUE__.   This way, any attempts by the
- *    client code to violate the principles of information hiding (by accessing
- *    the structure directly) can be diagnosed at translation time. However,
- *    note the resulting compiled unit is not suitable for linking.
- * 2. This is a pointer to the next node in the chain. In the last node of a
- *    chain, this pointer is null.
- * 3. The key is a pointer to some user supplied data that contains a unique
- *    identifier for each hash node in a given table. The interpretation of
- *    the data is up to the user. When creating or initializing a hash table,
- *    the user must supply a pointer to a function for comparing two keys,
- *    and a pointer to a function for hashing a key into a numeric value.
- * 4. The value is a user-supplied pointer to void which may refer to
- *    any data object. It is not interpreted in any way by the hashing
- *    module.
- * 5. The hashed key is stored in each node so that we don't have to rehash
- *    each key when the table must grow or shrink.
- */
-
-typedef struct hnode_t {
-    #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)  /* 1 */
-    struct hnode_t *hash_next;         /* 2 */
-    const void *hash_key;              /* 3 */
-    void *hash_data;                   /* 4 */
-    hash_val_t hash_hkey;              /* 5 */
-    #else
-    int hash_dummy;
-    #endif
-} hnode_t;
-
-/*
- * The comparison function pointer type. A comparison function takes two keys
- * and produces a value of -1 if the left key is less than the right key, a
- * value of 0 if the keys are equal, and a value of 1 if the left key is
- * greater than the right key.
- */
-
-typedef int (*hash_comp_t)(const void *, const void *);
-
-/*
- * The hashing function performs some computation on a key and produces an
- * integral value of type hash_val_t based on that key. For best results, the
- * function should have a good randomness properties in *all* significant bits
- * over the set of keys that are being inserted into a given hash table. In
- * particular, the most significant bits of hash_val_t are most significant to
- * the hash module. Only as the hash table expands are less significant bits
- * examined. Thus a function that has good distribution in its upper bits but
- * not lower is preferrable to one that has poor distribution in the upper bits
- * but not the lower ones.
- */
-
-typedef hash_val_t (*hash_fun_t)(const void *);
-
-/*
- * allocator functions
- */
-
-typedef hnode_t *(*hnode_alloc_t)(void *);
-typedef void (*hnode_free_t)(hnode_t *, void *);
-
-/*
- * This is the hash table control structure. It keeps track of information
- * about a hash table, as well as the hash table itself.
- * Notes:
- * 1.  Pointer to the hash table proper. The table is an array of pointers to
- *     hash nodes (of type hnode_t). If the table is empty, every element of
- *     this table is a null pointer. A non-null entry points to the first
- *     element of a chain of nodes.
- * 2.  This member keeps track of the size of the hash table---that is, the
- *     number of chain pointers.
- * 3.  The count member maintains the number of elements that are presently
- *     in the hash table.
- * 4.  The maximum count is the greatest number of nodes that can populate this
- *     table. If the table contains this many nodes, no more can be inserted,
- *     and the hash_isfull() function returns true.
- * 5.  The high mark is a population threshold, measured as a number of nodes,
- *     which, if exceeded, will trigger a table expansion. Only dynamic hash
- *     tables are subject to this expansion.
- * 6.  The low mark is a minimum population threshold, measured as a number of
- *     nodes. If the table population drops below this value, a table shrinkage
- *     will occur. Only dynamic tables are subject to this reduction.  No table
- *     will shrink beneath a certain absolute minimum number of nodes.
- * 7.  This is the a pointer to the hash table's comparison function. The
- *     function is set once at initialization or creation time.
- * 8.  Pointer to the table's hashing function, set once at creation or
- *     initialization time.
- * 9.  The current hash table mask. If the size of the hash table is 2^N,
- *     this value has its low N bits set to 1, and the others clear. It is used
- *     to select bits from the result of the hashing function to compute an
- *     index into the table.
- * 10. A flag which indicates whether the table is to be dynamically resized. It
- *     is set to 1 in dynamically allocated tables, 0 in tables that are
- *     statically allocated.
- */
-
-typedef struct hash_t {
-    #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
-    struct hnode_t **hash_table;               /* 1 */
-    hashcount_t hash_nchains;                  /* 2 */
-    hashcount_t hash_nodecount;                        /* 3 */
-    hashcount_t hash_maxcount;                 /* 4 */
-    hashcount_t hash_highmark;                 /* 5 */
-    hashcount_t hash_lowmark;                  /* 6 */
-    hash_comp_t hash_compare;                  /* 7 */
-    hash_fun_t hash_function;                  /* 8 */
-    hnode_alloc_t hash_allocnode;
-    hnode_free_t hash_freenode;
-    void *hash_context;
-    hash_val_t hash_mask;                      /* 9 */
-    int hash_dynamic;                          /* 10 */
-    #else
-    int hash_dummy;
-    #endif
-} hash_t;
-
-/*
- * Hash scanner structure, used for traversals of the data structure.
- * Notes:
- * 1. Pointer to the hash table that is being traversed.
- * 2. Reference to the current chain in the table being traversed (the chain
- *    that contains the next node that shall be retrieved).
- * 3. Pointer to the node that will be retrieved by the subsequent call to
- *    hash_scan_next().
- */
-
-typedef struct hscan_t {
-    #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
-    hash_t *hash_table;                /* 1 */
-    hash_val_t hash_chain;     /* 2 */
-    hnode_t *hash_next;                /* 3 */
-    #else
-    int hash_dummy;
-    #endif
-} hscan_t;
+#include <atalk/hash.h>
 
 extern hash_t *hash_create(hashcount_t, hash_comp_t, hash_fun_t);
 extern void hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *);
@@ -233,8 +71,4 @@ extern void hnode_destroy(hnode_t *);
 #define hnode_put(N, V) ((N)->hash_data = (V))
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif
index 1ad282f227f08f0049a0ed9d233b9e5e5975e58a..ffe594a01caefea42f9c8869225eec712d17d9a9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: unix.c,v 1.52 2009-02-02 11:55:01 franklahm Exp $
+ * $Id: unix.c,v 1.53 2009-10-02 09:32:40 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -36,6 +36,7 @@ char *strchr (), *strrchr ();
 #include <sys/stat.h>
 #include <atalk/logger.h>
 #include <atalk/adouble.h>
+#include <atalk/vfs.h>
 #include <atalk/afp.h>
 #include <atalk/util.h>
 
@@ -249,78 +250,6 @@ struct maccess     *ma;
     return( mode );
 }
 
-/* ----------------------------- */
-char *fullpathname(const char *name)
-{
-    static char wd[ MAXPATHLEN + 1];
-
-    if ( getcwd( wd , MAXPATHLEN) ) {
-        strlcat(wd, "/", MAXPATHLEN);
-        strlcat(wd, name, MAXPATHLEN);
-    }
-    else {
-        strlcpy(wd, name, MAXPATHLEN);
-    }
-    return wd;
-}
-
-/* -----------------------------
-   a dropbox is a folder where w is set but not r eg:
-   rwx-wx-wx or rwx-wx-- 
-   rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
-*/
-int stickydirmode(name, mode, dropbox)
-const char * name;
-const mode_t mode;
-const int dropbox;
-{
-    int retval = 0;
-
-#ifdef DROPKLUDGE
-    /* Turn on the sticky bit if this is a drop box, also turn off the setgid bit */
-    if ((dropbox & AFPVOL_DROPBOX)) {
-        int uid;
-
-        if ( ( (mode & S_IWOTH) && !(mode & S_IROTH)) ||
-             ( (mode & S_IWGRP) && !(mode & S_IRGRP)) )
-        {
-            uid=geteuid();
-            if ( seteuid(0) < 0) {
-                LOG(log_error, logtype_afpd, "stickydirmode: unable to seteuid root: %s", strerror(errno));
-            }
-            if ( (retval=chmod( name, ( (DIRBITS | mode | S_ISVTX) & ~default_options.umask) )) < 0) {
-                LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
-            } else {
-#ifdef DEBUG
-                LOG(log_info, logtype_afpd, "stickydirmode: (debug) chmod \"%s\": %s", fullpathname(name), strerror(retval) );
-#endif /* DEBUG */
-            }
-            seteuid(uid);
-            return retval;
-        }
-    }
-#endif /* DROPKLUDGE */
-
-    /*
-     *  Ignore EPERM errors:  We may be dealing with a directory that is
-     *  group writable, in which case chmod will fail.
-     */
-    if ( (chmod( name, (DIRBITS | mode) & ~default_options.umask ) < 0) && errno != EPERM && 
-               !(errno == ENOENT && (dropbox & AFPVOL_NOADOUBLE)) )  
-    {
-        LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
-        retval = -1;
-    }
-
-    return retval;
-}
-
-/* ------------------------- */
-int dir_rx_set(mode_t mode)
-{
-    return (mode & (S_IXUSR | S_IRUSR)) == (S_IXUSR | S_IRUSR);
-}
-
 #define EXEC_MODE (S_IXGRP | S_IXUSR | S_IXOTH)
 
 int setdeskmode( mode )
@@ -415,33 +344,12 @@ mode_t mode;
         
     mode |= vol->v_fperm;
 
-    if (setfilmode( path->u_name, mode, &path->st) < 0)
+    if (setfilmode( path->u_name, mode, &path->st, vol->v_umask) < 0)
         return -1;
     /* we need to set write perm if read set for resource fork */
     return vol->vfs->rf_setfilmode(vol, path->u_name, mode, &path->st);
 }
 
-/* --------------------- */
-int setfilmode(name, mode, st)
-const char * name;
-mode_t mode;
-struct stat *st;
-{
-struct stat sb;
-mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;  /* rwx for owner group and other, by default */
-
-    if (!st) {
-        if (stat(name, &sb) != 0)
-            return -1;
-        st = &sb;
-    }
-   
-   mode |= st->st_mode & ~mask; /* keep other bits from previous mode */
-   if ( chmod( name,  mode & ~default_options.umask ) < 0 && errno != EPERM ) {
-       return -1;
-   }
-   return 0;
-}
 
 /* --------------------- */
 int setdirunixmode( vol, name, mode )
@@ -455,14 +363,14 @@ mode_t           mode;
 
     if (dir_rx_set(mode)) {
        /* extending right? dir first then .AppleDouble in rf_setdirmode */
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
+       if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 )
                return -1;
     }
     if (vol->vfs->rf_setdirunixmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) {
         return  -1 ;
     }
     if (!dir_rx_set(mode)) {
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
+       if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 )
             return -1;
     }
     return 0;
@@ -486,7 +394,7 @@ mode_t           mode;
 
     if (dir_rx_set(mode)) {
        /* extending right? dir first */
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
+       if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 )
                return -1;
     }
     
@@ -508,7 +416,7 @@ mode_t           mode;
         if (!S_ISDIR(st.st_mode)) {
            int setmode = (osx && *dirp->d_name == '.')?hf_mode:mode;
 
-           if (setfilmode(dirp->d_name, setmode, &st) < 0) {
+           if (setfilmode(dirp->d_name, setmode, &st, vol->v_umask) < 0) {
                 LOG(log_error, logtype_afpd, "setdirmode: chmod %s: %s",dirp->d_name, strerror(errno) );
                 return -1;
            }
@@ -521,7 +429,7 @@ mode_t           mode;
     }
 
     if (!dir_rx_set(mode)) {
-       if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
+       if ( stickydirmode(name, DIRBITS | mode, dropbox, vol->v_umask) < 0 )
                return -1;
     }
     return( 0 );
index d2e0a6fef04d56ed6c02e2ff3dc5f522e448fd21..c319bcd6e0ac50d4ab6454545b143227664d741c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: unix.h,v 1.20 2005-06-02 12:32:18 didg Exp $
+ * $Id: unix.h,v 1.21 2009-10-02 09:32:40 franklahm Exp $
  */
 
 #ifndef AFPD_UNIX_H
@@ -221,15 +221,9 @@ extern int setdirunixmode   __P((const struct vol *, const char *, mode_t));
 extern int setdirmode       __P((const struct vol *, const char *, mode_t));
 extern int setdeskowner     __P((const uid_t, const gid_t));
 extern int setdirowner      __P((const struct vol *, const char *, const uid_t, const gid_t));
-extern int setfilmode       __P((const char *, mode_t , struct stat *));
 extern int setfilunixmode   __P((const struct vol *, struct path*, const mode_t));
 extern int setfilowner      __P((const struct vol *, const uid_t, const gid_t, struct path*));
-extern int unix_rename      __P((const char *oldpath, const char *newpath));
-extern int dir_rx_set       __P((mode_t mode));
-extern int stickydirmode    __P((const char * name, const mode_t mode, const int dropbox));
-
 extern void accessmode      __P((char *, struct maccess *, struct dir *, struct stat *));
-extern char *fullpathname   __P((const char *));
 
 #ifdef AFS     
     #define accessmode afsmode
index 979a2e295a077054ea53eee3753ca3206e228286..a8ffaca9f90031ada7557d46ea246d487bae459b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: volume.c,v 1.90 2009-09-11 09:14:16 franklahm Exp $
+ * $Id: volume.c,v 1.91 2009-10-02 09:32:40 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -125,6 +125,7 @@ m=u -> map both ways
 #define VOLOPT_DPERM     24  /* dperm default directories perms */
 #define VOLOPT_FPERM     25  /* fperm default files perms */
 #define VOLOPT_DFLTPERM  26  /* perm */
+#define VOLOPT_EA_VFS    27  /* Extended Attributes vfs indirection */
 
 #define VOLOPT_MAX       (VOLOPT_DFLTPERM +1)
 
@@ -133,10 +134,11 @@ m=u -> map both ways
 #define VOLPASSLEN  8
 #define VOLOPT_DEFAULT     ":DEFAULT:"
 #define VOLOPT_DEFAULT_LEN 9
-  struct vol_option {
-      char *c_value;
-      int i_value;
-  };
+
+struct vol_option {
+    char *c_value;
+    int i_value;
+};
 
 typedef struct _special_folder {
         const char *name;
@@ -181,7 +183,6 @@ static const _vol_opt_name vol_opt_names[] = {
     {AFPVOL_CASEINSEN,  "CASEINSENSITIVE"}, /* volume is case insensitive */
     {AFPVOL_EILSEQ,     "ILLEGALSEQ"},     /* encode illegal sequence */
     {AFPVOL_CACHE,      "CACHEID"},     /* Use adouble v2 CNID caching. Default: yes */
-    {AFPVOL_EXT_ATTRS,  "EXT_ATTRS"},   /* Vol supports Extened Attributes */
     {AFPVOL_ACLS,       "ACLS"},        /* Vol supports ACLs */
     {AFPVOL_TM,         "TM"},          /* Set "kSupportsTMLockSteal" is volume attributes */
     {0, NULL}
@@ -491,8 +492,6 @@ 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)
@@ -553,6 +552,12 @@ static void volset(struct vol_option *options, struct vol_option *save,
     } else if (optionok(tmp, "denied_hosts:", val)) {
         setoption(options, save, VOLOPT_DENIED_HOSTS, val);
 
+    } else if (optionok(tmp, "ea:", val)) {
+        if (strcasecmp(val + 1, "ad") == 0) /* the default anyway */
+            options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_AD;
+        else if (strcasecmp(val + 1, "solaris") == 0)
+            options[VOLOPT_EA_VFS].i_value = AFPVOL_EA_SOLARIS;
+
     } else {
         /* ignore unknown options */
         LOG(log_debug, logtype_afpd, "ignoring unknown volume option: %s", tmp);
@@ -706,6 +711,7 @@ static int creatvol(AFPObj *obj, struct passwd *pwd,
     /* os X start at 1 and use network order ie. 1 2 3 */
     volume->v_vid = ++lastvid;
     volume->v_vid = htons(volume->v_vid);
+    volume->v_vfs_ea = AFPVOL_EA_AD;
 
     /* handle options */
     if (options) {
@@ -714,6 +720,9 @@ static int creatvol(AFPObj *obj, struct passwd *pwd,
 
         /* shift in some flags */
         volume->v_flags = options[VOLOPT_FLAGS].i_value;
+        
+        if (options[VOLOPT_EA_VFS].i_value != AFPVOL_EA_AD)
+            volume->v_vfs_ea = options[VOLOPT_EA_VFS].i_value;
 
         volume->v_ad_options = 0;
         if ((volume->v_flags & AFPVOL_NODEV))
@@ -1463,6 +1472,7 @@ int               *buflen;
                 ashort |= VOLPBIT_ATTR_RO;
             }
             ashort |= VOLPBIT_ATTR_CATSEARCH;
+            ashort |= VOLPBIT_ATTR_EXT_ATTRS;
             if (afp_version >= 30) {
                 ashort |= VOLPBIT_ATTR_UTF8;
                 if (vol->v_flags & AFPVOL_UNIX_PRIV)
@@ -1471,10 +1481,8 @@ int              *buflen;
                     ashort |= VOLPBIT_ATTR_TM;
             }
             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;
+                ashort |= VOLPBIT_ATTR_ACLS;
             }
             ashort = htons(ashort);
             memcpy(data, &ashort, sizeof( ashort ));
index b579362bc5e246e790579a43c6d226c3c1accc83..cc3dfdc9a8e5a4c0bc260758193df1b3a24ccb53 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: volume.h,v 1.33 2009-09-11 09:14:16 franklahm Exp $
+ * $Id: volume.h,v 1.34 2009-10-02 09:32:40 franklahm Exp $
  *
  * Copyright (c) 1990,1994 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
 #include <sys/types.h>
 #include <netatalk/endian.h>
 
-#include "atalk/unicode.h"
-#include "globals.h"
-#include "afp_vfs.h"
-#include "hash.h"
-
-#define AFPVOL_U8MNAMELEN   255 /* AFP3 sepc */
-#define AFPVOL_MACNAMELEN    27 /* AFP2 spec */
-
+#include <atalk/volume.h>
 #include <atalk/cnid.h>
+#include <atalk/unicode.h>
 
-struct vol {
-    struct vol         *v_next;
-    char        *v_localname;   /* as defined in AppleVolumes.default */
-    ucs2_t             *v_u8mname;     /* converted to utf8-mac in ucs2 */
-    ucs2_t             *v_macname;     /* mangled to legacy longname in ucs2 */
-    ucs2_t             *v_name;        /* either v_u8mname or v_macname */
-    char               *v_path;
-    
-    struct dir         *v_dir, *v_root;
-    hash_t             *v_hash;
-    int                        v_flags;
-#ifdef __svr4__
-    int                        v_qfd;
-#endif /*__svr4__*/
-    char               *v_gvs;
-    time_t             v_mtime;
-    time_t             v_ctime;  /* volume creation date, not unix ctime */
-
-    u_int16_t          v_vid;
-    void                *v_nfsclient;
-    int                 v_nfs;
-    
-    int                 v_casefold;
-    size_t              max_filename;
-    
-    char                *v_password;
-    char                *v_veto;
-
-    char                *v_cnidscheme;
-    char                *v_dbpath;
-    dev_t               v_dev;              /* Unix volume device */
-    struct _cnid_db     *v_cdb;
-    char                v_stamp[ADEDLEN_PRIVSYN];
-    mode_t             v_umask;
-    mode_t             v_perm;             /* default permission value OR with requested perm*/
-    mode_t             v_dperm;             /* default directories permission value OR with requested perm*/
-    mode_t             v_fperm;             /* default files permission value OR with requested perm*/
-
-#ifdef FORCE_UIDGID
-    char               *v_forceuid;
-    char               *v_forcegid;
-#endif 
-
-    char                *v_volcodepage;
-    charset_t          v_volcharset;   
-    struct charset_functions   *v_vol;
-    char               *v_maccodepage;
-    charset_t          v_maccharset;
-    struct charset_functions   *v_mac;
-
-    int                 v_deleted;    /* volume open but deleted in new config file */
-    int                 v_hide;       /* new volume wait until old volume is closed */
-    int                 v_new;        /* volume deleted but there's a new one with the same name */
-    int                 v_adouble;    /* default adouble format */
-    int                        v_ad_options; /* adouble option NODEV, NOCACHE, etc.. */
-
-    char                *v_root_preexec;
-    char                *v_preexec;
-
-    char                *v_root_postexec;
-    char                *v_postexec;
-
-    int                 v_root_preexec_close;
-    int                 v_preexec_close;
-    
-    /* adouble indirection */
-    struct vfs_ops      *vfs;
-    int                 (*validupath)(const struct vol *, const char *);
-    char                *(*ad_path)(const char *, int);
-};
-
-#ifdef NO_LARGE_VOL_SUPPORT
-typedef u_int32_t VolSpace;
-#else /* NO_LARGE_VOL_SUPPORT */
-typedef u_int64_t VolSpace;
-#endif /* NO_LARGE_VOL_SUPPORT */
-
-#define AFPVOL_OPEN    (1<<0)
-
-/* flags  for AFS and quota 0xxx0 */
-#define AFPVOL_GVSMASK (7<<2)
-#define AFPVOL_NONE    (0<<2)
-#define AFPVOL_AFSGVS  (1<<2)
-#define AFPVOL_USTATFS (2<<2)
-#define AFPVOL_UQUOTA  (4<<2)
-
-/* 
-   Flags that alter volume behaviour.
-   Keep in sync with include/atalk/volinfo.h and libatalk/util/volinfo.c
-*/
-#define AFPVOL_A2VOL     (1 << 5)   /* prodos volume */
-#define AFPVOL_CRLF      (1 << 6)   /* cr/lf translation */
-#define AFPVOL_NOADOUBLE (1 << 7)   /* don't create .AppleDouble by default */
-#define AFPVOL_RO        (1 << 8)   /* read-only volume */
-#define AFPVOL_MSWINDOWS (1 << 9)   /* deal with ms-windows yuckiness.
-this is going away. */
-#define AFPVOL_NOHEX     (1 << 10)  /* don't do :hex translation */
-#define AFPVOL_USEDOTS   (1 << 11)  /* use real dots */
-#define AFPVOL_LIMITSIZE (1 << 12)  /* limit size for older macs */
-#define AFPVOL_MAPASCII  (1 << 13)  /* map the ascii range as well */
-#define AFPVOL_DROPBOX   (1 << 14)  /* dropkludge dropbox support */
-#define AFPVOL_NOFILEID  (1 << 15)  /* don't advertise createid resolveid and deleteid calls */
-#define AFPVOL_NOSTAT    (1 << 16)  /* advertise the volume even if we can't stat() it
-                                     * maybe because it will be mounted later in preexec */
-#define AFPVOL_UNIX_PRIV (1 << 17)  /* support unix privileges */
-#define AFPVOL_NODEV     (1 << 18)  /* always use 0 for device number in cnid calls 
-                                     * help if device number is notconsistent across reboot 
-                                     * NOTE symlink to a different device will return an ACCESS error
-                                     */
-#define AFPVOL_CASEINSEN (1 << 19)  /* volume is case insensitive */
-#define AFPVOL_EILSEQ    (1 << 20)  /* encode illegal sequence 'asis' UCS2, ex "\217-", which is not 
-                                       a valid SHIFT-JIS char, is encoded  as U\217 -*/
-
-#define AFPVOL_CACHE     (1 << 21)   /* Use adouble v2 CNID caching. Default: yes */
-#define AFPVOL_INV_DOTS  (1 << 22)   /* dots files are invisible */
-#define AFPVOL_EXT_ATTRS (1 << 23)   /* Volume supports Extended Attributes */
-#define AFPVOL_TM        (1 << 24)   /* Supports TimeMachine */
-#define AFPVOL_ACLS      (1 << 25)   /* Volume supports ACLS */
-
-/* FPGetSrvrParms options */
-#define AFPSRVR_CONFIGINFO     (1 << 0)
-#define AFPSRVR_PASSWD         (1 << 7)
-
-/* handle casefolding */
-#define AFPVOL_MTOUUPPER       (1 << 0) 
-#define AFPVOL_MTOULOWER       (1 << 1) 
-#define AFPVOL_UTOMUPPER       (1 << 2) 
-#define AFPVOL_UTOMLOWER       (1 << 3) 
-#define AFPVOL_UMLOWER         (AFPVOL_MTOULOWER | AFPVOL_UTOMLOWER)
-#define AFPVOL_UMUPPER         (AFPVOL_MTOUUPPER | AFPVOL_UTOMUPPER)
-#define AFPVOL_UUPPERMLOWER    (AFPVOL_MTOUUPPER | AFPVOL_UTOMLOWER)
-#define AFPVOL_ULOWERMUPPER    (AFPVOL_MTOULOWER | AFPVOL_UTOMUPPER)
-
-#define MSWINDOWS_BADCHARS ":\t\\/<>*?|\""
-
-int wincheck(const struct vol *vol, const char *path);
-
-#define AFPVOLSIG_FLAT          0x0001 /* flat fs */
-#define AFPVOLSIG_FIX          0x0002 /* fixed ids */
-#define AFPVOLSIG_VAR           0x0003 /* variable ids */
-#define AFPVOLSIG_DEFAULT       AFPVOLSIG_FIX
-
-/* volume attributes */
-#define VOLPBIT_ATTR_RO           (1 << 0)
-#define VOLPBIT_ATTR_PASSWD       (1 << 1)
-#define VOLPBIT_ATTR_FILEID       (1 << 2)
-#define VOLPBIT_ATTR_CATSEARCH    (1 << 3)
-#define VOLPBIT_ATTR_BLANKACCESS  (1 << 4)
-#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_TM           (1 << 13)
-
-#define VOLPBIT_ATTR   0
-#define VOLPBIT_SIG    1
-#define VOLPBIT_CDATE  2
-#define VOLPBIT_MDATE  3
-#define VOLPBIT_BDATE  4
-#define VOLPBIT_VID    5
-#define VOLPBIT_BFREE  6
-#define VOLPBIT_BTOTAL 7
-#define VOLPBIT_NAME   8
-/* handle > 4GB volumes */
-#define VOLPBIT_XBFREE  9
-#define VOLPBIT_XBTOTAL 10
-#define VOLPBIT_BSIZE   11        /* block size */
-
-
-#define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? \
-                           ADFLAGS_NOADOUBLE : 0)
-#ifdef AFP3x
-#define utf8_encoding() (afp_version >= 30)
-#else
-#define utf8_encoding() (0)
+#include "globals.h"
+#if 0
+#include "hash.h"
 #endif
 
-#define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
-
-#define vol_unix_priv(vol) (afp_version >= 30 && ((vol)->v_flags & AFPVOL_UNIX_PRIV))
-
-#define vol_inv_dots(vol) (((vol)->v_flags & AFPVOL_INV_DOTS) ? 1 : 0)
-
 extern struct vol      *getvolbyvid __P((const u_int16_t));
 extern int              ustatfs_getvolspace __P((const struct vol *,
             VolSpace *, VolSpace *,
index 77a7e898e7a7fd7219c6b263324840261df125b6..f7af5585791b9c6854599c9bfd59ea5b5d1c4b4d 100644 (file)
@@ -2,10 +2,10 @@
 
 atalkincludedir = $(includedir)/atalk
 atalkinclude_HEADERS = \
-       adouble.h aep.h afp.h asp.h atp.h boolean.h \
+       adouble.h vfs.h aep.h afp.h asp.h atp.h boolean.h \
        cnid.h compat.h ddp.h dsi.h ldapconfig.h list.h logger.h \
        nbp.h netddp.h pap.h paths.h rtmp.h server_child.h \
        server_ipc.h tdb.h uam.h unicode.h util.h uuid.h volinfo.h \
-       zip.h
+       zip.h ea.h
 
 noinst_HEADERS = cnid_dbd_private.h
index f5894e7d89d1ed02800772f47f18de572bc81b6b..580bff6afd70e8413c6984eb626b48c2b7f0576b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: adouble.h,v 1.42 2009-09-14 14:11:45 franklahm Exp $
+ * $Id: adouble.h,v 1.43 2009-10-02 09:32:40 franklahm Exp $
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
  * All Rights Reserved.
  *
 #define AD_APPLESINGLE_MAGIC 0x00051600
 #define AD_APPLEDOUBLE_MAGIC 0x00051607
 #define AD_MAGIC             AD_APPLEDOUBLE_MAGIC
-
 #define SFM_MAGIC            0x00504641
 
 /* sizes of relevant entry bits */
@@ -552,4 +551,13 @@ extern ssize_t ad_writefile __P((struct adouble *, const int,
 #endif /* HAVE_SENDFILE_WRITE */
 #endif /* 0 */
 
+/* ad_unix.c */
+extern int netatalk_unlink(const char *name);
+extern char *fullpathname(const char *);
+extern int netatalk_rmdir(const char *name);
+extern int setfilmode(const char *, mode_t, struct stat *, mode_t);
+extern int dir_rx_set(mode_t mode);
+extern int stickydirmode(const char *name, const mode_t mode, const int dropbox, const mode_t v_umask);
+extern int unix_rename(const char *oldpath, const char *newpath);
+
 #endif /* _ATALK_ADOUBLE_H */
index fff1f41ab5784cee3b4c900f6b1a952795b9dbe0..aecf954305916fdb16a7aa96a4e1eea5c4ab165d 100644 (file)
@@ -199,6 +199,7 @@ typedef enum {
 
 /* version 3.1 */
 #define AFP_ENUMERATE_EXT2      68
+#define AFP_SPOTLIGHT_PRIVATE   76
 #define AFP_SYNCDIR             78
 #define AFP_SYNCFORK            79
 #define AFP_ZZZ                 122
@@ -211,6 +212,5 @@ typedef enum {
 #define AFP_GETACL              73
 #define AFP_SETACL              74
 #define AFP_ACCESS              75
-#define AFP_SPOTLIGHT_PRIVATE   76
 
 #endif
diff --git a/include/atalk/directory.h b/include/atalk/directory.h
new file mode 100644 (file)
index 0000000..d02b4f2
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * $Id: directory.h,v 1.1 2009-10-02 09:32:40 franklahm Exp $
+ *
+ * Copyright (c) 1990,1991 Regents of The University of Michigan.
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation, and that the name of The University
+ * of Michigan not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. This software is supplied as is without expressed or
+ * implied warranties of any kind.
+ *
+ *     Research Systems Unix Group
+ *     The University of Michigan
+ *     c/o Mike Clark
+ *     535 W. William Street
+ *     Ann Arbor, Michigan
+ *     +1-313-763-0525
+ *     netatalk@itd.umich.edu
+ */
+
+#ifndef ATALK_DIRECTORY_H
+#define ATALK_DIRECTORY_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/endian.h>
+#include <dirent.h>
+
+#include <atalk/cnid.h>
+#include <atalk/directory.h>
+
+/* setgid directories */
+#ifndef DIRBITS
+# ifdef AFS
+#  define DIRBITS 0
+# else /* AFS */
+#  define DIRBITS S_ISGID
+# endif /* AFS */
+#endif /* DIRBITS */
+
+/* the did tree is now a red-black tree while the parent/child
+ * tree is a circular doubly-linked list. how exciting. */
+struct dir {
+    struct dir *d_left, *d_right, *d_back; /* for red-black tree */
+    int                d_color;
+    struct dir  *d_parent, *d_child; /* parent-child */
+    struct dir  *d_prev, *d_next;    /* siblings */
+    void        *d_ofork;            /* oforks using this directory. */
+    u_int32_t   d_did;
+    int                d_flags;
+
+    time_t      ctime;                /* inode ctime */
+    u_int32_t   offcnt;               /* offspring count */
+
+    char       *d_m_name;            /* mac name */
+    char        *d_u_name;            /* unix name */
+    ucs2_t     *d_m_name_ucs2;       /* mac name as UCS2 */
+};
+
+struct path {
+    int         m_type;             /* mac name type (long name, unicode */
+    char       *m_name;            /* mac name */
+    char        *u_name;            /* unix name */
+    cnid_t     id;                 /* file id (only for getmetadata) */
+    struct dir  *d_dir;             /* */
+    int         st_valid;           /* does st_errno and st set */
+    int         st_errno;
+    struct stat st;
+};
+
+static inline int path_isadir(struct path *o_path)
+{
+    return o_path->d_dir != NULL;
+#if 0
+    return o_path->m_name == '\0' || /* we are in a it */
+           !o_path->st_valid ||      /* in cache but we can't chdir in it */ 
+           (!o_path->st_errno && S_ISDIR(o_path->st.st_mode)); /* not in cache an can't chdir */
+#endif
+}
+
+
+#endif /* ATALK_DIRECTORY_H */
diff --git a/include/atalk/ea.h b/include/atalk/ea.h
new file mode 100644 (file)
index 0000000..3509745
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+   $Id: ea.h,v 1.1 2009-10-02 09:32:40 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 ATALK_EA_H
+#define ATALK_EA_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*
+ * 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
+
+/* 
+ * Library user must provide a static buffer of size ATTRNAMEBUFSIZ.
+ * It's used when listing EAs as intermediate buffer. For afpd it's
+ * defined in extattrs.c.
+ */
+#define ATTRNAMEBUFSIZ 4096
+
+enum {
+    kXAttrNoFollow = 0x1,
+    kXAttrCreate = 0x2,
+    kXAttrReplace = 0x4
+};
+
+
+#define EA_MAGIC    0x61644541 /* "adEA" */
+#define EA_VERSION1 0x01
+#define EA_VERSION  EA_VERSION1
+
+typedef enum {
+    /* ea_open flags */
+    EA_CREATE    = (1<<1),      /* create if not existing on ea_open */
+    EA_RDONLY    = (1<<2),      /* open read only */
+    EA_RDWR      = (1<<3),      /* open read/write */
+    /* ea_open internal flags */
+    EA_DIR       = (1<<4)       /* ea header file is for a dir, ea_open adds it as appropiate */
+} eaflags_t;
+
+#define EA_MAGIC_OFF   0
+#define EA_MAGIC_LEN   4
+#define EA_VERSION_OFF (EA_MAGIC_OFF + EA_MAGIC_LEN)
+#define EA_VERSION_LEN 2
+#define EA_COUNT_OFF   (EA_VERSION_OFF + EA_VERSION_LEN)
+#define EA_COUNT_LEN   2
+#define EA_HEADER_SIZE (EA_MAGIC_LEN + EA_VERSION_LEN + EA_COUNT_LEN)
+
+/* 
+ * structs describing the layout of the Extended Attributes bookkeeping file.
+ * This isn't really an AppleDouble structure, it's just a binary blob that
+ * lives in our .AppleDouble directory too.
+ */
+
+struct ea_entry {
+    size_t       ea_namelen; /* len of ea_name without terminating 0 ie. strlen(ea_name)*/
+    size_t       ea_size;    /* size of EA*/
+    char         *ea_name;   /* name of the EA */
+};
+
+/* We read the on-disk data into *ea_data and parse it into this*/
+struct ea {
+    const struct vol     *vol;            /* vol handle, ea_close needs it */
+    char                 *filename;       /* name of file, needed by ea_close too */
+    unsigned int         ea_count;        /* number of EAs in ea_entries array */
+    struct ea_entry      (*ea_entries)[]; /* malloced and realloced as needed by ea_count*/
+    int                  ea_fd;           /* open fd for ea_data */
+    eaflags_t            ea_flags;        /* flags */
+    size_t               ea_size;         /* size of header file = size of ea_data buffer */
+    char                 *ea_data;        /* pointer to buffer into that we actually *
+                                           * read the disc file into                 */
+};
+
+/* On-disk format, just for reference ! */
+#if 0
+struct ea_entry_ondisk {
+    uint32_t               ea_size;
+    char                   ea_name[]; /* zero terminated string */
+};
+
+struct ea_ondisk {
+    u_int32_t              ea_magic;
+    u_int16_t              ea_version;
+    u_int16_t              ea_count;
+    struct ea_entry_ondisk ea_entries[ea_count];
+};
+#endif /* 0 */
+
+#endif /* ATALK_EA_H */
diff --git a/include/atalk/hash.h b/include/atalk/hash.h
new file mode 100644 (file)
index 0000000..9195a4b
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Hash Table Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: hash.h,v 1.1 2009-10-02 09:32:40 franklahm Exp $
+ * $Name:  $
+ */
+
+#ifndef ATALK_HASH_H
+#define ATALK_HASH_H
+
+#include <limits.h>
+
+typedef unsigned long hashcount_t;
+#define HASHCOUNT_T_MAX ULONG_MAX
+
+typedef unsigned long hash_val_t;
+#define HASH_VAL_T_MAX ULONG_MAX
+
+extern int hash_val_t_bit;
+
+#ifndef HASH_VAL_T_BIT
+#define HASH_VAL_T_BIT ((int) hash_val_t_bit)
+#endif
+
+/*
+ * Hash chain node structure.
+ * Notes:
+ * 1. This preprocessing directive is for debugging purposes.  The effect is
+ *    that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the
+ *    inclusion of this header,  then the structure shall be declared as having
+ *    the single member   int __OPAQUE__.   This way, any attempts by the
+ *    client code to violate the principles of information hiding (by accessing
+ *    the structure directly) can be diagnosed at translation time. However,
+ *    note the resulting compiled unit is not suitable for linking.
+ * 2. This is a pointer to the next node in the chain. In the last node of a
+ *    chain, this pointer is null.
+ * 3. The key is a pointer to some user supplied data that contains a unique
+ *    identifier for each hash node in a given table. The interpretation of
+ *    the data is up to the user. When creating or initializing a hash table,
+ *    the user must supply a pointer to a function for comparing two keys,
+ *    and a pointer to a function for hashing a key into a numeric value.
+ * 4. The value is a user-supplied pointer to void which may refer to
+ *    any data object. It is not interpreted in any way by the hashing
+ *    module.
+ * 5. The hashed key is stored in each node so that we don't have to rehash
+ *    each key when the table must grow or shrink.
+ */
+
+typedef struct hnode_t {
+    #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)  /* 1 */
+    struct hnode_t *hash_next;         /* 2 */
+    const void *hash_key;              /* 3 */
+    void *hash_data;                   /* 4 */
+    hash_val_t hash_hkey;              /* 5 */
+    #else
+    int hash_dummy;
+    #endif
+} hnode_t;
+
+/*
+ * The comparison function pointer type. A comparison function takes two keys
+ * and produces a value of -1 if the left key is less than the right key, a
+ * value of 0 if the keys are equal, and a value of 1 if the left key is
+ * greater than the right key.
+ */
+
+typedef int (*hash_comp_t)(const void *, const void *);
+
+/*
+ * The hashing function performs some computation on a key and produces an
+ * integral value of type hash_val_t based on that key. For best results, the
+ * function should have a good randomness properties in *all* significant bits
+ * over the set of keys that are being inserted into a given hash table. In
+ * particular, the most significant bits of hash_val_t are most significant to
+ * the hash module. Only as the hash table expands are less significant bits
+ * examined. Thus a function that has good distribution in its upper bits but
+ * not lower is preferrable to one that has poor distribution in the upper bits
+ * but not the lower ones.
+ */
+
+typedef hash_val_t (*hash_fun_t)(const void *);
+
+/*
+ * allocator functions
+ */
+
+typedef hnode_t *(*hnode_alloc_t)(void *);
+typedef void (*hnode_free_t)(hnode_t *, void *);
+
+/*
+ * This is the hash table control structure. It keeps track of information
+ * about a hash table, as well as the hash table itself.
+ * Notes:
+ * 1.  Pointer to the hash table proper. The table is an array of pointers to
+ *     hash nodes (of type hnode_t). If the table is empty, every element of
+ *     this table is a null pointer. A non-null entry points to the first
+ *     element of a chain of nodes.
+ * 2.  This member keeps track of the size of the hash table---that is, the
+ *     number of chain pointers.
+ * 3.  The count member maintains the number of elements that are presently
+ *     in the hash table.
+ * 4.  The maximum count is the greatest number of nodes that can populate this
+ *     table. If the table contains this many nodes, no more can be inserted,
+ *     and the hash_isfull() function returns true.
+ * 5.  The high mark is a population threshold, measured as a number of nodes,
+ *     which, if exceeded, will trigger a table expansion. Only dynamic hash
+ *     tables are subject to this expansion.
+ * 6.  The low mark is a minimum population threshold, measured as a number of
+ *     nodes. If the table population drops below this value, a table shrinkage
+ *     will occur. Only dynamic tables are subject to this reduction.  No table
+ *     will shrink beneath a certain absolute minimum number of nodes.
+ * 7.  This is the a pointer to the hash table's comparison function. The
+ *     function is set once at initialization or creation time.
+ * 8.  Pointer to the table's hashing function, set once at creation or
+ *     initialization time.
+ * 9.  The current hash table mask. If the size of the hash table is 2^N,
+ *     this value has its low N bits set to 1, and the others clear. It is used
+ *     to select bits from the result of the hashing function to compute an
+ *     index into the table.
+ * 10. A flag which indicates whether the table is to be dynamically resized. It
+ *     is set to 1 in dynamically allocated tables, 0 in tables that are
+ *     statically allocated.
+ */
+
+typedef struct hash_t {
+    #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+    struct hnode_t **hash_table;               /* 1 */
+    hashcount_t hash_nchains;                  /* 2 */
+    hashcount_t hash_nodecount;                        /* 3 */
+    hashcount_t hash_maxcount;                 /* 4 */
+    hashcount_t hash_highmark;                 /* 5 */
+    hashcount_t hash_lowmark;                  /* 6 */
+    hash_comp_t hash_compare;                  /* 7 */
+    hash_fun_t hash_function;                  /* 8 */
+    hnode_alloc_t hash_allocnode;
+    hnode_free_t hash_freenode;
+    void *hash_context;
+    hash_val_t hash_mask;                      /* 9 */
+    int hash_dynamic;                          /* 10 */
+    #else
+    int hash_dummy;
+    #endif
+} hash_t;
+
+/*
+ * Hash scanner structure, used for traversals of the data structure.
+ * Notes:
+ * 1. Pointer to the hash table that is being traversed.
+ * 2. Reference to the current chain in the table being traversed (the chain
+ *    that contains the next node that shall be retrieved).
+ * 3. Pointer to the node that will be retrieved by the subsequent call to
+ *    hash_scan_next().
+ */
+
+typedef struct hscan_t {
+    #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+    hash_t *hash_table;                /* 1 */
+    hash_val_t hash_chain;     /* 2 */
+    hnode_t *hash_next;                /* 3 */
+    #else
+    int hash_dummy;
+    #endif
+} hscan_t;
+
+
+#endif /* ATALK_HASH_H */
index bb5bed2b4e6540e1aa425e992f20f3263e02b86f..4811b47d009e887284d54edfde0750e7c24f0052 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: util.h,v 1.9 2009-04-28 13:01:24 franklahm Exp $
+ * $Id: util.h,v 1.10 2009-10-02 09:32:40 franklahm Exp $
  */
 
 #ifndef _ATALK_UTIL_H
@@ -106,4 +106,54 @@ extern int loadvolinfo __P((char *path, struct volinfo *vol));
 extern int vol_load_charsets __P(( struct volinfo *vol));
 #endif /* 0 */
 
+/*
+ * Function: lock_reg
+ *
+ * Purpose: lock a file with fctnl
+ *
+ * Arguments:
+ *
+ *    fd         (r) File descriptor
+ *    cmd        (r) cmd to fcntl, only F_SETLK is usable here
+ *    type       (r) F_RDLCK, F_WRLCK, F_UNLCK
+ *    offset     (r) byte offset relative to l_whence
+ *    whence     (r) SEEK_SET, SEEK_CUR, SEEK_END
+ *    len        (r) no. of bytes (0 means to EOF)
+ *
+ * Returns: 0 on success, -1 on failure
+ *          fcntl return value and errno
+ *
+ * Effects:
+ *
+ * Function called by macros to ease locking.
+ */
+extern int lock_reg(int fd, int cmd, int type, off_t offest, int whence, off_t len);
+
+/*
+ * Macros: read_lock, write_lock, un_lock
+ *
+ * Purpose: lock and unlock files
+ *
+ * Arguments:
+ *
+ *    fd         (r) File descriptor
+ *    offset     (r) byte offset relative to l_whence
+ *    whence     (r) SEEK_SET, SEEK_CUR, SEEK_END
+ *    len        (r) no. of bytes (0 means to EOF)
+ *
+ * Returns: 0 on success, -1 on failure
+ *          fcntl return value and errno
+ *
+ * Effects:
+ *
+ * Nice locking macros.
+ */
+
+#define read_lock(fd, offset, whence, len) \
+    lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len))
+#define write_lock(fd, offset, whence, len) \
+    lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
+#define unlock(fd, offset, whence, len) \
+    lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))
+
 #endif
diff --git a/include/atalk/vfs.h b/include/atalk/vfs.h
new file mode 100644 (file)
index 0000000..bca16be
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+   Copyright (c) 2004 Didier Gautheron
+   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.
+
+   vfs layer for afp
+*/
+
+#ifndef ATALK_VFS_H
+#define ATALK_VFS_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_NFSv4_ACLS
+#include <sys/acl.h>
+#endif
+
+#include <atalk/adouble.h>
+#include <atalk/volume.h>
+
+struct vfs_ops {
+    /* low level adouble fn */
+    char *(*ad_path)(const char *, int);
+
+    /* */
+    int (*validupath)(const struct vol *, const char *);
+    int (*rf_chown)(const struct vol *, const char *path, uid_t owner, gid_t group);
+    int (*rf_renamedir)(const struct vol *, const char *oldpath, const char *newpath);
+    int (*rf_deletecurdir)(const struct vol *);
+    int (*rf_setfilmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
+    int (*rf_setdirmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
+    int (*rf_setdirunixmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
+
+    int (*rf_setdirowner)(const struct vol *, const char *path, uid_t owner, gid_t group);
+
+    int (*rf_deletefile)(const struct vol *, const char * );
+    int (*rf_renamefile)(const struct vol *, const char *oldpath, const char *newpath);
+#ifdef HAVE_NFSv4_ACLS
+    int (*rf_acl)(const struct vol *, const char *path, int cmd, int count, ace_t *aces);
+    int (*rf_remove_acl)(const struct vol *, const char *path, int dir);
+#endif
+
+    /* Extended Attributes */
+    int (*get_easize)(const struct vol * restrict, char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, const char * restrict attruname);
+    int (*get_eacontent)(const struct vol * restrict , char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, const char * restrict attruname, int maxreply);
+    int (*list_eas)(const struct vol * restrict , char * restrict attrnamebuf, int * restrict buflen, const char * restrict uname, int oflag);
+    int (*set_ea)(const struct vol * restrict , const char * restrict uname, const char * restrict attruname, const char * restrict ibuf, size_t attrsize, int oflag);
+    int (*remove_ea)(const struct vol * restrict , const char * restrict uname, const char * restrict attruname, int oflag);
+};
+
+extern void initvol_vfs(struct vol * restrict vol);
+
+/* VFS inderected funcs ... : */
+/* ...default adouble EAs */
+extern int get_easize(const struct vol * restrict vol, char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, const char * restrict attruname);
+extern int get_eacontent(const struct vol * restrict vol, char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, const char * restrict attruname, int maxreply);
+extern int list_eas(const struct vol * restrict vol, char * restrict attrnamebuf, int * restrict buflen, const char * restrict uname, int oflag);
+extern int set_ea(const struct vol * restrict vol, const char * restrict uname, const char * restrict attruname, const char * restrict ibuf, size_t attrsize, int oflag);
+extern int remove_ea(const struct vol * restrict vol, const char * restrict uname, const char * restrict attruname, int oflag);
+
+/* ... Solaris native EAs */
+#ifdef HAVE_SOLARIS_EAS
+extern int sol_get_easize(const struct vol * restrict vol, char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, const char * restrict attruname);
+extern int sol_get_eacontent(const struct vol * restrict vol, char * restrict rbuf, int * restrict rbuflen, const char * restrict uname, int oflag, char * restrict attruname, int maxreply);
+extern int sol_list_eas(const struct vol * restrict vol, char * restrict attrnamebuf, int * restrict buflen, const char * restrict uname, int oflag);
+extern int sol_set_ea(const struct vol * restrict vol, const char * restrict uname, const char * restrict attruname, const char * restrict ibuf,size_t attrsize, int oflag);
+extern int sol_remove_ea(const struct vol * restrict vol, const char * restrict uname, const char * restrict attruname, int oflag);
+#endif /* HAVE_SOLARIS_EAS */
+
+/* Solaris NFSv4 ACL stuff */
+#ifdef HAVE_NFSv4_ACLS
+extern int get_nfsv4_acl(const char *name, ace_t **retAces);
+extern int remove_acl(const char *name);
+#endif /* HAVE_NFSv4_ACLS */
+
+#endif /* ATALK_VFS_H */
index 73bc29f5de84974d0f86dc19ba1a57424acfa305..541f28733ec14dccc0e2e2e19acb593be8eae8dc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: volinfo.h,v 1.7 2009-09-11 09:14:16 franklahm Exp $
+ * $Id: volinfo.h,v 1.8 2009-10-02 09:32:41 franklahm Exp $
  */
 
 #ifndef _ATALK_VOLINFO_H
@@ -32,7 +32,6 @@
                                        a valid SHIFT-JIS char, is encoded  as U\217 -*/
 #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_TM        (1 << 24)   /* Supports TimeMachine */
 #define AFPVOL_ACLS      (1 << 25)   /* Volume supports ACLS */
 
diff --git a/include/atalk/volume.h b/include/atalk/volume.h
new file mode 100644 (file)
index 0000000..64c5e96
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * $Id: volume.h,v 1.1 2009-10-02 09:32:41 franklahm Exp $
+ *
+ * Copyright (c) 1990,1994 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ */
+
+#ifndef ATALK_VOLUME_H
+#define ATALK_VOLUME_H 1
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <netatalk/endian.h>
+
+#include <atalk/unicode.h>
+#include <atalk/cnid.h>
+#include <atalk/hash.h>
+
+#define AFPVOL_U8MNAMELEN   255 /* AFP3 sepc */
+#define AFPVOL_MACNAMELEN    27 /* AFP2 spec */
+
+struct vol {
+    struct vol         *v_next;
+    char        *v_localname;   /* as defined in AppleVolumes.default */
+    ucs2_t             *v_u8mname;     /* converted to utf8-mac in ucs2 */
+    ucs2_t             *v_macname;     /* mangled to legacy longname in ucs2 */
+    ucs2_t             *v_name;        /* either v_u8mname or v_macname */
+    char               *v_path;
+    
+    struct dir         *v_dir, *v_root;
+    hash_t             *v_hash;
+    int                        v_flags;
+#ifdef __svr4__
+    int                        v_qfd;
+#endif /*__svr4__*/
+    char               *v_gvs;
+    time_t             v_mtime;
+    time_t             v_ctime;  /* volume creation date, not unix ctime */
+
+    u_int16_t          v_vid;
+    void                *v_nfsclient;
+    int                 v_nfs;
+    
+    int                 v_casefold;
+    size_t              max_filename;
+    
+    char                *v_password;
+    char                *v_veto;
+
+    char                *v_cnidscheme;
+    char                *v_dbpath;
+    dev_t               v_dev;              /* Unix volume device */
+    struct _cnid_db     *v_cdb;
+    char                v_stamp[ADEDLEN_PRIVSYN];
+    mode_t             v_umask;
+    mode_t             v_perm;             /* default permission value OR with requested perm*/
+    mode_t             v_dperm;             /* default directories permission value OR with requested perm*/
+    mode_t             v_fperm;             /* default files permission value OR with requested perm*/
+
+#ifdef FORCE_UIDGID
+    char               *v_forceuid;
+    char               *v_forcegid;
+#endif 
+
+    char                *v_volcodepage;
+    charset_t          v_volcharset;   
+    struct charset_functions   *v_vol;
+    char               *v_maccodepage;
+    charset_t          v_maccharset;
+    struct charset_functions   *v_mac;
+
+    int                 v_deleted;    /* volume open but deleted in new config file */
+    int                 v_hide;       /* new volume wait until old volume is closed */
+    int                 v_new;        /* volume deleted but there's a new one with the same name */
+    int                 v_adouble;    /* default adouble format */
+    int                 v_ad_options; /* adouble option NODEV, NOCACHE, etc.. */
+
+    char                *v_root_preexec;
+    char                *v_preexec;
+
+    char                *v_root_postexec;
+    char                *v_postexec;
+
+    int                 v_root_preexec_close;
+    int                 v_preexec_close;
+    
+    /* adouble indirection */
+    struct vfs_ops      *vfs;
+    int                 v_vfs_ea;       /* The AFPVOL_EA_xx flag */
+    int                 (*validupath)(const struct vol *, const char *);
+    char                *(*ad_path)(const char *, int);
+};
+
+#ifdef NO_LARGE_VOL_SUPPORT
+typedef u_int32_t VolSpace;
+#else /* NO_LARGE_VOL_SUPPORT */
+typedef u_int64_t VolSpace;
+#endif /* NO_LARGE_VOL_SUPPORT */
+
+#define AFPVOL_OPEN    (1<<0)
+
+/* flags  for AFS and quota 0xxx0 */
+#define AFPVOL_GVSMASK (7<<2)
+#define AFPVOL_NONE    (0<<2)
+#define AFPVOL_AFSGVS  (1<<2)
+#define AFPVOL_USTATFS (2<<2)
+#define AFPVOL_UQUOTA  (4<<2)
+
+/* 
+   Flags that alter volume behaviour.
+   Keep in sync with include/atalk/volinfo.h and libatalk/util/volinfo.c
+*/
+#define AFPVOL_A2VOL     (1 << 5)   /* prodos volume */
+#define AFPVOL_CRLF      (1 << 6)   /* cr/lf translation */
+#define AFPVOL_NOADOUBLE (1 << 7)   /* don't create .AppleDouble by default */
+#define AFPVOL_RO        (1 << 8)   /* read-only volume */
+#define AFPVOL_MSWINDOWS (1 << 9)   /* deal with ms-windows yuckiness.
+this is going away. */
+#define AFPVOL_NOHEX     (1 << 10)  /* don't do :hex translation */
+#define AFPVOL_USEDOTS   (1 << 11)  /* use real dots */
+#define AFPVOL_LIMITSIZE (1 << 12)  /* limit size for older macs */
+#define AFPVOL_MAPASCII  (1 << 13)  /* map the ascii range as well */
+#define AFPVOL_DROPBOX   (1 << 14)  /* dropkludge dropbox support */
+#define AFPVOL_NOFILEID  (1 << 15)  /* don't advertise createid resolveid and deleteid calls */
+#define AFPVOL_NOSTAT    (1 << 16)  /* advertise the volume even if we can't stat() it
+                                     * maybe because it will be mounted later in preexec */
+#define AFPVOL_UNIX_PRIV (1 << 17)  /* support unix privileges */
+#define AFPVOL_NODEV     (1 << 18)  /* always use 0 for device number in cnid calls 
+                                     * help if device number is notconsistent across reboot 
+                                     * NOTE symlink to a different device will return an ACCESS error
+                                     */
+#define AFPVOL_CASEINSEN (1 << 19)  /* volume is case insensitive */
+#define AFPVOL_EILSEQ    (1 << 20)  /* encode illegal sequence 'asis' UCS2, ex "\217-", which is not 
+                                       a valid SHIFT-JIS char, is encoded  as U\217 -*/
+
+#define AFPVOL_CACHE     (1 << 21)   /* Use adouble v2 CNID caching. Default: yes */
+#define AFPVOL_INV_DOTS  (1 << 22)   /* dots files are invisible */
+#define AFPVOL_TM        (1 << 24)   /* Supports TimeMachine */
+#define AFPVOL_ACLS      (1 << 25)   /* Volume supports ACLS */
+
+/* Extended Attributes vfs indirection  */
+#define AFPVOL_EA_AD             0   /* Store them in adouble files. Default. */
+#define AFPVOL_EA_SOLARIS        1   /* Store them in native Solaris EAs */
+
+/* FPGetSrvrParms options */
+#define AFPSRVR_CONFIGINFO     (1 << 0)
+#define AFPSRVR_PASSWD         (1 << 7)
+
+/* handle casefolding */
+#define AFPVOL_MTOUUPPER       (1 << 0) 
+#define AFPVOL_MTOULOWER       (1 << 1) 
+#define AFPVOL_UTOMUPPER       (1 << 2) 
+#define AFPVOL_UTOMLOWER       (1 << 3) 
+#define AFPVOL_UMLOWER         (AFPVOL_MTOULOWER | AFPVOL_UTOMLOWER)
+#define AFPVOL_UMUPPER         (AFPVOL_MTOUUPPER | AFPVOL_UTOMUPPER)
+#define AFPVOL_UUPPERMLOWER    (AFPVOL_MTOUUPPER | AFPVOL_UTOMLOWER)
+#define AFPVOL_ULOWERMUPPER    (AFPVOL_MTOULOWER | AFPVOL_UTOMUPPER)
+
+#define MSWINDOWS_BADCHARS ":\t\\/<>*?|\""
+
+int wincheck(const struct vol *vol, const char *path);
+
+#define AFPVOLSIG_FLAT          0x0001 /* flat fs */
+#define AFPVOLSIG_FIX          0x0002 /* fixed ids */
+#define AFPVOLSIG_VAR           0x0003 /* variable ids */
+#define AFPVOLSIG_DEFAULT       AFPVOLSIG_FIX
+
+/* volume attributes */
+#define VOLPBIT_ATTR_RO           (1 << 0)
+#define VOLPBIT_ATTR_PASSWD       (1 << 1)
+#define VOLPBIT_ATTR_FILEID       (1 << 2)
+#define VOLPBIT_ATTR_CATSEARCH    (1 << 3)
+#define VOLPBIT_ATTR_BLANKACCESS  (1 << 4)
+#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_TM           (1 << 13)
+
+#define VOLPBIT_ATTR   0
+#define VOLPBIT_SIG    1
+#define VOLPBIT_CDATE  2
+#define VOLPBIT_MDATE  3
+#define VOLPBIT_BDATE  4
+#define VOLPBIT_VID    5
+#define VOLPBIT_BFREE  6
+#define VOLPBIT_BTOTAL 7
+#define VOLPBIT_NAME   8
+/* handle > 4GB volumes */
+#define VOLPBIT_XBFREE  9
+#define VOLPBIT_XBTOTAL 10
+#define VOLPBIT_BSIZE   11        /* block size */
+
+
+#define vol_noadouble(vol) (((vol)->v_flags & AFPVOL_NOADOUBLE) ? \
+                           ADFLAGS_NOADOUBLE : 0)
+#ifdef AFP3x
+#define utf8_encoding() (afp_version >= 30)
+#else
+#define utf8_encoding() (0)
+#endif
+
+#define vol_nodev(vol) (((vol)->v_flags & AFPVOL_NODEV) ? 1 : 0)
+#define vol_unix_priv(vol) (afp_version >= 30 && ((vol)->v_flags & AFPVOL_UNIX_PRIV))
+#define vol_inv_dots(vol) (((vol)->v_flags & AFPVOL_INV_DOTS) ? 1 : 0)
+
+
+#endif
index de222108ea9d0feabcf42e8e065251d8810ec9e0..5fcf448399e1064f000a9860cfa09d8f59281bba 100644 (file)
@@ -1,7 +1,7 @@
 
 # Makefile.am for libatalk/
 
-SUBDIRS = adouble asp atp compat cnid dsi nbp netddp util tdb unicode acl
+SUBDIRS = acl adouble asp atp compat cnid dsi nbp netddp tdb util unicode vfs
 
 lib_LTLIBRARIES = libatalk.la
 
@@ -16,8 +16,9 @@ libatalk_la_LIBADD  = \
        nbp/libnbp.la           \
        netddp/libnetddp.la     \
        util/libutil.la         \
-        tdb/libtdb.la           \
-       unicode/libunicode.la @LIBATALK_ACLS@
+       tdb/libtdb.la       \
+       unicode/libunicode.la \
+       vfs/libvfs.la @LIBATALK_ACLS@
 
 libatalk_la_DEPENDENCIES = \
        adouble/libadouble.la   \
@@ -28,8 +29,9 @@ libatalk_la_DEPENDENCIES = \
        nbp/libnbp.la           \
        netddp/libnetddp.la     \
        util/libutil.la         \
-        tdb/libtdb.la           \
-       unicode/libunicode.la @LIBATALK_ACLS@
+       tdb/libtdb.la       \
+       unicode/libunicode.la \
+       vfs/libvfs.la @LIBATALK_ACLS@
 
 libatalk_la_LDFLAGS = -static
 
index 299ab3441a7eddbe46ae47c9d3d21ab9cc815c07..8a242c2a87679e775aa3773c3d335f02ad6d49f6 100644 (file)
@@ -2,6 +2,7 @@
 
 noinst_LTLIBRARIES = libadouble.la
 
-libadouble_la_SOURCES = ad_open.c ad_flush.c ad_read.c ad_write.c ad_size.c ad_mmap.c ad_lock.c ad_date.c ad_attr.c ad_sendfile.c
+libadouble_la_SOURCES = ad_open.c ad_flush.c ad_read.c ad_write.c ad_size.c \
+       ad_mmap.c ad_lock.c ad_date.c ad_attr.c ad_sendfile.c
 
 noinst_HEADERS = ad_private.h
index 4f62e95241836ca81e41bb813132f2ed545ba003..6d9bf8f63d8b8e9948b6363df0a54bea40b8d9b1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: ad_open.c,v 1.47 2009-09-14 00:02:21 didg Exp $
+ * $Id: ad_open.c,v 1.48 2009-10-02 09:32:41 franklahm Exp $
  *
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
  * Copyright (c) 1990,1991 Regents of The University of Michigan.
@@ -219,6 +219,8 @@ static int ad_update(struct adouble *ad, const char *path)
     if (!path || ad->ad_flags != AD_VERSION2)
         return 0;
 
+    LOG(log_maxdebug, logtype_default, "ad_update: checking whether '%s' needs an upgrade.", path);
+
     if (!(ad->ad_md->adf_flags & O_RDWR)) {
         /* we were unable to open the file read write the last time */
         return 0;
index f64d133d7a99cd17961476e250723e19aa782074..2ae09252bbf26df1b529ae1845fbc3a35ed2eb81 100644 (file)
@@ -19,6 +19,7 @@ libutil_la_SOURCES = \
        strdicasecmp.c  \
        strlcpy.c       \
        fault.c         \
-       volinfo.c
+       volinfo.c \
+       locking.c
 
 #      util_unicode.c
diff --git a/libatalk/util/locking.c b/libatalk/util/locking.c
new file mode 100644 (file)
index 0000000..f42edac
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+   $Id: locking.c,v 1.1 2009-10-02 09:32:41 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.
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+
+/*
+ * Function: lock_reg
+ *
+ * Purpose: lock a file with fctnl
+ *
+ * Arguments:
+ *
+ * fd         (r) File descriptor
+ * cmd        (r) cmd to fcntl, only F_SETLK is usable here
+ * type       (r) F_RDLCK, F_WRLCK, F_UNLCK
+ * offset     (r) byte offset relative to l_whence
+ * whence     (r) SEEK_SET, SEEK_CUR, SEEK_END
+ * len        (r) no. of bytes (0 means to EOF)
+ *
+ * Returns: fcntl return value
+ *
+ * Effects:
+ *
+ * Function called by macros {read|write|un]_lock to ease locking.
+ */
+int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)
+{
+    struct flock lock;
+
+    lock.l_type = type;
+    lock.l_start = offset;
+    lock.l_whence = whence;
+    lock.l_len = len;
+
+    return (fcntl(fd, cmd, &lock));
+}
index dbb44e106173ddfe5ced90e40d66e034cc231368..167c93c9d56a72032261b67e1e1a394fe2dac40b 100644 (file)
@@ -96,7 +96,6 @@ static const _vol_opt_name vol_opt_names[] = {
     {AFPVOL_EILSEQ,     "ILLEGALSEQ"},  /* encode illegal sequence */
     {AFPVOL_CACHE,      "CACHEID"},     /* Use adouble v2 CNID caching, default don't use it */
     {AFPVOL_INV_DOTS,   "INVISIBLEDOTS"}, 
-    {AFPVOL_EXT_ATTRS,  "EXT_ATTRS"},   /* Vol supports Extened Attributes */
     {AFPVOL_ACLS,       "ACLS"},        /* Vol supports ACLs */
     {AFPVOL_TM,         "TM"},          /* Set "kSupportsTMLockSteal" is volume attributes */
     {0, NULL}
diff --git a/libatalk/vfs/.cvsignore b/libatalk/vfs/.cvsignore
new file mode 100644 (file)
index 0000000..70177ec
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.in
+*.lo
+*.la
+*.loT
+.deps
+.libs
diff --git a/libatalk/vfs/Makefile.am b/libatalk/vfs/Makefile.am
new file mode 100644 (file)
index 0000000..f208caf
--- /dev/null
@@ -0,0 +1,10 @@
+# Makefile.am for libatalk/adouble/
+
+noinst_LTLIBRARIES = libvfs.la
+
+libvfs_la_SOURCES = vfs.c unix.c ea.c
+
+if USE_NFSv4_ACLS
+libvfs_la_SOURCES += acl.c
+endif
+
diff --git a/libatalk/vfs/acl.c b/libatalk/vfs/acl.c
new file mode 100644 (file)
index 0000000..d851f70
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+  $Id: acl.c,v 1.1 2009-10-02 09:32:41 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <errno.h>
+#include <sys/acl.h>
+
+#include <atalk/util.h>
+#include <atalk/logger.h>
+
+/* Get ACL. Allocates storage as needed. Caller must free.
+ * Returns no of ACEs or -1 on error.  */
+int get_nfsv4_acl(const char *name, ace_t **retAces)
+{
+    int ace_count = -1;
+    ace_t *aces;
+
+    *retAces = NULL;
+    ace_count = acl(name, ACE_GETACLCNT, 0, NULL);
+    if (ace_count <= 0) {
+       LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACLCNT) error");
+        return -1;
+    }
+
+    aces = malloc(ace_count * sizeof(ace_t));
+    if (aces == NULL) {
+       LOG(log_error, logtype_afpd, "get_nfsv4_acl: malloc error");
+       return -1;
+    }
+
+    if ( (acl(name, ACE_GETACL, ace_count, aces)) == -1 ) {
+       LOG(log_error, logtype_afpd, "get_nfsv4_acl: acl(ACE_GETACL) error");
+       free(aces);
+       return -1;
+    }
+
+    LOG(log_debug9, logtype_afpd, "get_nfsv4_acl: file: %s -> No. of ACEs: %d", name, ace_count);
+    *retAces = aces;
+
+    return ace_count;
+}
+
+/* Removes all non-trivial ACLs from object. Returns full AFPERR code. */
+int remove_acl(const char *name)
+{
+    int ret,i, ace_count, trivial_aces, new_aces_count;
+    ace_t *old_aces = NULL;
+    ace_t *new_aces = NULL;
+
+    LOG(log_debug9, logtype_afpd, "remove_acl: BEGIN");
+
+    /* Get existing ACL and count trivial ACEs */
+    if ((ace_count = get_nfsv4_acl(name, &old_aces)) == -1)
+        return AFPERR_MISC;
+    trivial_aces = 0;
+    for ( i=0; i < ace_count; i++) {
+        if (old_aces[i].a_flags & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE))
+            trivial_aces++;
+    }
+
+    /* malloc buffer for new ACL */
+    if ((new_aces = malloc(trivial_aces * sizeof(ace_t))) == NULL) {
+        LOG(log_error, logtype_afpd, "remove_acl: malloc %s", strerror(errno));
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+
+    /* Now copy the trivial ACEs */
+    new_aces_count = 0;
+    for (i=0; i < ace_count; i++) {
+        if (old_aces[i].a_flags  & (ACE_OWNER | ACE_GROUP | ACE_EVERYONE)) {
+            memcpy(&new_aces[new_aces_count], &old_aces[i], sizeof(ace_t));
+            new_aces_count++;
+        }
+    }
+
+    if ( (acl(name, ACE_SETACL, trivial_aces, new_aces)) == 0)
+        ret = AFP_OK;
+    else {
+        LOG(log_error, logtype_afpd, "set_acl: error setting acl: %s", strerror(errno));
+        if (errno == (EACCES | EPERM))
+            ret = AFPERR_ACCESS;
+        else if (errno == ENOENT)
+            ret = AFPERR_NOITEM;
+        else
+            ret = AFPERR_MISC;
+    }
+
+exit:
+    free(old_aces);
+    free(new_aces);
+
+    LOG(log_debug9, logtype_afpd, "remove_acl: END");
+    return ret;
+}
diff --git a/libatalk/vfs/ea.c b/libatalk/vfs/ea.c
new file mode 100644 (file)
index 0000000..e776ec6
--- /dev/null
@@ -0,0 +1,1509 @@
+/*
+  $Id: ea.c,v 1.1 2009-10-02 09:32:41 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 */
+#ifdef HAVE_SOLARIS_EAS
+#define _ATFILE_SOURCE
+#endif
+
+#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>
+
+/*
+ * 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"
+ */
+
+/*
+ * 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, 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)
+{
+    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_path
+ *
+ * Purpose: return name of ea header filename
+ *
+ * Arguments:
+ *
+ *    ea           (r) ea handle
+ *    eaname       (r) name of EA or NULL
+ *
+ * Returns: pointer to name in static buffer
+ *
+ * Effects:
+ *
+ * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
+ * Files: "file" -> "file/.AppleDouble/file::EA"
+ * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
+ * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
+ */
+static char * ea_path(const struct ea * restrict ea,
+                      const char * restrict eaname)
+{
+    char *adname;
+    static char pathbuf[MAXPATHLEN + 1];
+
+    /* get name of a adouble file from uname */
+    adname = ea->vol->vfs->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
+    /* copy it so we can work with it */
+    strlcpy(pathbuf, adname, MAXPATHLEN + 1);
+    /* append "::EA" */
+    strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
+
+    if (eaname) {
+        strlcat(pathbuf, "::", MAXPATHLEN + 1);
+        strlcat(pathbuf, eaname, MAXPATHLEN + 1);
+    }
+
+    return pathbuf;
+}
+
+/*
+ * Function: ea_addentry
+ *
+ * Purpose: add one EA into ea->ea_entries[]
+ *
+ * Arguments:
+ *
+ *    ea            (rw) pointer to struct ea
+ *    uname         (r) name of file
+ *    attruname     (r) name of EA
+ *    attrsize      (r) size of ea
+ *
+ * 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 uname,
+                       const char * restrict attruname,
+                       size_t attrsize)
+{
+    void *tmprealloc;
+
+    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 {
+        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: ea_delentry
+ *
+ * Purpose: delete one EA from ea->ea_entries[]
+ *
+ * Arguments:
+ *
+ *    ea            (rw) pointer to struct ea
+ *    uname         (r) name of EA
+ *    attruname     (r) size of ea
+ *
+ * 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 uname,
+                       const char * restrict attruname)
+{
+    int ret = 0, count = 0;
+
+    if (ea->ea_count == 0) {
+        return -1;
+    }
+
+    while (count < ea->ea_count) {
+        /* search matching EA */
+        if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
+            free((*ea->ea_entries)[count].ea_name);
+            (*ea->ea_entries)[count].ea_name = NULL;
+
+            LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
+                attruname, count + 1, ea->ea_count);
+
+            break;
+        }
+        count++;
+    }
+
+    return ret;
+}
+
+/*
+ * Function: create_ea_header
+ *
+ * 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;
+
+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;
+
+    eaname = ea_path(ea, attruname);
+    LOG(log_maxdebug, logtype_afpd, "write_ea: ea_apth: %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)) != attrsize) {
+        LOG(log_error, logtype_afpd, "write_ea: short write: %s", eaname);
+        ret = -1;
+        goto exit;
+    }
+
+exit:
+    if (fd != -1)
+        close(fd); /* and unlock */
+    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;
+
+    eafile = ea_path(ea, eaname);
+
+    /* 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;
+}
+
+/*
+ * 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 error
+ *
+ * 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.
+ */
+static 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;
+    }
+
+    if ((stat(uname, &st)) != 0) {
+        LOG(log_debug, logtype_afpd, "ea_open: cant stat: %s", 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;
+    if (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);
+    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 = -1;
+                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 */
+    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: error on open for header: %s", eaname);
+        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)) != 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:
+    if (ret != 0) {
+        if (ea->ea_data) {
+            free(ea->ea_data);
+            ea->ea_data = NULL;
+        }
+        if (ea->ea_fd) {
+            close(ea->ea_fd);
+            ea->ea_fd = -1;
+        }
+    }
+    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
+ */
+static int ea_close(struct ea * restrict ea)
+{
+    int ret = 0, count = 0;
+    char *eaname;
+    struct stat st;
+
+    LOG(log_debug, logtype_afpd, "ea_close('%s')", ea->filename);
+
+    /* 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);
+                if ((stat(eaname, &st)) == 0) {
+                    if ((unlink(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) != 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(const struct vol * restrict vol,
+               char * restrict rbuf,
+               int * restrict rbuflen,
+               const char * restrict uname,
+               int oflag,
+               const char * restrict attruname)
+{
+    int ret = AFPERR_MISC, count = 0;
+    uint32_t uint32;
+    struct ea ea;
+
+    LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
+
+    if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
+        LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
+        return AFPERR_MISC;
+    }
+
+    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(const struct vol * restrict vol,
+                  char * restrict rbuf,
+                  int * restrict rbuflen,
+                  const char * restrict uname,
+                  int oflag,
+                  const char * restrict attruname,
+                  int maxreply)
+{
+    int ret = AFPERR_MISC, count = 0, fd = -1;
+    uint32_t uint32;
+    size_t toread;
+    struct ea ea;
+
+    LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
+
+    if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
+        LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
+        return AFPERR_MISC;
+    }
+
+    while (count < ea.ea_count) {
+        if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
+            if ((fd = open(ea_path(&ea, attruname), O_RDONLY)) == -1) {
+                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)) != toread) {
+                LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
+                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(const struct vol * restrict vol,
+             char * restrict attrnamebuf,
+             int * restrict buflen,
+             const char * restrict uname,
+             int oflag)
+{
+    int count = 0, 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(const struct vol * restrict vol,
+           const char * restrict uname,
+           const char * restrict attruname,
+           const char * restrict ibuf,
+           size_t attrsize,
+           int oflag)
+{
+    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, uname, attruname, attrsize)) == -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(const struct vol * restrict vol,
+              const char * restrict uname,
+              const char * restrict attruname,
+              int oflag)
+{
+    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, uname, 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;
+}
+
+/**********************************************************************************
+ * Solaris EA VFS funcs
+ **********************************************************************************/
+
+/*
+ * Function: sol_get_easize
+ *
+ * Purpose: get size of an EA on Solaris native EA
+ *
+ * Arguments:
+ *
+ *    vol          (r) current volume
+ *    rbuf         (w) DSI reply buffer
+ *    rbuflen      (rw) current length of data in reply buffer
+ *    uname        (r) filename
+ *    oflag        (r) link and create flag
+ *    attruname    (r) name of attribute
+ *
+ * Returns: AFP code: AFP_OK on success or appropiate AFP error code
+ *
+ * Effects:
+ *
+ * Copies EA size into rbuf in network order. Increments *rbuflen +4.
+ */
+#ifdef HAVE_SOLARIS_EAS
+int sol_get_easize(const struct vol * restrict vol,
+                   char * restrict rbuf,
+                   int * restrict rbuflen,
+                   const char * restrict uname,
+                   int oflag,
+                   cons char * restrict attruname)
+{
+    int                 ret, attrdirfd;
+    uint32_t            attrsize;
+    struct stat         st;
+
+    LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
+
+    if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
+        if (errno == ELOOP) {
+            /* its a symlink and client requested O_NOFOLLOW  */
+            LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
+
+            memset(rbuf, 0, 4);
+            *rbuflen += 4;
+
+            return AFP_OK;
+        }
+        LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno));
+        return AFPERR_MISC;
+    }
+
+    if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
+        LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno));
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+    attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
+
+    /* Start building reply packet */
+
+    LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
+
+    /* length of attribute data */
+    attrsize = htonl(attrsize);
+    memcpy(rbuf, &attrsize, 4);
+    *rbuflen += 4;
+
+    ret = AFP_OK;
+
+exit:
+    close(attrdirfd);
+    return ret;
+}
+#endif /* HAVE_SOLARIS_EAS */
+
+/*
+ * Function: sol_get_eacontent
+ *
+ * Purpose: copy Solaris native EA into rbuf
+ *
+ * Arguments:
+ *
+ *    vol          (r) current volume
+ *    rbuf         (w) DSI reply buffer
+ *    rbuflen      (rw) current length of data in reply buffer
+ *    uname        (r) filename
+ *    oflag        (r) link and create flag
+ *    attruname    (r) name of attribute
+ *    maxreply     (r) maximum EA size as of current specs/real-life
+ *
+ * Returns: AFP code: AFP_OK on success or appropiate AFP error code
+ *
+ * Effects:
+ *
+ * Copies EA into rbuf. Increments *rbuflen accordingly.
+ */
+#ifdef HAVE_SOLARIS_EAS
+int sol_get_eacontent(const struct vol * restrict vol,
+                      char * restrict rbuf,
+                      int * restrict rbuflen,
+                      const char * restrict uname,
+                      int oflag,
+                      char * restrict attruname,
+                      int maxreply)
+{
+    int                 ret, attrdirfd;
+    size_t              toread, okread = 0, len;
+    char                *datalength;
+    struct stat         st;
+
+    if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
+        if (errno == ELOOP) {
+            /* its a symlink and client requested O_NOFOLLOW  */
+            LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
+
+            memset(rbuf, 0, 4);
+            *rbuflen += 4;
+
+            return AFP_OK;
+        }
+        LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
+        return AFPERR_MISC;
+    }
+
+    if ( -1 == (fstat(attrdirfd, &st))) {
+        LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+
+    /* Start building reply packet */
+
+    maxreply -= MAX_REPLY_EXTRA_BYTES;
+    if (maxreply > MAX_EA_SIZE)
+        maxreply = MAX_EA_SIZE;
+
+    /* But never send more than the client requested */
+    toread = (maxreply < st.st_size) ? maxreply : st.st_size;
+
+    LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
+
+    /* remember where we must store length of attribute data in rbuf */
+    datalength = rbuf;
+    rbuf += 4;
+    *rbuflen += 4;
+
+    while (1) {
+        len = read(attrdirfd, rbuf, toread);
+        if (len == -1) {
+            LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno));
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+        okread += len;
+        rbuf += len;
+        *rbuflen += len;
+        if ((len == 0) || (okread == toread))
+            break;
+    }
+
+    okread = htonl((uint32_t)okread);
+    memcpy(datalength, &okread, 4);
+
+    ret = AFP_OK;
+
+exit:
+    close(attrdirfd);
+    return ret;
+}
+#endif /* HAVE_SOLARIS_EAS */
+
+/*
+ * Function: sol_list_eas
+ *
+ * Purpose: copy names of Solaris native EA into attrnamebuf
+ *
+ * Arguments:
+ *
+ *    vol          (r) current volume
+ *    attrnamebuf  (w) store names a consecutive C strings here
+ *    buflen       (rw) length of names in attrnamebuf
+ *    uname        (r) filename
+ *    oflag        (r) link and create flag
+ *
+ * Returns: AFP code: AFP_OK on success or appropiate AFP error code
+ *
+ * Effects:
+ *
+ * Copies names of all EAs of uname as consecutive C strings into rbuf.
+ * Increments *rbuflen accordingly.
+ */
+#ifdef HAVE_SOLARIS_EAS
+int sol_list_eas(const struct vol * restrict vol,
+                 char * restrict attrnamebuf,
+                 int * restrict buflen,
+                 const char * restrict uname,
+                 int oflag)
+{
+    int ret, attrbuflen = *buflen, len, attrdirfd = 0;
+    struct dirent *dp;
+    DIR *dirp = NULL;
+
+    /* Now list file attribute dir */
+    if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
+        if (errno == ELOOP) {
+            /* its a symlink and client requested O_NOFOLLOW */
+            ret = AFPERR_BADTYPE;
+            goto exit;
+        }
+        LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+
+    if (NULL == (dirp = fdopendir(attrdirfd))) {
+        LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
+        ret = AFPERR_MISC;
+        goto exit;
+    }
+
+    while ((dp = readdir(dirp)))  {
+        /* check if its "." or ".." */
+        if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
+            (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
+            continue;
+
+        len = strlen(dp->d_name);
+
+        /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
+        if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+        if (len == 255)
+            /* convert_string didn't 0-terminate */
+            attrnamebuf[attrbuflen + 255] = 0;
+
+        LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_name);
+
+        attrbuflen += len + 1;
+        if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
+            /* Next EA name could overflow, so bail out with error.
+               FIXME: evantually malloc/memcpy/realloc whatever.
+               Is it worth it ? */
+            LOG(log_warning, logtype_afpd, "sol_list_extattr(%s): running out of buffer for EA names", uname);
+            ret = AFPERR_MISC;
+            goto exit;
+        }
+    }
+
+    ret = AFP_OK;
+
+exit:
+    if (dirp)
+        closedir(dirp);
+
+    if (attrdirfd > 0)
+        close(attrdirfd);
+
+    *buflen = attrbuflen;
+    return ret;
+}
+#endif /* HAVE_SOLARIS_EAS */
+
+/*
+ * Function: sol_set_ea
+ *
+ * Purpose: set a Solaris native EA
+ *
+ * Arguments:
+ *
+ *    vol          (r) current volume
+ *    uname        (r) filename
+ *    attruname    (r) EA name
+ *    ibuf         (r) buffer with EA content
+ *    attrsize     (r) length EA in ibuf
+ *    oflag        (r) link and create flag
+ *
+ * Returns: AFP code: AFP_OK on success or appropiate AFP error code
+ *
+ * Effects:
+ *
+ * Copies names of all EAs of uname as consecutive C strings into rbuf.
+ * Increments *rbuflen accordingly.
+ */
+#ifdef HAVE_SOLARIS_EAS
+int sol_set_ea(const struct vol * restrict vol,
+               const char * restrict u_name,
+               const char * restrict attruname,
+               const char * restrict ibuf,
+               size_t attrsize,
+               int oflag)
+{
+    int attrdirfd;
+
+    if ( -1 == (attrdirfd = attropen(u_name, attruname, oflag, 0666))) {
+        if (errno == ELOOP) {
+            /* its a symlink and client requested O_NOFOLLOW  */
+            LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
+            return AFP_OK;
+        }
+        LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
+        return AFPERR_MISC;
+    }
+
+    if ((write(attrdirfd, ibuf, attrsize)) != attrsize) {
+        LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
+        return AFPERR_MISC;
+    }
+
+    return AFP_OK;
+}
+#endif /* HAVE_SOLARIS_EAS */
+
+/*
+ * Function: sol_remove_ea
+ *
+ * Purpose: remove a Solaris native EA
+ *
+ * Arguments:
+ *
+ *    vol          (r) current volume
+ *    uname        (r) filename
+ *    attruname    (r) EA name
+ *    oflag        (r) link and create flag
+ *
+ * Returns: AFP code: AFP_OK on success or appropiate AFP error code
+ *
+ * Effects:
+ *
+ * Removes EA attruname from file uname.
+ */
+#ifdef HAVE_SOLARIS_EAS
+int sol_remove_ea(const struct vol * restrict vol,
+                  const char * restrict uname,
+                  const char * restrict attruname,
+                  int oflag)
+{
+    int attrdirfd;
+
+    if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) {
+        switch (errno) {
+        case ELOOP:
+            /* its a symlink and client requested O_NOFOLLOW  */
+            LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", s_path->u_name);
+            return AFP_OK;
+        case EACCES:
+            LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", s_path->u_name, strerror(errno));
+            return AFPERR_ACCESS;
+        default:
+            LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", s_path->u_name, strerror(errno));
+            return AFPERR_MISC;
+        }
+    }
+
+    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;
+    }
+
+}
+#endif /* HAVE_SOLARIS_EAS */
diff --git a/libatalk/vfs/unix.c b/libatalk/vfs/unix.c
new file mode 100644 (file)
index 0000000..056bdf0
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * $Id: unix.c,v 1.1 2009-10-02 09:32:41 franklahm Exp $
+ *
+ * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * All Rights Reserved.  See COPYRIGHT.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include <atalk/afp.h>
+#include <atalk/util.h>
+#include <atalk/directory.h>
+#include <atalk/volume.h>
+#include <atalk/logger.h>
+
+/* -----------------------------
+   a dropbox is a folder where w is set but not r eg:
+   rwx-wx-wx or rwx-wx-- 
+   rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
+*/
+int stickydirmode(const char *name, const mode_t mode, const int dropbox, const mode_t v_umask)
+{
+    int retval = 0;
+
+#ifdef DROPKLUDGE
+    /* Turn on the sticky bit if this is a drop box, also turn off the setgid bit */
+    if ((dropbox & AFPVOL_DROPBOX)) {
+        int uid;
+
+        if ( ( (mode & S_IWOTH) && !(mode & S_IROTH)) ||
+             ( (mode & S_IWGRP) && !(mode & S_IRGRP)) )
+        {
+            uid=geteuid();
+            if ( seteuid(0) < 0) {
+                LOG(log_error, logtype_afpd, "stickydirmode: unable to seteuid root: %s", strerror(errno));
+            }
+            if ( (retval=chmod( name, ( (DIRBITS | mode | S_ISVTX) & ~v_umask) )) < 0) {
+                LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
+            } else {
+                LOG(log_debug, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(retval) );
+            }
+            seteuid(uid);
+            return retval;
+        }
+    }
+#endif /* DROPKLUDGE */
+
+    /*
+     *  Ignore EPERM errors:  We may be dealing with a directory that is
+     *  group writable, in which case chmod will fail.
+     */
+    if ( (chmod( name, (DIRBITS | mode) & ~v_umask ) < 0) && errno != EPERM && 
+               !(errno == ENOENT && (dropbox & AFPVOL_NOADOUBLE)) )  
+    {
+        LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) );
+        retval = -1;
+    }
+
+    return retval;
+}
+
+/* ------------------------- */
+int dir_rx_set(mode_t mode)
+{
+    return (mode & (S_IXUSR | S_IRUSR)) == (S_IXUSR | S_IRUSR);
+}
+
+/* --------------------- */
+int setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+{
+struct stat sb;
+mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;  /* rwx for owner group and other, by default */
+
+    if (!st) {
+        if (stat(name, &sb) != 0)
+            return -1;
+        st = &sb;
+    }
+   
+   mode |= st->st_mode & ~mask; /* keep other bits from previous mode */
+   if ( chmod( name,  mode & ~v_umask ) < 0 && errno != EPERM ) {
+       return -1;
+   }
+   return 0;
+}
+
+/* -------------------
+   system rmdir with afp error code.
+   ENOENT is not an error.
+ */
+int netatalk_rmdir(const char *name)
+{
+    if (rmdir(name) < 0) {
+        switch ( errno ) {
+        case ENOENT :
+            break;
+        case ENOTEMPTY : 
+            return AFPERR_DIRNEMPT;
+        case EPERM:
+        case EACCES :
+            return AFPERR_ACCESS;
+        case EROFS:
+            return AFPERR_VLOCK;
+        default :
+            return AFPERR_PARAM;
+        }
+    }
+    return AFP_OK;
+}
+
+/* -------------------
+   system unlink with afp error code.
+   ENOENT is not an error.
+ */
+int netatalk_unlink(const char *name)
+{
+    if (unlink(name) < 0) {
+        switch (errno) {
+        case ENOENT :
+            break;
+        case EROFS:
+            return AFPERR_VLOCK;
+        case EPERM:
+        case EACCES :
+            return AFPERR_ACCESS;
+        default :
+            return AFPERR_PARAM;
+        }
+    }
+    return AFP_OK;
+}
+
+char *fullpathname(const char *name)
+{
+    static char wd[ MAXPATHLEN + 1];
+
+    if ( getcwd( wd , MAXPATHLEN) ) {
+        strlcat(wd, "/", MAXPATHLEN);
+        strlcat(wd, name, MAXPATHLEN);
+    }
+    else {
+        strlcpy(wd, name, MAXPATHLEN);
+    }
+    return wd;
+}
diff --git a/libatalk/vfs/vfs.c b/libatalk/vfs/vfs.c
new file mode 100644 (file)
index 0000000..b9b4f50
--- /dev/null
@@ -0,0 +1,862 @@
+/*
+    Copyright (c) 2004 Didier Gautheron
+   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.
+*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <atalk/afp.h>    
+#include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/logger.h>
+#include <atalk/util.h>
+#include <atalk/volume.h>
+#include <atalk/directory.h>
+
+#if 0
+#include "extattrs.h"
+#endif
+
+#ifdef HAVE_NFSv4_ACLS
+extern int remove_acl(const char *name);
+#endif
+
+struct perm {
+    uid_t uid;
+    gid_t gid;
+};
+
+typedef int (*rf_loop)(struct dirent *, char *, void *, int , mode_t );
+
+/* ----------------------------- */
+static int 
+for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag, mode_t v_umask)
+{
+    char            buf[ MAXPATHLEN + 1];
+    char            *m;
+    DIR             *dp;
+    struct dirent   *de;
+    int             ret;
+    
+
+    if (NULL == ( dp = opendir( name)) ) {
+        if (!flag) {
+            LOG(log_error, logtype_afpd, "%s: opendir %s: %s", from, fullpathname(name),strerror(errno) );
+            return -1;
+        }
+        return 0;
+    }
+    strlcpy( buf, name, sizeof(buf));
+    strlcat( buf, "/", sizeof(buf) );
+    m = strchr( buf, '\0' );
+    ret = 0;
+    while ((de = readdir(dp))) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+                continue;
+        }
+        
+        strlcat(buf, de->d_name, sizeof(buf));
+        if (fn && (ret = fn(de, buf, data, flag, v_umask))) {
+           closedir(dp);
+           return ret;
+        }
+        *m = 0;
+    }
+    closedir(dp);
+    return ret;
+}
+
+/*******************************************************************************
+ * classic adouble format 
+ *******************************************************************************/
+
+static int netatalk_name(const char *name)
+{
+    return strcasecmp(name,".AppleDB") &&
+        strcasecmp(name,".AppleDouble") &&
+        strcasecmp(name,".AppleDesktop");
+}
+
+static int validupath_adouble(const struct vol *vol, const char *name) 
+{
+    return (vol->v_flags & AFPVOL_USEDOTS) ? 
+        netatalk_name(name) && strcasecmp(name,".Parent"): name[0] != '.';
+}                                           
+
+/* ----------------- */
+static int RF_chown_adouble(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
+
+{
+    struct stat st;
+    char        *ad_p;
+
+    ad_p = vol->vfs->ad_path(path, ADFLAGS_HF );
+
+    if ( stat( ad_p, &st ) < 0 )
+        return 0; /* ignore */
+
+    return chown( ad_p, uid, gid );
+}
+
+/* ----------------- */
+int RF_renamedir_adouble(const struct vol *vol _U_, const char *oldpath _U_, const char *newpath _U_)
+{
+    return 0;
+}
+
+/* ----------------- */
+static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask)
+{
+    struct stat st;
+    int         err;
+    
+    /* bail if the file exists in the current directory.
+     * note: this will not fail with dangling symlinks */
+    
+    if (stat(de->d_name, &st) == 0)
+        return AFPERR_DIRNEMPT;
+
+    if ((err = netatalk_unlink(name)))
+        return err;
+
+    return 0;
+}
+
+static int RF_deletecurdir_adouble(const struct vol *vol)
+{
+    int err;
+
+    /* delete stray .AppleDouble files. this happens to get .Parent files
+       as well. */
+    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1, vol->v_umask))) 
+        return err;
+    return netatalk_rmdir( ".AppleDouble" );
+}
+
+/* ----------------- */
+static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+{
+    return setfilmode(name, ad_hf_mode(mode), st, v_umask);
+}
+
+static int RF_setfilmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_HF ), mode, st, vol->v_umask);
+}
+
+/* ----------------- */
+static int RF_setdirunixmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
+    int  dropbox = vol->v_flags;
+
+    if (dir_rx_set(mode)) {
+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0 ) 
+            return -1;
+    }
+
+    if (adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st, vol->v_umask) < 0) 
+        return -1;
+
+    if (!dir_rx_set(mode)) {
+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0 ) 
+            return  -1 ;
+    }
+    return 0;
+}
+
+/* ----------------- */
+static int setdirmode_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask)
+{
+    mode_t hf_mode = *(mode_t *)data;
+    struct stat st;
+
+    if ( stat( name, &st ) < 0 ) {
+        if (flag)
+            return 0;
+        LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) );
+    }
+    else if (!S_ISDIR(st.st_mode)) {
+        if (setfilmode(name, hf_mode , &st, v_umask) < 0) {
+               /* FIXME what do we do then? */
+        }
+    }
+    return 0;
+}
+
+static int RF_setdirmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st _U_)
+{
+    int   dropbox = vol->v_flags;
+    mode_t hf_mode = ad_hf_mode(mode);
+    char  *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
+    char  *adouble_p = ad_dir(adouble);
+
+    if (dir_rx_set(mode)) {
+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0) 
+            return -1;
+    }
+
+    if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, vol_noadouble(vol), vol->v_umask))
+        return -1;
+
+    if (!dir_rx_set(mode)) {
+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0) 
+            return  -1 ;
+    }
+    return 0;
+}
+
+/* ----------------- */
+static int setdirowner_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_)
+{
+    struct perm   *owner  = data;
+
+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
+         /* return ( -1 ); Sometimes this is okay */
+    }
+    return 0;
+}
+
+static int RF_setdirowner_adouble(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
+
+{
+    int           noadouble = vol_noadouble(vol);
+    char          *adouble_p;
+    struct stat   st;
+    struct perm   owner;
+    
+    owner.uid = uid;
+    owner.gid = gid;
+
+    adouble_p = ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR ));
+
+    if (for_each_adouble("setdirowner", adouble_p, setdirowner_adouble_loop, &owner, noadouble, vol->v_umask)) 
+        return -1;
+
+    /*
+     * We cheat: we know that chown doesn't do anything.
+     */
+    if ( stat( ".AppleDouble", &st ) < 0) {
+        if (errno == ENOENT && noadouble)
+            return 0;
+        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
+        return -1;
+    }
+    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
+        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
+            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
+        /* return ( -1 ); Sometimes this is okay */
+    }
+    return 0;
+}
+
+/* ----------------- */
+static int RF_deletefile_adouble(const struct vol *vol, const char *file )
+{
+       return netatalk_unlink(vol->vfs->ad_path( file, ADFLAGS_HF));
+}
+
+/* ----------------- */
+int RF_renamefile_adouble(const struct vol *vol, const char *src, const char *dst)
+{
+    char  adsrc[ MAXPATHLEN + 1];
+    int   err = 0;
+
+    strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
+    if (unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) < 0) {
+        struct stat st;
+
+        err = errno;
+        if (errno == ENOENT) {
+               struct adouble    ad;
+
+            if (stat(adsrc, &st)) /* source has no ressource fork, */
+                return 0;
+            
+            /* We are here  because :
+             * -there's no dest folder. 
+             * -there's no .AppleDouble in the dest folder.
+             * if we use the struct adouble passed in parameter it will not
+             * create .AppleDouble if the file is already opened, so we
+             * use a diff one, it's not a pb,ie it's not the same file, yet.
+             */
+            ad_init(&ad, vol->v_adouble, vol->v_ad_options); 
+            if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
+               ad_close(&ad, ADFLAGS_HF);
+               if (!unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) ) 
+                   err = 0;
+                else 
+                   err = errno;
+            }
+            else { /* it's something else, bail out */
+                   err = errno;
+               }
+           }
+       }
+       if (err) {
+               errno = err;
+               return -1;
+       }
+       return 0;
+}
+
+#ifdef HAVE_NFSv4_ACLS
+static int RF_acl(const struct vol *vol, const char *path, int cmd, int count, ace_t *aces)
+{
+    static char buf[ MAXPATHLEN + 1];
+    struct stat st;
+
+    if ((stat(path, &st)) != 0)
+       return -1;
+    if (S_ISDIR(st.st_mode)) {
+       if ((snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path)) < 0)
+           return -1;
+       /* set acl on .AppleDouble dir first */
+       if ((acl(buf, cmd, count, aces)) != 0)
+           return -1;
+       /* now set ACL on ressource fork */
+       if ((acl(vol->vfs->ad_path(path, ADFLAGS_DIR), cmd, count, aces)) != 0)
+           return -1;
+    } else
+       /* set ACL on ressource fork */
+       if ((acl(vol->vfs->ad_path(path, ADFLAGS_HF), cmd, count, aces)) != 0)
+           return -1;
+
+    return 0;
+}
+
+static int RF_remove_acl(const struct vol *vol, const char *path, int dir)
+{
+    int ret;
+    static char buf[ MAXPATHLEN + 1];
+
+    if (dir) {
+       if ((snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path)) < 0)
+           return AFPERR_MISC;
+       /* remove ACL from .AppleDouble/.Parent first */
+       if ((ret = remove_acl(vol->vfs->ad_path(path, ADFLAGS_DIR))) != AFP_OK)
+           return ret;
+       /* now remove from .AppleDouble dir */
+       if ((ret = remove_acl(buf)) != AFP_OK)
+           return ret;
+    } else
+       /* remove ACL from ressource fork */
+       if ((ret = remove_acl(vol->vfs->ad_path(path, ADFLAGS_HF))) != AFP_OK)
+           return ret;
+
+    return AFP_OK;
+}
+#endif
+
+/*********************************************************************************
+ * sfm adouble format
+ *********************************************************************************/
+static int ads_chown_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_)
+{
+    struct perm   *owner  = data;
+    
+    if (chown( name , owner->uid, owner->gid ) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static int RF_chown_ads(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
+
+{
+    struct        stat st;
+    char          *ad_p;
+    struct perm   owner;
+    
+    owner.uid = uid;
+    owner.gid = gid;
+
+
+    ad_p = ad_dir(vol->vfs->ad_path(path, ADFLAGS_HF ));
+
+    if ( stat( ad_p, &st ) < 0 ) {
+       /* ignore */
+        return 0;
+    }
+    
+    if (chown( ad_p, uid, gid ) < 0) {
+       return -1;
+    }
+    return for_each_adouble("chown_ads", ad_p, ads_chown_loop, &owner, 1, vol->v_umask);
+}
+
+/* --------------------------------- */
+static int deletecurdir_ads1_loop(struct dirent *de _U_, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_)
+{
+    return netatalk_unlink(name);
+}
+
+static int ads_delete_rf(char *name) 
+{
+    int err;
+
+    if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1, 0))) 
+        return err;
+    /* FIXME 
+     * it's a problem for a nfs mounted folder, there's .nfsxxx around
+     * for linux the following line solve it.
+     * but it could fail if rm .nfsxxx  create a new .nfsyyy :(
+    */
+    if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1, 0))) 
+        return err;
+    return netatalk_rmdir(name);
+}
+
+static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_)
+{
+    struct stat st;
+    
+    /* bail if the file exists in the current directory.
+     * note: this will not fail with dangling symlinks */
+    
+    if (stat(de->d_name, &st) == 0) {
+        return AFPERR_DIRNEMPT;
+    }
+    return ads_delete_rf(name);
+}
+
+static int RF_deletecurdir_ads(const struct vol *vol _U_)
+{
+    int err;
+    
+    /* delete stray .AppleDouble files. this happens to get .Parent files as well. */
+    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_ads_loop, NULL, 1, 0))) 
+        return err;
+    return netatalk_rmdir( ".AppleDouble" );
+}
+
+/* ------------------- */
+struct set_mode {
+    mode_t mode;
+    struct stat *st;
+};
+
+static int ads_setfilmode_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask)
+{
+    struct set_mode *param = data;
+
+    return setfilmode(name, param->mode, param->st, v_umask);
+}
+
+static int ads_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask)
+{
+    mode_t file_mode = ad_hf_mode(mode);
+    mode_t dir_mode = file_mode;
+    struct set_mode param;
+
+    if ((dir_mode & (S_IRUSR | S_IWUSR )))
+        dir_mode |= S_IXUSR;
+    if ((dir_mode & (S_IRGRP | S_IWGRP )))
+        dir_mode |= S_IXGRP;
+    if ((dir_mode & (S_IROTH | S_IWOTH )))
+        dir_mode |= S_IXOTH;   
+    
+       /* change folder */
+       dir_mode |= DIRBITS;
+    if (dir_rx_set(dir_mode)) {
+        if (chmod( name,  dir_mode ) < 0)
+            return -1;
+    }
+    param.st = st;
+    param.mode = file_mode;
+    if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, &param, 0, v_umask) < 0)
+        return -1;
+
+    if (!dir_rx_set(dir_mode)) {
+        if (chmod( name,  dir_mode ) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+static int RF_setfilmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    return ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_HF )), mode, st, vol->v_umask);
+}
+
+/* ------------------- */
+static int RF_setdirunixmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
+    char   ad_p[ MAXPATHLEN + 1];
+    int dropbox = vol->v_flags;
+
+    strlcpy(ad_p,ad_dir(adouble), MAXPATHLEN + 1);
+
+    if (dir_rx_set(mode)) {
+
+        /* .AppleDouble */
+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox, vol->v_umask) < 0) 
+            return -1;
+
+        /* .AppleDouble/.Parent */
+        if (stickydirmode(ad_p, DIRBITS | mode, dropbox, vol->v_umask) < 0) 
+            return -1;
+    }
+
+    if (ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR)), mode, st, vol->v_umask) < 0)
+        return -1;
+
+    if (!dir_rx_set(mode)) {
+        if (stickydirmode(ad_p, DIRBITS | mode, dropbox, vol->v_umask) < 0) 
+            return  -1 ;
+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox, vol->v_umask) < 0) 
+            return -1;
+    }
+    return 0;
+}
+
+/* ------------------- */
+struct dir_mode {
+    mode_t mode;
+    int    dropbox;
+};
+
+static int setdirmode_ads_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask)
+{
+
+    struct dir_mode *param = data;
+    int    ret = 0; /* 0 ignore error, -1 */
+
+    if (dir_rx_set(param->mode)) {
+        if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, v_umask) < 0) {
+            if (flag) {
+                return 0;
+            }
+            return ret;
+        }
+    }
+    if (ads_setfilmode(name, param->mode, NULL, v_umask) < 0)
+        return ret;
+
+    if (!dir_rx_set(param->mode)) {
+        if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, v_umask) < 0) {
+            if (flag) {
+                return 0;
+            }
+            return ret;
+        }
+    }
+    return 0;
+}
+
+static int RF_setdirmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st _U_)
+{
+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
+    char   ad_p[ MAXPATHLEN + 1];
+    struct dir_mode param;
+
+    param.mode = mode;
+    param.dropbox = vol->v_flags;
+
+    strlcpy(ad_p,ad_dir(adouble), sizeof(ad_p));
+
+    if (dir_rx_set(mode)) {
+        /* .AppleDouble */
+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox, vol->v_umask) < 0) 
+            return -1;
+    }
+
+    if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, &param, vol_noadouble(vol), vol->v_umask))
+        return -1;
+
+    if (!dir_rx_set(mode)) {
+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox, vol->v_umask) < 0 ) 
+            return -1;
+    }
+    return 0;
+}
+
+/* ------------------- */
+static int setdirowner_ads1_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_)
+{
+    struct perm   *owner  = data;
+
+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
+         /* return ( -1 ); Sometimes this is okay */
+    }
+    return 0;
+}
+
+static int setdirowner_ads_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask _U_)
+{
+    struct perm   *owner  = data;
+
+    if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, data, flag, 0) < 0)
+        return -1;
+
+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
+         /* return ( -1 ); Sometimes this is okay */
+    }
+    return 0;
+}
+
+static int RF_setdirowner_ads(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
+{
+    int           noadouble = vol_noadouble(vol);
+    char          adouble_p[ MAXPATHLEN + 1];
+    struct stat   st;
+    struct perm   owner;
+    
+    owner.uid = uid;
+    owner.gid = gid;
+
+    strlcpy(adouble_p, ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR )), sizeof(adouble_p));
+
+    if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, &owner, noadouble, 0)) 
+        return -1;
+
+    /*
+     * We cheat: we know that chown doesn't do anything.
+     */
+    if ( stat( ".AppleDouble", &st ) < 0) {
+        if (errno == ENOENT && noadouble)
+            return 0;
+        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
+        return -1;
+    }
+    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
+        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
+            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
+        /* return ( -1 ); Sometimes this is okay */
+    }
+    return 0;
+}
+
+/* ------------------- */
+static int RF_deletefile_ads(const struct vol *vol, const char *file )
+{
+    char *ad_p = ad_dir(vol->vfs->ad_path(file, ADFLAGS_HF ));
+
+    return ads_delete_rf(ad_p);
+}
+
+/* --------------------------- */
+int RF_renamefile_ads(const struct vol *vol, const char *src, const char *dst)
+{
+    char  adsrc[ MAXPATHLEN + 1];
+    int   err = 0;
+
+    strcpy( adsrc, ad_dir(vol->vfs->ad_path( src, 0 )));
+    if (unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) < 0) {
+        struct stat st;
+
+        err = errno;
+        if (errno == ENOENT) {
+               struct adouble    ad;
+
+            if (stat(adsrc, &st)) /* source has no ressource fork, */
+                return 0;
+            
+            /* We are here  because :
+             * -there's no dest folder. 
+             * -there's no .AppleDouble in the dest folder.
+             * if we use the struct adouble passed in parameter it will not
+             * create .AppleDouble if the file is already opened, so we
+             * use a diff one, it's not a pb,ie it's not the same file, yet.
+             */
+            ad_init(&ad, vol->v_adouble, vol->v_ad_options); 
+            if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
+               ad_close(&ad, ADFLAGS_HF);
+
+               /* We must delete it */
+               RF_deletefile_ads(vol, dst );
+               if (!unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) ) 
+                   err = 0;
+                else 
+                   err = errno;
+            }
+            else { /* it's something else, bail out */
+                   err = errno;
+               }
+           }
+       }
+       if (err) {
+               errno = err;
+               return -1;
+       }
+       return 0;
+}
+
+/*************************************************************************
+ * osx adouble format 
+ ************************************************************************/
+static int validupath_osx(const struct vol *vol, const char *name) 
+{
+    return strncmp(name,"._", 2) && (
+      (vol->v_flags & AFPVOL_USEDOTS) ? netatalk_name(name): name[0] != '.');
+}             
+
+/* ---------------- */
+int RF_renamedir_osx(const struct vol *vol, const char *oldpath, const char *newpath)
+{
+    /* We simply move the corresponding ad file as well */
+    char   tempbuf[258]="._";
+    return rename(vol->vfs->ad_path(oldpath,0),strcat(tempbuf,newpath));
+}
+
+/* ---------------- */
+int RF_deletecurdir_osx(const struct vol *vol)
+{
+    return netatalk_unlink( vol->vfs->ad_path(".",0) );
+}
+
+/* ---------------- */
+static int RF_setdirunixmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st, vol->v_umask);
+}
+
+/* ---------------- */
+static int 
+RF_setdirmode_osx(const struct vol *vol _U_, const char *name _U_, mode_t mode _U_, struct stat *st _U_)
+{
+    return 0;
+}
+
+/* ---------------- */
+static int 
+RF_setdirowner_osx(const struct vol *vol _U_, const char *path _U_, uid_t uid _U_, gid_t gid _U_)
+{
+       return 0;
+}
+
+/* ---------------- */
+int RF_renamefile_osx(const struct vol *vol, const char *src, const char *dst)
+{
+    char  adsrc[ MAXPATHLEN + 1];
+    int   err = 0;
+
+    strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
+
+    if (unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) < 0) {
+        struct stat st;
+
+        err = errno;
+        if (errno == ENOENT && stat(adsrc, &st)) /* source has no ressource fork, */
+            return 0;
+        errno = err;
+        return -1;
+    }
+    return 0;
+}
+
+struct vfs_ops netatalk_adouble = {
+    /* ad_path:           */ ad_path,
+    /* validupath:        */ validupath_adouble,
+    /* rf_chown:          */ RF_chown_adouble,
+    /* rf_renamedir:      */ RF_renamedir_adouble,
+    /* rf_deletecurdir:   */ RF_deletecurdir_adouble,
+    /* rf_setfilmode:     */ RF_setfilmode_adouble,
+    /* rf_setdirmode:     */ RF_setdirmode_adouble,
+    /* rf_setdirunixmode: */ RF_setdirunixmode_adouble,
+    /* rf_setdirowner:    */ RF_setdirowner_adouble,
+    /* rf_deletefile:     */ RF_deletefile_adouble,
+    /* rf_renamefile:     */ RF_renamefile_adouble,
+#ifdef HAVE_NFSv4_ACLS
+    /* rf_acl:            */ RF_acl,
+    /* rf_remove_acl      */ RF_remove_acl
+#endif
+};
+
+struct vfs_ops netatalk_adouble_osx = {
+    /* ad_path:          */ ad_path_osx,
+    /* validupath:       */ validupath_osx,
+    /* rf_chown:         */ RF_chown_adouble,
+    /* rf_renamedir:     */ RF_renamedir_osx,
+    /* rf_deletecurdir:  */ RF_deletecurdir_osx,
+    /* rf_setfilmode:    */ RF_setfilmode_adouble,
+    /* rf_setdirmode:    */ RF_setdirmode_osx,
+    /* rf_setdirunixmode:*/ RF_setdirunixmode_osx,
+    /* rf_setdirowner:   */ RF_setdirowner_osx,
+    /* rf_deletefile:    */ RF_deletefile_adouble,
+    /* rf_renamefile:    */ RF_renamefile_osx,
+};
+
+/* samba sfm format. ad_path shouldn't be set her */
+struct vfs_ops netatalk_adouble_sfm = {
+    /* ad_path:          */ ad_path_sfm,
+    /* validupath:       */ validupath_adouble,
+    /* rf_chown:         */ RF_chown_ads,
+    /* rf_renamedir:     */ RF_renamedir_adouble,
+    /* rf_deletecurdir:  */ RF_deletecurdir_ads,
+    /* rf_setfilmode:    */ RF_setfilmode_ads,
+    /* rf_setdirmode:    */ RF_setdirmode_ads,
+    /* rf_setdirunixmode:*/ RF_setdirunixmode_ads,
+    /* rf_setdirowner:   */ RF_setdirowner_ads,
+    /* rf_deletefile:    */ RF_deletefile_ads,
+    /* rf_renamefile:    */ RF_renamefile_ads,
+};
+
+/* ---------------- */
+void initvol_vfs(struct vol *vol)
+{
+    /* adouble stuff */
+    if (vol->v_adouble == AD_VERSION2_OSX) {
+        vol->vfs = &netatalk_adouble_osx;
+    }
+    else if (vol->v_adouble == AD_VERSION1_SFM) {
+        vol->vfs = &netatalk_adouble_sfm;
+    }
+    else {
+        vol->vfs = &netatalk_adouble;
+    }
+
+    /* Extended Attributes */
+    if (vol->v_vfs_ea == AFPVOL_EA_SOLARIS) {
+
+#ifdef HAVE_SOLARIS_EAS
+        LOG(log_debug, logtype_afpd, "initvol_vfs: Enabling EA support with Solaris native EAs.");
+
+        netatalk_adouble.list_eas = sol_list_eas;
+        netatalk_adouble.get_easize = sol_get_easize;
+        netatalk_adouble.get_eacontent = sol_get_eacontent;
+        netatalk_adouble.set_ea = sol_set_ea;
+        netatalk_adouble.remove_ea = sol_remove_ea;
+#else
+        LOG(log_error, logtype_afpd, "initvol_vfs: Can't enable Solaris EA support.");
+        goto enable_adea;
+#endif
+    } else {
+    enable_adea:
+        /* default: AFPVOL_EA_AD */
+        LOG(log_debug, logtype_afpd, "initvol_vfs: Enabling EA support with adouble files.");
+
+        netatalk_adouble.set_ea = set_ea;
+        netatalk_adouble.list_eas = list_eas;
+        netatalk_adouble.get_easize = get_easize;
+        netatalk_adouble.get_eacontent = get_eacontent;
+        netatalk_adouble.remove_ea = remove_ea;
+    }
+}
+
index ddcf2f003489fecab3499fa087e00119ddb9678c..78ffb3eae7f3a7b12f052550c89e5065795a9a98 100644 (file)
@@ -1,4 +1,4 @@
-dnl $Id: summary.m4,v 1.5 2009-02-16 13:49:20 franklahm Exp $
+dnl $Id: summary.m4,v 1.6 2009-10-02 09:32:41 franklahm Exp $
 dnl Autoconf macros, display configure summary
 
 AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
@@ -15,6 +15,7 @@ AC_DEFUN([AC_NETATALK_CONFIG_SUMMARY], [
        if test "x$afp3" = "xyes"; then
                AC_MSG_RESULT([         Large file support (>2GB) for AFP3: $wx_largefile])
        fi
+       AC_MSG_RESULT([         Extended Attributes: $neta_cv_eas])
        AC_MSG_RESULT([         DDP enabled: $netatalk_cv_ddp_enabled])
        AC_MSG_RESULT([    CNID:])
        AC_MSG_RESULT([         backends: $compiled_backends])
@@ -61,7 +62,6 @@ 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])
index 74e9c4ce23bffab95cd812e17c587a1c799e4bb9..c24c0aa923acab3065d4caf86b1414d716c4201d 100644 (file)
-.TH AppleVolumes.default 5 "06 September 2004" 2.0.0 Netatalk 
-.SH NAME
-AppleVolumes.default \- Configuration file used by afpd(8) to determine the shares made available through Appletalk
-.SH DESCRIPTION
-\fB:ETCDIR:/AppleVolumes.default\fR is the
-configuration file used by afpd to determine what
-portions of the file system will be shared via Apple Filing Protocol, as
-well as their behaviour. Any line not prefixed with # is interpreted. The
-configuration lines are composed like:
-.PP
-\fBpath\fR \fI[ volume name ] [ options
-]\fR
-.PP
-The path name must be a fully qualified path name, or a path name
-using either the ~ shell shorthand or any of the substitution variables,
-which are listed below.
-.PP
-The volume name is the name that appears in the Chooser ot the
-"connect to server" dialog on Macintoshes to represent the appropriate
-share. If there are spaces in the name, it should be in quotes (i.e. "File
-Share"). The volume name may not exceed 27 characters in length, and
-cannot contain the \fB':'\fR character.
-.RS 
+'\" t
+.\"     Title: AppleVolumes.default
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.74.3 <http://docbook.sf.net/>
+.\"      Date: 02 Octobre 2009
+.\"    Manual: Netatalk 2.1
+.\"    Source: Netatalk 2.1
+.\"  Language: English
+.\"
+.TH "APPLEVOLUMES\&.DEFAU" "5" "02 Octobre 2009" "Netatalk 2.1" "Netatalk 2.1"
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+AppleVolumes.default \- Configuration file used by \fBafpd\fR(8) to determine the shares made available through Appletalk
+.SH "DESCRIPTION"
+.PP
+:ETCDIR:/AppleVolumes\&.default
+is the configuration file used by
+\fBafpd\fR
+to determine what portions of the file system will be shared via Apple Filing Protocol, as well as their behaviour\&. Any line not prefixed with # is interpreted\&. Newline escaping is supported\&. The configuration lines are composed like:
+.PP
+path
+\fI[ volume name ] [ options ]\fR
+.PP
+The path name must be a fully qualified path name, or a path name using either the ~ shell shorthand or any of the substitution variables, which are listed below\&.
+.PP
+The volume name is the name that appears in the Chooser ot the "connect to server" dialog on Macintoshes to represent the appropriate share\&. If there are spaces in the name, it should be in quotes (i\&.e\&. "File Share")\&. The volume name may not exceed 27 characters in length, and cannot contain the
+\':\'
+character\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
 \fBNote\fR
+.ps -1
+.br
 .PP
-Each volume has to be configured on a \fBsingle\fR line.
+Each volume has to be configured on a
+\fBsingle\fR
+line\&. Though newline escaping is supported\&.
+.sp .5v
 .RE
 .PP
 The possible options and their meanings are:
-.TP 
+.PP
 adouble:\fI[v1|v2|osx]\fR
-specify the format of the metadata files, which are used for
-saving Mac resource fork as well. Earlier versions used AppleDouble
-V1, the new default format is V2. Starting with Netatalk 2.0, the
-scheme MacOS X uses currently (10.3.x), is also supported
-.RS 
+.RS 4
+Specify the format of the metadata files, which are used for saving Mac resource fork as well\&. Earlier versions used AppleDouble V1, the new default format is V2\&. Starting with Netatalk 2\&.0, the scheme MacOS X uses currently (10\&.3\&.x), is also supported
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
 \fBNote\fR
-
-Using \fBadouble:osx\fR is \fBnot\fR recommended for production use. Its
-only aim is to temporarely share eg. FAT32 formatted FireWire
-harddrives written on a Macintosh with afpd. Apple's metadata
-scheme lacks several essential features, so using it on the
-server's side will break both CNIDs and MacOS 9
-compatibility
-.RE
-.TP 
+.ps -1
+.br
+Using
+\fBadouble:osx\fR
+is
+\fBnot\fR
+recommended for production use\&. Its only aim is to temporarely share eg\&. FAT32 formatted FireWire harddrives written on a Macintosh with afpd\&. Apple\'s metadata scheme lacks several essential features, so using it on the server\'s side will break both CNIDs and MacOS 9 compatibility
+.sp .5v
+.RE
+.RE
+.PP
 allow:\fI[users/groups]\fR
-The allow option allows the users and groups that access a
-share to be specified. Users and groups are specified, delimited by
-commas. Groups are designated by a @ prefix. Example:
-allow:user1,user2,@group
-.TP 
+.RS 4
+The allow option allows the users and groups that access a share to be specified\&. Users and groups are specified, delimited by commas\&. Groups are designated by a @ prefix\&. Example: allow:user1,user2,@group
+.RE
+.PP
 deny:\fI[users/groups]\fR
-The deny option specifies users and groups who are not allowed
-access to the share. It follows the same format as the allow
-option.
-.TP 
+.RS 4
+The deny option specifies users and groups who are not allowed access to the share\&. It follows the same format as the allow option\&.
+.RE
+.PP
+allowed_hosts:\fI[IPv4 host address/IPv4 netmask bits[, \&.\&.\&. ]]\fR
+.RS 4
+Only listed hosts and networks are allowed, all others are rejected\&. Example: allowed_hosts:10\&.1\&.0\&.0/16,10\&.2\&.1\&.100
+.RE
+.PP
+denied_hosts:\fI[IPv4 host address/IPv4 netmask bits[, \&.\&.\&.]]\fR
+.RS 4
+Listed hosts and nets are rejected, all others are allowed\&. Example: denied_hosts: 192\&.168\&.100/24,10\&.1\&.1\&.1
+.RE
+.PP
 cnidscheme:\fI[backend]\fR
-set the CNID backend to be used for the volume, default is
-[:DEFAULT_CNID_SCHEME:] available schemes:
-[:COMPILED_BACKENDS:]
-.TP 
+.RS 4
+set the CNID backend to be used for the volume, default is [:DEFAULT_CNID_SCHEME:] available schemes: [:COMPILED_BACKENDS:]
+.RE
+.PP
 dbpath:\fI[path]\fR
-Sets the database information to be stored in path. You have
-to specifiy a writable location, even if the volume is read
-only.
-.TP 
+.RS 4
+Sets the database information to be stored in path\&. You have to specifiy a writable location, even if the volume is read only\&.
+.RE
+.PP
+ea:\fI[ad|solaris]\fR
+.RS 4
+Specify the format of the metadata files which are used for saving Extended Attributes\&.
+\fBad\fR
+is the default and stores the EAs inside the
+\&.AppleDouble
+directories\&.
+\fBsolaris\fR
+uses native
+\fI[Open]Solaris\fR
+EAs if available\&.
+.RE
+.PP
 maccharset:\fI[charset]\fR
-specifies the mac client codepage for this Volume, e.g.
-"MAC_ROMAN", "MAC_CYRILLIC". If not specified the setting from
-\fBafpd.conf\fR is inherited. This setting is only
-required if you need volumes, where the mac codepage differs from
-the one globally set in \fBafpd.conf\fR.
-.TP 
+.RS 4
+specifies the mac client codepage for this Volume, e\&.g\&. "MAC_ROMAN", "MAC_CYRILLIC"\&. If not specified the setting from
+afpd\&.conf
+is inherited\&. This setting is only required if you need volumes, where the mac codepage differs from the one globally set in
+afpd\&.conf\&.
+.RE
+.PP
 options:\fI[option]\fR
-This allows multiple options to be specified in a comma
-delimited format. The available options are:
-.RS 
-.TP 
+.RS 4
+This allows multiple options to be specified in a comma delimited format\&. The available options are:
+.PP
+acls
+.RS 4
+Enable ACLs on this volume\&. Requires a
+\fINFSv4 ACLs\fR
+compatible filesystem (e\&.g\&. ZFS) and an ACL API compatible to *Solaris\&. In other words: this requires Solaris, Opensolaris or a derived distribution\&.
+.RE
+.PP
+tm
+.RS 4
+Enable Time Machine suport for this volume\&.
+.RE
+.PP
+invisibledots
+.RS 4
+Use with
+\fBusedots\fR: make dot files invisible\&.
+.RE
+.PP
 limitsize
-Limit disk size reporting to 2GB. This can be used for
-older Macintoshes using newer Appleshare clients.
-.TP 
+.RS 4
+Limit disk size reporting to 2GB\&. This can be used for older Macintoshes using newer Appleshare clients\&.
+.RE
+.PP
+preexec_close
+.RS 4
+a non\-zero return code from preexec close the volume being immediately, preventing clients to mount/see the volume in question\&.
+.RE
+.PP
 ro
-Specifies the share as being read only for all users.
-The .AppleDB directory has to be writeable, you can use the
-\fB\-dbpath\fR option to relocate it.
-.TP 
-usedots
-Don't do :hex translation for dot files. note: when this
-option gets set, certain file names become illegal. These are
-\&.Parent and anything that starts with .Apple. Also, dot files
-created on the unix side are marked invisible.
-.TP 
+.RS 4
+Specifies the share as being read only for all users\&. The \&.AppleDB directory has to be writeable, you can use the
+\fB\-dbpath\fR
+option to relocate it\&.
+.RE
+.PP
 root_preexec_close
-a non\-zero return code from root_preexec closes the
-volume immediately, preventing clients to mount/see the volume
-in question.
-.TP 
-preexec_close
-a non\-zero return code from preexec close the volume
-being immediately, preventing clients to mount/see the volume
-in question.
+.RS 4
+a non\-zero return code from root_preexec closes the volume immediately, preventing clients to mount/see the volume in question\&.
+.RE
+.PP
+upriv
+.RS 4
+use AFP3 unix privileges\&. Become familiar with the new "unix privileges" AFP permissions concepts in MacOS X before using this option\&. See also:
+\fBperm|fperm|dperm\fR\&.
+.RE
+.PP
+usedots
+.RS 4
+Don\'t do :hex translation for dot files\&. note: when this option gets set, certain file names become illegal\&. These are \&.Parent and anything that starts with \&.Apple\&. See also
+\fBinvisibledots\fR\&.
+.RE
 .RE
-.TP 
+.PP
 password:\fI[password]\fR
-This option allows you to set a volume password, which can be
-a maximum of 8 characters long (using ASCII strongly recommended at
-the time of this writing).
-.TP 
+.RS 4
+This option allows you to set a volume password, which can be a maximum of 8 characters long (using ASCII strongly recommended at the time of this writing)\&.
+.RE
+.PP
+perm|fperm|dperm:[mode]
+.RS 4
+Add(or) with the client requested permissions:
+\fBperm\fR
+affects files and directories,
+\fBfperm\fR
+is for files only,
+\fBdperm\fR
+is for directories only\&. Use with
+\fBoptions:upriv\fR\&.
+.PP
+\fBExample.\ \&Volume for a collaborative workgroup\fR
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+/path/to/volume "Workgroup" options:upriv dperm:0770 fperm:0660
+.fi
+.if n \{\
+.RE
+.\}
+.RE
+.PP
 preexec:\fI[command]\fR
-command to be run when the volume is mounted, ignored for user
-defined volumes
-.TP 
+.RS 4
+command to be run when the volume is mounted, ignored for user defined volumes
+.RE
+.PP
 postexec:\fI[command]\fR
-command to be run when the volume is closed, ignored for user
-defined volumes
-.TP 
+.RS 4
+command to be run when the volume is closed, ignored for user defined volumes
+.RE
+.PP
 root_preexec:\fI[command]\fR
-command to be run as root when the volume is mounted, ignored
-for user defined volumes
-.TP 
+.RS 4
+command to be run as root when the volume is mounted, ignored for user defined volumes
+.RE
+.PP
 root_postexec:\fI[command]\fR
-command to be run as root when the volume is closed, ignored
-for user defined volumes
-.TP 
+.RS 4
+command to be run as root when the volume is closed, ignored for user defined volumes
+.RE
+.PP
 rolist:[\fBusers/groups\fR]
-Allows certain users and groups to have read\-only access to a
-share. This follows the allow option format.
-.TP 
+.RS 4
+Allows certain users and groups to have read\-only access to a share\&. This follows the allow option format\&.
+.RE
+.PP
 rwlist:\fI[users/groups]\fR
-Allows certain users and groups to have read/write access to a
-share. This follows the allow option format.
-.TP 
+.RS 4
+Allows certain users and groups to have read/write access to a share\&. This follows the allow option format\&.
+.RE
+.PP
 veto:\fI[vetoed name]\fR
-hide files and directories,where the path matches one of the
-\&'/' delimited vetoed names. Matches are partial, e.g. path is
-\fB/abc/def/file\fR and veto:/abc/ will hide the
-file.
-.TP 
+.RS 4
+hide files and directories,where the path matches one of the \'/\' delimited vetoed names\&. Matches are partial, e\&.g\&. path is
+/abc/def/file
+and veto:/abc/ will hide the file\&.
+.RE
+.PP
 volcharset:\fI[charset]\fR
-specifies the volume codepage, e.g. "UTF8", "UTF8\-MAC",
-"ISO\-8859\-15". Defaults to "UTF8".
+.RS 4
+specifies the volume codepage, e\&.g\&. "UTF8", "UTF8\-MAC", "ISO\-8859\-15"\&. Defaults to "UTF8"\&.
+.RE
 .SH "VARIABLE SUBSTITUTIONS"
-You can use variables in both volume path and volume name.
-.TP 
-1.
-if you specify an unknown variable, it will not get
-converted.
-.TP 
-2.
-if you specify a known variable, but that variable doesn't have
-a value, it will get ignored.
+.PP
+You can use variables in both volume path and volume name\&.
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 1.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP "  1." 4.2
+.\}
+if you specify an unknown variable, it will not get converted\&.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 2.\h'+01'\c
+.\}
+.el \{\
+.sp -1
+.IP "  2." 4.2
+.\}
+if you specify a known variable, but that variable doesn\'t have a value, it will get ignored\&.
+.RE
 .PP
 The variables which can be used for substitutions are:
-.TP 
+.PP
 $b
+.RS 4
 basename
-.TP 
+.RE
+.PP
 $c
-client's ip or appletalk address
-.TP 
+.RS 4
+client\'s ip or appletalk address
+.RE
+.PP
 $d
+.RS 4
 volume pathname on server
-.TP 
+.RE
+.PP
 $f
-full name (contents of the gecos field in the passwd
-file)
-.TP 
+.RS 4
+full name (contents of the gecos field in the passwd file)
+.RE
+.PP
 $g
+.RS 4
 group name
-.TP 
+.RE
+.PP
 $h
+.RS 4
 hostname
-.TP 
+.RE
+.PP
 $i
-client's ip, without port
-.TP 
+.RS 4
+client\'s ip, without port
+.RE
+.PP
 $s
+.RS 4
 server name (this can be the hostname)
-.TP 
+.RE
+.PP
 $u
-user name (if guest, it is the user that guest is running
-as)
-.TP 
+.RS 4
+user name (if guest, it is the user that guest is running as)
+.RE
+.PP
 $v
+.RS 4
 volume name (either ADEID_NAME or basename of path)
-.TP 
+.RE
+.PP
 $z
+.RS 4
 appletalk zone (may not exist)
-.TP 
+.RE
+.PP
 $$
+.RS 4
 prints dollar sign ($)
+.RE
 .PP
-When using variable substitution in the volume name, always keep in
-mind, not to exceed the 27 characters limit
+When using variable substitution in the volume name, always keep in mind, not to exceed the 27 characters limit
 .PP
-\fBUsing variable substitution when defining volumes\fR
+\fBExample.\ \&Using variable substitution when defining volumes\fR
 .PP
+.if n \{\
+.RS 4
+.\}
 .nf
 /home/groups/$g "Groupdir for $g"
 ~ "$f is the best one"
 .fi
-
-We define "groupdirs" for each primary
-group and use a personalized server name for homedir shares.
+.if n \{\
+.RE
+.\}
+.sp
+We define "groupdirs" for each primary group and use a personalized server name for homedir shares\&.
 .SH "CNID BACKENDS"
-The AFP protocol mostly refers to files and directories by ID and
-not by name. Netatalk needs a way to store these ID's in a persistent way,
-to achieve this several different CNID backends are available. The CNID
-Databases are by default located in the \fB.AppleDB\fR
-folder in the volume root.
-.TP 
+.PP
+The AFP protocol mostly refers to files and directories by ID and not by name\&. Netatalk needs a way to store these ID\'s in a persistent way, to achieve this several different CNID backends are available\&. The CNID Databases are by default located in the
+\&.AppleDB
+folder in the volume root\&.
+.PP
 cdb
-"Concurrent database", backend is based on Sleepycat's Berkely
-DB. With this backend several afpd deamons access
-the CNID database directly. Berkeley DB locking is used to
-synchronize access, if more than one afpd process
-is active for a volume. The drawback is, that the crash of a single
-afpd process might corrupt the database.
-.TP 
+.RS 4
+"Concurrent database", backend is based on Sleepycat\'s Berkely DB\&. With this backend several
+\fBafpd\fR
+deamons access the CNID database directly\&. Berkeley DB locking is used to synchronize access, if more than one
+\fBafpd\fR
+process is active for a volume\&. The drawback is, that the crash of a single
+\fBafpd\fR
+process might corrupt the database\&.
+.RE
+.PP
 dbd
+.RS 4
 Access to the CNID database is restricted to the
-cnid_metad daemon process.
-afpd processes communicate with the daemon for
-database reads and updates. If built with Berkeley DB transactions
-the probability for database corruption is practically zero, but
-performance can be slower than with \fBcdb\fR
-.TP 
+\fBcnid_metad\fR
+daemon process\&.
+\fBafpd\fR
+processes communicate with the daemon for database reads and updates\&. If built with Berkeley DB transactions the probability for database corruption is practically zero, but performance can be slower than with
+\fBcdb\fR
+.RE
+.PP
 last
-This backend is an exception, in terms of ID persistency. ID's
-are only valid for the current session. This is basically what
-afpd did in the 1.5 (and 1.6) versions. This
-backend is still available, as it is useful for e.g. sharing
-cdroms.
-
+.RS 4
+This backend is an exception, in terms of ID persistency\&. ID\'s are only valid for the current session\&. This is basically what
+\fBafpd\fR
+did in the 1\&.5 (and 1\&.6) versions\&. This backend is still available, as it is useful for e\&.g\&. sharing cdroms\&.
+.sp
 \fBWarning\fR: It is
-\fINOT\fR recommended to use this backend for volumes
-anymore, as afpd now relies heavily on a
-persistent ID database. Aliases will likely not work and filename
-mangling is not supported.
-.PP
-Even though ./configure \-\-help might show that
-there are other CNID backends available, be warned those are likely broken
-or mainly used for testing. Don't use them unless you know what you're
-doing, they may be removed without further notice from future
-versions.
+\fINOT\fR
+recommended to use this backend for volumes anymore, as
+\fBafpd\fR
+now relies heavily on a persistent ID database\&. Aliases will likely not work and filename mangling is not supported\&.
+.RE
+.PP
+Even though
+\fB\&./configure \-\-help\fR
+might show that there are other CNID backends available, be warned those are likely broken or mainly used for testing\&. Don\'t use them unless you know what you\'re doing, they may be removed without further notice from future versions\&.
 .SH "CHARSET OPTIONS"
-With OS X Apple introduced the AFP3 protocol. One of the most
-important changes was that AFP3 uses unicode names encoded as UTF\-8
-decomposed. Previous AFP/OS versions used codepages, like MacRoman,
-MacCentralEurope, etc.
-.PP
-afpd needs a way to preserve extended macintosh
-characters, or characters illegal in unix filenames, when saving files on
-a unix filesystem. Earlier versions used the the so called CAP encoding.
-An extended character (>0x7F) would be converted to a :xx sequence,
-e.g. the Apple Logo (MacRoman: 0XF0) was saved as \fB:f0\fR.
-Some special characters will be converted as to :xx notation as well.
-\&'\fB/\fR' will be encoded to \fB:2f\fR, if
-\fB\-usedots\fR is not specified, a leading dot
-\&'\fB.\fR' will be encoded as \fB:2e\fR.
-.PP
-This version now uses UTF\-8 as the default encoding for names.
-Special characters, like '\fB/\fR' and a leading
-\&'\fB.\fR' will still be CAP style encoded .
-.PP
-The \fB\-volcharset\fR option will allow you to select
-another volume encoding. E.g. for western users another useful setting
-could be \-volcharset ISO\-8859\-15. apfd will accept any
-\fBiconv\fR(1) provided charset. If a character cannot be converted
-from the mac codepage to the selected volcharset, afpd will save it as a
-CAP encoded character. For AFP3 clients, afpd will
-convert the UTF\-8 character to \fB\-maccharset\fR first. If this
-conversion fails, you'll receive a \-50 error on the mac.
-.PP
-\fINote\fR: Whenever you can, please stick with the
-default UTF\-8 volume format.
+.PP
+With OS X Apple introduced the AFP3 protocol\&. One of the most important changes was that AFP3 uses unicode names encoded as UTF\-8 decomposed\&. Previous AFP/OS versions used codepages, like MacRoman, MacCentralEurope, etc\&.
+.PP
+\fBafpd\fR
+needs a way to preserve extended macintosh characters, or characters illegal in unix filenames, when saving files on a unix filesystem\&. Earlier versions used the the so called CAP encoding\&. An extended character (>0x7F) would be converted to a :xx sequence, e\&.g\&. the Apple Logo (MacRoman: 0XF0) was saved as
+:f0\&. Some special characters will be converted as to :xx notation as well\&. \'/\' will be encoded to
+:2f, if
+\fB\-usedots\fR
+is not specified, a leading dot \'\&.\' will be encoded as
+:2e\&.
+.PP
+This version now uses UTF\-8 as the default encoding for names\&. Special characters, like \'/\' and a leading \'\&.\' will still be CAP style encoded \&.
+.PP
+The
+\fB\-volcharset\fR
+option will allow you to select another volume encoding\&. E\&.g\&. for western users another useful setting could be \-volcharset ISO\-8859\-15\&.
+\fBapfd\fR
+will accept any
+\fBiconv\fR(1)
+provided charset\&. If a character cannot be converted from the mac codepage to the selected volcharset, afpd will save it as a CAP encoded character\&. For AFP3 clients,
+\fBafpd\fR
+will convert the UTF\-8
+character to
+\fB\-maccharset\fR
+first\&. If this conversion fails, you\'ll receive a \-50 error on the mac\&.
+.PP
+\fINote\fR: Whenever you can, please stick with the default UTF\-8 volume format\&.
 .SH "COMPATIBILITY WITH EARLIER VERSIONS"
-To use a volume created with an earlier afpd
-version, you'll have to specify the following options:
 .PP
-\fBuse a 1.x style volume\fR
+To use a volume created with an earlier
+\fBafpd\fR
+version, you\'ll have to specify the following options:
 .PP
+\fBExample.\ \&use a 1.x style volume\fR
+.sp
+.if n \{\
+.RS 4
+.\}
 .nf
 /path/to/volume "Volname" adouble:v1 volcharset:ASCII
 .fi
+.if n \{\
+.RE
+.\}
 .PP
-In case you used an NLS you could try using a compatible iconv
-charset for \fB\-volcharset\fR.
-.PP
-\fBuse a 1.x style volume, created with maccode.iso8859\-1\fR
+In case you used an NLS you could try using a compatible iconv charset for
+\fB\-volcharset\fR\&.
 .PP
+\fBExample.\ \&use a 1.x style volume, created with maccode.iso8859-1\fR
+.sp
+.if n \{\
+.RS 4
+.\}
 .nf
 /path/to/volume "Volname" adouble:v1 volcharset:ISO\-8859\-1
 .fi
+.if n \{\
+.RE
+.\}
 .PP
-You should consider converting old style volumes to the new
-UTF\-8/AD2 format. The safest way to do this, is to create a new volume
-with the default options and copy the files between this volumes with a
-mac.
+You should consider converting old style volumes to the new UTF\-8/AD2 format\&. The safest way to do this, is to create a new volume with the default options and copy the files between this volumes with a mac\&.
 .PP
-\fINote\fR: Using above example options will allow
-you to downgrade to 1.x netatalk again.
+\fINote\fR: Using above example options will allow you to downgrade to 1\&.x netatalk again\&.
 .PP
-\fINote\fR: Some 1.x NLS files used non standard
-mappings, e.g. \fBmaccode.iso8859\-1.adapted\fR. This is not
-supported anymore. You'll have to copy the contents of those volumes files
-to a Mac and then back to the netatalk server, preferably to an UTF\-8
-volume.
+\fINote\fR: Some 1\&.x NLS files used non standard mappings, e\&.g\&.
+maccode\&.iso8859\-1\&.adapted\&. Three 1\&.x CAP double\-byte maccharsets are incompatible to netatalk 2\&.x; "MAC_CHINESE_TRAD", "MAC_JAPANESE" and "MAC_KOREAN"\&. These are not supported anymore\&. You\'ll have to copy the contents of those volumes files to a Mac and then back to the netatalk server, preferably to an UTF\-8 volume\&.
 .SH "ADVANCED OPTIONS"
-The following options should only be used after serious
-consideration. Be sure you fully understood the, sometimes complex,
-consequences, before using them.
-.TP 
+.PP
+The following options should only be used after serious consideration\&. Be sure you fully understood the, sometimes complex, consequences, before using them\&.
+.PP
 casefold:\fB[option]\fR
-The casefold option handles, if the case of filenames should
-be changed. The available options are:
-
-\fBtolower\fR \- Lowercases names in both
-directions.
-
-\fBtoupper\fR \- Uppercases names in both
-directions.
-
-\fBxlatelower\fR \- Client sees lowercase, server
-sees uppercase.
-
-\fBxlateupper\fR \- Client sees uppercase, server
-sees lowercase.
-.TP 
+.RS 4
+The casefold option handles, if the case of filenames should be changed\&. The available options are:
+.sp
+\fBtolower\fR
+\- Lowercases names in both directions\&.
+.sp
+\fBtoupper\fR
+\- Uppercases names in both directions\&.
+.sp
+\fBxlatelower\fR
+\- Client sees lowercase, server sees uppercase\&.
+.sp
+\fBxlateupper\fR
+\- Client sees uppercase, server sees lowercase\&.
+.RE
+.PP
 options:[\fBoption\fR]
-This allows multiple options to be specified in a comma
-delimited format. The available options are:
-.RS 
-.TP 
+.RS 4
+This allows multiple options to be specified in a comma delimited format\&. The available options are:
+.PP
+nocnidcache
+.RS 4
+If set
+\fBafpd\fR
+doesn\'t store the ID information in AppleDouble V2 header files\&. As these IDs are used for caching and as a database backup, this option normally shouldn\'t be set\&.
+.RE
+.PP
 crlf
-Enables crlf translation for TEXT files, automatically
-converting macintosh line breaks into Unix ones. Use of this
-option might be dangerous since some older programs store
-binary data files as type "TEXT" when saving and switch the
-filetype in a second step. Afpd will
-potentially destroy such files when "erroneously" changing
-bytes in order to do line break translation.
-.TP 
+.RS 4
+Enables crlf translation for TEXT files, automatically converting macintosh line breaks into Unix ones\&. Use of this option might be dangerous since some older programs store binary data files as type "TEXT" when saving and switch the filetype in a second step\&.
+\fBAfpd\fR
+will potentially destroy such files when "erroneously" changing bytes in order to do line break translation\&.
+.RE
+.PP
 dropbox
-Allows a volume to be declared as being a "dropbox."
-Note that netatalk must be compiled with dropkludge support
-for this to function. \fIWarning\fR: This
-option is deprecated and might not work as expected.
-.TP 
+.RS 4
+Allows a volume to be declared as being a "dropbox\&." Note that netatalk must be compiled with dropkludge support for this to function\&.
+\fIWarning\fR: This option is deprecated and might not work as expected\&.
+.RE
+.PP
 mswindows
-Forces filename restrictions imposed by MS WinXX.
-\fIWarning\fR: This is \fINOT\fR
-recommened for volumes mainly used by Macs. Please make sure
-you fully understand this option before using it.
-.TP 
+.RS 4
+Forces filename restrictions imposed by MS WinXX\&.
+\fIWarning\fR: This is
+\fINOT\fR
+recommened for volumes mainly used by Macs\&. Please make sure you fully understand this option before using it\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBWarning\fR
+.ps -1
+.br
+This option breaks direct saving to netatalk volumes from some applications, i\&.e\&. OfficeX\&.
+.sp .5v
+.RE
+.RE
+.PP
 noadouble
-Forces afpd to not create
-\&.AppleDouble directories unless macintosh metadata needs to be
-written. This option is only useful if you want to share files
-mostly used NOT by macs, causing afpd to
-not automatically create .AppleDouble subdirs containing AD
-header files in every directory it enters (which will it do by
-default).
-
-In case, you save or change files from mac clients, AD
-metadata files have to be written even in case you set this
-option. So you can't avoid the creation of .AppleDouble
-directories and its contents when you give macs write access
-to a share and they make use of it.
-
-Try to avoid \fBnoadouble\fR whenever
-possible.
-.TP 
+.RS 4
+Forces
+\fBafpd\fR
+to not create \&.AppleDouble directories unless macintosh metadata needs to be written\&. This option is only useful if you want to share files mostly used NOT by macs, causing
+\fBafpd\fR
+to not automatically create \&.AppleDouble subdirs containing AD header files in every directory it enters (which will it do by default)\&.
+.sp
+In case, you save or change files from mac clients, AD metadata files have to be written even in case you set this option\&. So you can\'t avoid the creation of \&.AppleDouble directories and its contents when you give macs write access to a share and they make use of it\&.
+.sp
+Try to avoid
+\fBnoadouble\fR
+whenever possible\&.
+.RE
+.PP
 nodev
-always use 0 for device number, helps when the device
-number is not constant across a reboot, cluster, ...
-.TP 
+.RS 4
+always use 0 for device number, helps when the device number is not constant across a reboot, cluster, \&.\&.\&.
+.RE
+.PP
 nofileid
-don't advertise createfileid, resolveid, deleteid
-calls.
-.TP 
+.RS 4
+don\'t advertise createfileid, resolveid, deleteid calls\&.
+.RE
+.PP
 nohex
-Disables :hex translations for anything except dot
-files. This option makes the \fB'/\fR' character
-illegal.
-.TP 
-prodos
-Provides compatibility with Apple II clients.
-.TP 
+.RS 4
+Disables :hex translations for anything except dot files\&. This option makes the
+\'/\' character illegal\&.
+.RE
+.PP
 nostat
-don't stat volume path when enumerating volumes list,
-useful for automounting or volumes created by a preexec
-script.
-.TP 
-upriv
-use AFP3 unix privileges. Become familiar with the new
-"unix privileges" AFP permissions concepts in MacOS X before
-using this option.
+.RS 4
+don\'t stat volume path when enumerating volumes list, useful for automounting or volumes created by a preexec script\&.
+.RE
+.PP
+prodos
+.RS 4
+Provides compatibility with Apple II clients\&.
+.RE
 .RE
 .SH "SEE ALSO"
-\fBafpd.conf\fR(5), \fBafpd\fR(8)
-
+.PP
+\fBafpd.conf\fR(5),
+\fBafpd\fR(8)