]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/file.c
small clean up, use of_ad when we can
[netatalk.git] / etc / afpd / file.c
index 88f1ad12cdc3c5045663a9c2c726db059c7c960a..fac35b7fa71a1e282d92dcb28a30f1b2f75e3f5c 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * $Id: file.c,v 1.77 2003-01-24 07:08:42 didg Exp $
+ * $Id: file.c,v 1.120 2009-11-02 14:35:27 didg Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
@@ -11,9 +11,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
 
 #include <stdio.h>
 #include <stdlib.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
 
 /* STDC check */
 #if STDC_HEADERS
 
 /* STDC check */
 #if STDC_HEADERS
@@ -24,6 +21,7 @@
 #define strrchr index
 #endif /* HAVE_STRCHR */
 char *strchr (), *strrchr ();
 #define strrchr index
 #endif /* HAVE_STRCHR */
 char *strchr (), *strrchr ();
+
 #ifndef HAVE_MEMCPY
 #define memcpy(d,s,n) bcopy ((s), (d), (n))
 #define memmove(d,s,n) bcopy ((s), (d), (n))
 #ifndef HAVE_MEMCPY
 #define memcpy(d,s,n) bcopy ((s), (d), (n))
 #define memmove(d,s,n) bcopy ((s), (d), (n))
@@ -31,26 +29,17 @@ char *strchr (), *strrchr ();
 #endif /* STDC_HEADERS */
 
 #include <utime.h>
 #endif /* STDC_HEADERS */
 
 #include <utime.h>
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif /* HAVE_FCNTL_H */
-#include <dirent.h>
-#include <sys/mman.h>
 #include <errno.h>
 #include <errno.h>
-
-#include <atalk/logger.h>
-#include <sys/types.h>
-#include <sys/time.h>
 #include <sys/param.h>
 #include <sys/param.h>
-#include <sys/stat.h>
 
 
-#include <netatalk/endian.h>
 #include <atalk/adouble.h>
 #include <atalk/adouble.h>
+#include <atalk/vfs.h>
+#include <atalk/logger.h>
 #include <atalk/afp.h>
 #include <atalk/util.h>
 #include <atalk/afp.h>
 #include <atalk/util.h>
-#ifdef CNID_DB
 #include <atalk/cnid.h>
 #include <atalk/cnid.h>
-#endif /* CNID_DB */
+#include <atalk/unix.h>
+
 #include "directory.h"
 #include "desktop.h"
 #include "volume.h"
 #include "directory.h"
 #include "desktop.h"
 #include "volume.h"
@@ -58,6 +47,7 @@ char *strchr (), *strrchr ();
 #include "file.h"
 #include "filedir.h"
 #include "globals.h"
 #include "file.h"
 #include "filedir.h"
 #include "globals.h"
+#include "unix.h"
 
 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
  * field         bytes        subfield    bytes
 
 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
  * field         bytes        subfield    bytes
@@ -78,45 +68,85 @@ char *strchr (), *strrchr ();
  *                           putawayID    4  home directory id
  */
 
  *                           putawayID    4  home directory id
  */
 
-const u_char ufinderi[] = {
-                              'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X',
+const u_char ufinderi[ADEDLEN_FINDERI] = {
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
+                              1, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0
                           };
 
                               0, 0, 0, 0, 0, 0, 0, 0,
                               0, 0, 0, 0, 0, 0, 0, 0
                           };
 
-/* FIXME mpath : unix or mac name ? (for now it's mac name ) */
-void *get_finderinfo(const char *mpath, struct adouble *adp, void *data)
+static const u_char old_ufinderi[] = {
+                              'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
+                          };
+
+/* ---------------------- 
+*/
+static int default_type(void *finder) 
+{
+    if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
+        return 1;
+    return 0;
+}
+
+/* FIXME path : unix or mac name ? (for now it's unix name ) */
+void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data)
 {
     struct extmap      *em;
 {
     struct extmap      *em;
+    void                *ad_finder = NULL;
+    int                 chk_ext = 0;
+    
+    if (adp)
+        ad_finder = ad_entry(adp, ADEID_FINDERI);
 
 
-    if (adp) {
-        memcpy(data, ad_entry(adp, ADEID_FINDERI), 32);
+    if (ad_finder) {
+        memcpy(data, ad_finder, ADEDLEN_FINDERI);
+        /* default type ? */
+        if (default_type(ad_finder)) 
+            chk_ext = 1;
     }
     else {
     }
     else {
-        memcpy(data, ufinderi, 32);
+        memcpy(data, ufinderi, ADEDLEN_FINDERI);
+        chk_ext = 1;
+        if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
+            u_int16_t ashort;
+            
+            ashort = htons(FINDERINFO_INVISIBLE);
+            memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
+        }
     }
     }
-
-    if ((!adp  || !memcmp(ad_entry(adp, ADEID_FINDERI),ufinderi , 8 )) 
-               && (em = getextmap( mpath ))
-    ) {
+    /** Only enter if no appledouble information and no finder information found. */
+    if (chk_ext && (em = getextmap( upath ))) {
         memcpy(data, em->em_type, sizeof( em->em_type ));
         memcpy(data, em->em_type, sizeof( em->em_type ));
-        memcpy(data + 4, em->em_creator, sizeof(em->em_creator));
+        memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
     }
     return data;
 }
 
 /* ---------------------
 */
     }
     return data;
 }
 
 /* ---------------------
 */
-char *set_name(char *data, const char *name, u_int32_t utf8) 
+char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8) 
 {
 {
-    u_int32_t           aint;
-
+    u_int32_t   aint;
+    char        *tp = NULL;
+    char        *src = name;
     aint = strlen( name );
 
     if (!utf8) {
     aint = strlen( name );
 
     if (!utf8) {
-        if (afp_version >= 30) {
-            /* the name is in utf8 */
+        /* want mac name */
+        if (utf8_encoding()) {
+            /* but name is an utf8 mac name */
+            char *u, *m;
+           
+            /* global static variable... */
+            tp = strdup(name);
+            if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
+               aint = 0;
+            }
+            else {
+                aint = strlen(m);
+                src = m;
+            }
+            
         }
         if (aint > MACFILELEN)
             aint = MACFILELEN;
         }
         if (aint > MACFILELEN)
             aint = MACFILELEN;
@@ -128,7 +158,7 @@ char *set_name(char *data, const char *name, u_int32_t utf8)
         if (aint > 255)  /* FIXME safeguard, anyway if no ascii char it's game over*/
            aint = 255;
 
         if (aint > 255)  /* FIXME safeguard, anyway if no ascii char it's game over*/
            aint = 255;
 
-        utf8 = htonl(utf8);
+        utf8 = vol->v_mac?htonl(vol->v_mac->kTextEncoding):0;         /* htonl(utf8) */
         memcpy(data, &utf8, sizeof(utf8));
         data += sizeof(utf8);
         
         memcpy(data, &utf8, sizeof(utf8));
         data += sizeof(utf8);
         
@@ -137,9 +167,12 @@ char *set_name(char *data, const char *name, u_int32_t utf8)
         data += sizeof(temp);
     }
 
         data += sizeof(temp);
     }
 
-    memcpy( data, name, aint );
+    memcpy( data, src, aint );
     data += aint;
     data += aint;
-
+    if (tp) {
+        strcpy(name, tp);
+        free(tp);
+    }
     return data;
 }
 
     return data;
 }
 
@@ -153,33 +186,94 @@ char *set_name(char *data, const char *name, u_int32_t utf8)
                                  (1 << FILPBIT_FINFO) |\
                                  (1 << FILPBIT_RFLEN) |\
                                  (1 << FILPBIT_EXTRFLEN) |\
                                  (1 << FILPBIT_FINFO) |\
                                  (1 << FILPBIT_RFLEN) |\
                                  (1 << FILPBIT_EXTRFLEN) |\
-                                 (1 << FILPBIT_PDINFO)))
+                                 (1 << FILPBIT_PDINFO) |\
+                                 (1 << FILPBIT_UNIXPR)))
+
+/* -------------------------- */
+u_int32_t get_id(struct vol *vol, struct adouble *adp,  const struct stat *st,
+             const cnid_t did, char *upath, const int len) 
+{
+u_int32_t aint = 0;
+
+#if AD_VERSION > AD_VERSION1
 
 
+    if ((aint = ad_getid(adp, st->st_dev, st->st_ino, did, vol->v_stamp))) {
+       return aint;
+    }
+#endif
 
 
+    if (vol->v_cdb != NULL) {
+           aint = cnid_add(vol->v_cdb, st, did, upath, len, aint);
+           /* Throw errors if cnid_add fails. */
+           if (aint == CNID_INVALID) {
+            switch (errno) {
+            case CNID_ERR_CLOSE: /* the db is closed */
+                break;
+            case CNID_ERR_PARAM:
+                LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
+                afp_errno = AFPERR_PARAM;
+                return CNID_INVALID;
+            case CNID_ERR_PATH:
+                afp_errno = AFPERR_PARAM;
+                return CNID_INVALID;
+            default:
+                afp_errno = AFPERR_MISC;
+                return CNID_INVALID;
+            }
+        }
+#if AD_VERSION > AD_VERSION1
+        else if (adp ) {
+            /* update the ressource fork
+             * for a folder adp is always null
+             */
+            if (ad_setid(adp, st->st_dev, st->st_ino, aint, did, vol->v_stamp)) {
+                ad_flush(adp);
+            }
+        }
+#endif    
+    }
+    return aint;
+}
+             
 /* -------------------------- */
 int getmetadata(struct vol *vol,
                  u_int16_t bitmap,
 /* -------------------------- */
 int getmetadata(struct vol *vol,
                  u_int16_t bitmap,
-                 char *path, struct dir *dir, struct stat *st,
-                 char *buf, int *buflen, struct adouble *adp, int attrbits )
+                 struct path *path, struct dir *dir, 
+                 char *buf, size_t *buflen, struct adouble *adp)
 {
 {
-#ifndef USE_LASTDID
-    struct stat                lst, *lstp;
-#endif /* USE_LASTDID */
     char               *data, *l_nameoff = NULL, *upath;
     char                *utf_nameoff = NULL;
     int                        bit = 0;
     u_int32_t          aint;
     char               *data, *l_nameoff = NULL, *upath;
     char                *utf_nameoff = NULL;
     int                        bit = 0;
     u_int32_t          aint;
+    cnid_t              id = 0;
     u_int16_t          ashort;
     u_char              achar, fdType[4];
     u_int32_t           utf8 = 0;
     u_int16_t          ashort;
     u_char              achar, fdType[4];
     u_int32_t           utf8 = 0;
+    struct stat         *st;
+    struct maccess     ma;
 
 #ifdef DEBUG
 
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin getmetadata:");
+    LOG(log_debug9, logtype_afpd, "begin getmetadata:");
 #endif /* DEBUG */
 
 #endif /* DEBUG */
 
-    upath = mtoupath(vol, path);
+    upath = path->u_name;
+    st = &path->st;
 
     data = buf;
 
     data = buf;
+
+    if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
+         || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
+         || (bitmap & (1 << FILPBIT_FNUM))) {
+        if (!path->id)
+            id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
+        else 
+            id = path->id;
+        if (id == 0)
+            return afp_errno;
+        if (!path->m_name) {
+            path->m_name = utompath(vol, upath, id, utf8_encoding());
+        }
+    }
     while ( bitmap != 0 ) {
         while (( bitmap & 1 ) == 0 ) {
             bitmap = bitmap>>1;
     while ( bitmap != 0 ) {
         while (( bitmap & 1 ) == 0 ) {
             bitmap = bitmap>>1;
@@ -190,7 +284,7 @@ int getmetadata(struct vol *vol,
         case FILPBIT_ATTR :
             if ( adp ) {
                 ad_getattr(adp, &ashort);
         case FILPBIT_ATTR :
             if ( adp ) {
                 ad_getattr(adp, &ashort);
-            } else if (*upath == '.') {
+            } else if (vol_inv_dots(vol) && *upath == '.') {
                 ashort = htons(ATTRBIT_INVISIBLE);
             } else
                 ashort = 0;
                 ashort = htons(ATTRBIT_INVISIBLE);
             } else
                 ashort = 0;
@@ -202,12 +296,10 @@ int getmetadata(struct vol *vol,
             if ((ma.ma_user & AR_UWRITE)) {
                accessmode( upath, &ma, dir , st);
                if (!(ma.ma_user & AR_UWRITE)) {
             if ((ma.ma_user & AR_UWRITE)) {
                accessmode( upath, &ma, dir , st);
                if (!(ma.ma_user & AR_UWRITE)) {
-                       attrbits |= ATTRBIT_NOWRITE;
+                       ashort |= htons(ATTRBIT_NOWRITE);
                 }
             }
 #endif
                 }
             }
 #endif
-            if (attrbits)
-                ashort = htons(ntohs(ashort) | attrbits);
             memcpy(data, &ashort, sizeof( ashort ));
             data += sizeof( ashort );
             break;
             memcpy(data, &ashort, sizeof( ashort ));
             data += sizeof( ashort );
             break;
@@ -244,15 +336,8 @@ int getmetadata(struct vol *vol,
             break;
 
         case FILPBIT_FINFO :
             break;
 
         case FILPBIT_FINFO :
-           get_finderinfo(path, adp, (char *)data);
-            if (!adp) {
-                if (*upath == '.') { /* make it invisible */
-                    ashort = htons(FINDERINFO_INVISIBLE);
-                    memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
-                }
-            }
-
-            data += 32;
+           get_finderinfo(vol, upath, adp, (char *)data);
+            data += ADEDLEN_FINDERI;
             break;
 
         case FILPBIT_LNAME :
             break;
 
         case FILPBIT_LNAME :
@@ -266,68 +351,8 @@ int getmetadata(struct vol *vol,
             break;
 
         case FILPBIT_FNUM :
             break;
 
         case FILPBIT_FNUM :
-            aint = 0;
-#if AD_VERSION > AD_VERSION1
-            /* look in AD v2 header */
-            if (adp)
-                memcpy(&aint, ad_entry(adp, ADEID_DID), sizeof(aint));
-#endif /* AD_VERSION > AD_VERSION1 */
-
-#ifdef CNID_DB
-            aint = cnid_add(vol->v_db, st, dir->d_did, upath,
-                            strlen(upath), aint);
-            /* Throw errors if cnid_add fails. */
-            if (aint == CNID_INVALID) {
-                switch (errno) {
-                case CNID_ERR_PARAM:
-                    LOG(log_error, logtype_afpd, "getfilparams: Incorrect parameters passed to cnid_add");
-                    return(AFPERR_PARAM);
-                case CNID_ERR_PATH:
-                    return(AFPERR_PARAM);
-                case CNID_ERR_DB:
-                case CNID_ERR_MAX:
-                    return(AFPERR_MISC);
-                }
-            }
-#endif /* CNID_DB */
-
-            if (aint == 0) {
-                /*
-                 * What a fucking mess.  First thing:  DID and FNUMs are
-                 * in the same space for purposes of enumerate (and several
-                 * other wierd places).  While we consider this Apple's bug,
-                 * this is the work-around:  In order to maintain constant and
-                 * unique DIDs and FNUMs, we monotonically generate the DIDs
-                 * during the session, and derive the FNUMs from the filesystem.
-                 * Since the DIDs are small, we insure that the FNUMs are fairly
-                 * large by setting thier high bits to the device number.
-                 *
-                 * AFS already does something very similar to this for the
-                 * inode number, so we don't repeat the procedure.
-                 *
-                 * new algorithm:
-                 * due to complaints over did's being non-persistent,
-                 * here's the current hack to provide semi-persistent
-                 * did's:
-                 *      1) we reserve the first bit for file ids.
-                 *      2) the next 7 bits are for the device.
-                 *      3) the remaining 24 bits are for the inode.
-                 *
-                 * both the inode and device information are actually hashes
-                 * that are then truncated to the requisite bit length.
-                 *
-                 * it should be okay to use lstat to deal with symlinks.
-                 */
-#ifdef USE_LASTDID
-                aint = htonl(( st->st_dev << 16 ) | (st->st_ino & 0x0000ffff));
-#else /* USE_LASTDID */
-                lstp = lstat(upath, &lst) < 0 ? st : &lst;
-                aint = htonl(CNID(lstp, 1));
-#endif /* USE_LASTDID */
-            }
-
-            memcpy(data, &aint, sizeof( aint ));
-            data += sizeof( aint );
+            memcpy(data, &id, sizeof( id ));
+            data += sizeof( id );
             break;
 
         case FILPBIT_DFLEN :
             break;
 
         case FILPBIT_DFLEN :
@@ -428,6 +453,40 @@ int getmetadata(struct vol *vol,
             memcpy(data, &aint, sizeof( aint ));
             data += sizeof( aint );
             break;
             memcpy(data, &aint, sizeof( aint ));
             data += sizeof( aint );
             break;
+        case FILPBIT_UNIXPR :
+            /* accessmode may change st_mode with ACLs */
+            accessmode( upath, &ma, dir , st);
+
+            aint = htonl(st->st_uid);
+            memcpy( data, &aint, sizeof( aint ));
+            data += sizeof( aint );
+            aint = htonl(st->st_gid);
+            memcpy( data, &aint, sizeof( aint ));
+            data += sizeof( aint );
+
+           /* FIXME: ugly hack
+               type == slnk indicates an OSX style symlink, 
+               we have to add S_IFLNK to the mode, otherwise
+               10.3 clients freak out. */
+
+           aint = st->st_mode;
+           if (adp) {
+               memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
+                if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
+                   aint |= S_IFLNK;
+               }
+           }
+            aint = htonl(aint);
+
+            memcpy( data, &aint, sizeof( aint ));
+            data += sizeof( aint );
+
+            *data++ = ma.ma_user;
+            *data++ = ma.ma_world;
+            *data++ = ma.ma_group;
+            *data++ = ma.ma_owner;
+            break;
+            
         default :
             return( AFPERR_BITMAP );
         }
         default :
             return( AFPERR_BITMAP );
         }
@@ -437,12 +496,12 @@ int getmetadata(struct vol *vol,
     if ( l_nameoff ) {
         ashort = htons( data - buf );
         memcpy(l_nameoff, &ashort, sizeof( ashort ));
     if ( l_nameoff ) {
         ashort = htons( data - buf );
         memcpy(l_nameoff, &ashort, sizeof( ashort ));
-        data = set_name(data, path, 0);
+        data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
     }
     if ( utf_nameoff ) {
         ashort = htons( data - buf );
         memcpy(utf_nameoff, &ashort, sizeof( ashort ));
     }
     if ( utf_nameoff ) {
         ashort = htons( data - buf );
         memcpy(utf_nameoff, &ashort, sizeof( ashort ));
-        data = set_name(data, path, utf8);
+        data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
     }
     *buflen = data - buf;
     return (AFP_OK);
     }
     *buflen = data - buf;
     return (AFP_OK);
@@ -452,68 +511,56 @@ int getmetadata(struct vol *vol,
 int getfilparams(struct vol *vol,
                  u_int16_t bitmap,
                  struct path *path, struct dir *dir, 
 int getfilparams(struct vol *vol,
                  u_int16_t bitmap,
                  struct path *path, struct dir *dir, 
-                 char *buf, int *buflen )
+                 char *buf, size_t *buflen )
 {
     struct adouble     ad, *adp;
 {
     struct adouble     ad, *adp;
-    struct ofork        *of;
-    char                   *upath;
-    u_int16_t          attrbits = 0;
     int                 opened = 0;
     int rc;    
 
 #ifdef DEBUG
     int                 opened = 0;
     int rc;    
 
 #ifdef DEBUG
-    LOG(log_info, logtype_default, "begin getfilparams:");
+    LOG(log_debug9, logtype_default, "begin getfilparams:");
 #endif /* DEBUG */
 
     opened = PARAM_NEED_ADP(bitmap);
     adp = NULL;
 #endif /* DEBUG */
 
     opened = PARAM_NEED_ADP(bitmap);
     adp = NULL;
+
     if (opened) {
     if (opened) {
+        char *upath;
+        int  flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
+
+        adp = of_ad(vol, path, &ad);
         upath = path->u_name;
         upath = path->u_name;
-        if ((of = of_findname(path))) {
-            adp = of->of_ad;
-           attrbits = ((of->of_ad->ad_df.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
-           attrbits |= ((of->of_ad->ad_hf.adf_refcount > of->of_ad->ad_df.adf_refcount)? ATTRBIT_ROPEN : 0);
-        } else {
-            memset(&ad, 0, sizeof(ad));
-            adp = &ad;
-        }
 
 
-        if ( ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, adp) < 0 ) {
-             adp = NULL;
+        if ( ad_metadata( upath, flags, adp) < 0 ) {
+            switch (errno) {
+            case EACCES:
+                LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
+                upath, strerror(errno));
+                return AFPERR_ACCESS;
+            case EIO:
+                LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
+                /* fall through */
+            case ENOENT:
+            default:
+                adp = NULL;
+                break;
+            }
         }
         }
-        else {
-           /* FIXME 
-              we need to check if the file is open by another process.
-              it's slow so we only do it if we have to:
-              - bitmap is requested.
-              - we don't already have the answer!
-           */
-           if ((bitmap & (1 << FILPBIT_ATTR))) {
-                if (!(attrbits & ATTRBIT_ROPEN)) {
-                }
-                if (!(attrbits & ATTRBIT_DOPEN)) {
-                }
-           }
-       }
     }
     }
-    rc = getmetadata(vol, bitmap, path->m_name, dir, &path->st, buf, buflen, adp, attrbits);
+    rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
     if ( adp ) {
     if ( adp ) {
-        ad_close( adp, ADFLAGS_HF );
+        ad_close_metadata( adp);
     }
 #ifdef DEBUG
     }
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end getfilparams:");
+    LOG(log_debug9, logtype_afpd, "end getfilparams:");
 #endif /* DEBUG */
 
     return( rc );
 }
 
 /* ----------------------------- */
 #endif /* DEBUG */
 
     return( rc );
 }
 
 /* ----------------------------- */
-int afp_createfile(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj      *obj;
-char   *ibuf, *rbuf;
-int            ibuflen, *rbuflen;
+int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
 {
-    struct stat         *st;
     struct adouble     ad, *adp;
     struct vol         *vol;
     struct dir         *dir;
     struct adouble     ad, *adp;
     struct vol         *vol;
     struct dir         *dir;
@@ -521,13 +568,8 @@ int                ibuflen, *rbuflen;
     char               *path, *upath;
     int                        creatf, did, openf, retvalue = AFP_OK;
     u_int16_t          vid;
     char               *path, *upath;
     int                        creatf, did, openf, retvalue = AFP_OK;
     u_int16_t          vid;
-    int                 ret;
     struct path                *s_path;
     
     struct path                *s_path;
     
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin afp_createfile:");
-#endif /* DEBUG */
-
     *rbuflen = 0;
     ibuf++;
     creatf = (unsigned char) *ibuf++;
     *rbuflen = 0;
     ibuf++;
     creatf = (unsigned char) *ibuf++;
@@ -550,7 +592,7 @@ int         ibuflen, *rbuflen;
     }
 
     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
     }
 
     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
-        return afp_errno;
+        return get_afp_errno(AFPERR_PARAM);
     }
 
     if ( *s_path->m_name == '\0' ) {
     }
 
     if ( *s_path->m_name == '\0' ) {
@@ -558,14 +600,12 @@ int               ibuflen, *rbuflen;
     }
 
     upath = s_path->u_name;
     }
 
     upath = s_path->u_name;
-    if (0 != (ret = check_name(vol, upath))) 
-       return  ret;
     
     /* if upath is deleted we already in trouble anyway */
     if ((of = of_findname(s_path))) {
         adp = of->of_ad;
     } else {
     
     /* if upath is deleted we already in trouble anyway */
     if ((of = of_findname(s_path))) {
         adp = of->of_ad;
     } else {
-        memset(&ad, 0, sizeof(ad));
+        ad_init(&ad, vol->v_adouble, vol->v_ad_options);
         adp = &ad;
     }
     if ( creatf) {
         adp = &ad;
     }
     if ( creatf) {
@@ -583,28 +623,41 @@ int               ibuflen, *rbuflen;
         openf = O_RDWR|O_CREAT|O_EXCL;
     }
 
         openf = O_RDWR|O_CREAT|O_EXCL;
     }
 
-    if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF,
+    if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
                   openf, 0666, adp) < 0 ) {
         switch ( errno ) {
                   openf, 0666, adp) < 0 ) {
         switch ( errno ) {
+        case EROFS:
+            return AFPERR_VLOCK;
+        case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
+            return ( AFPERR_NOOBJ );
         case EEXIST :
             return( AFPERR_EXIST );
         case EACCES :
             return( AFPERR_ACCESS );
         case EEXIST :
             return( AFPERR_EXIST );
         case EACCES :
             return( AFPERR_ACCESS );
-        case ENOENT:
-            /* on noadouble volumes, just creating the data fork is ok */
-            st = &s_path->st;
-            if (vol_noadouble(vol) && (stat(upath, st) == 0))
-                goto createfile_done;
-            /* fallthrough */
+        case EDQUOT:
+        case ENOSPC :
+            return( AFPERR_DFULL );
         default :
             return( AFPERR_PARAM );
         }
     }
         default :
             return( AFPERR_PARAM );
         }
     }
+    if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
+         /* on noadouble volumes, just creating the data fork is ok */
+         if (vol_noadouble(vol)) {
+             ad_close( adp, ADFLAGS_DF );
+             goto createfile_done;
+         }
+         /* FIXME with hard create on an existing file, we already
+          * corrupted the data file.
+          */
+         netatalk_unlink( upath );
+         ad_close( adp, ADFLAGS_DF );
+         return AFPERR_ACCESS;
+    }
+
     path = s_path->m_name;
     path = s_path->m_name;
-    ad_setentrylen( adp, ADEID_NAME, strlen( path ));
-    memcpy(ad_entry( adp, ADEID_NAME ), path,
-           ad_getentrylen( adp, ADEID_NAME ));
-    ad_flush( adp, ADFLAGS_DF|ADFLAGS_HF );
+    ad_setname(adp, path);
+    ad_flush( adp);
     ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
 
 createfile_done:
     ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
 
 createfile_done:
@@ -618,17 +671,10 @@ createfile_done:
 
     setvoltime(obj, vol );
 
 
     setvoltime(obj, vol );
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end afp_createfile");
-#endif /* DEBUG */
-
     return (retvalue);
 }
 
     return (retvalue);
 }
 
-int afp_setfilparams(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj      *obj;
-char   *ibuf, *rbuf;
-int            ibuflen, *rbuflen;
+int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
     struct vol *vol;
     struct dir *dir;
 {
     struct vol *vol;
     struct dir *dir;
@@ -636,10 +682,6 @@ int                ibuflen, *rbuflen;
     int                did, rc;
     u_int16_t  vid, bitmap;
 
     int                did, rc;
     u_int16_t  vid, bitmap;
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin afp_setfilparams:");
-#endif /* DEBUG */
-
     *rbuflen = 0;
     ibuf += 2;
 
     *rbuflen = 0;
     ibuf += 2;
 
@@ -663,13 +705,17 @@ int               ibuflen, *rbuflen;
     ibuf += sizeof( bitmap );
 
     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
     ibuf += sizeof( bitmap );
 
     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
-        return afp_errno;
+        return get_afp_errno(AFPERR_PARAM);
     }
 
     if (path_isadir(s_path)) {
         return( AFPERR_BADTYPE ); /* it's a directory */
     }
 
     }
 
     if (path_isadir(s_path)) {
         return( AFPERR_BADTYPE ); /* it's a directory */
     }
 
+    if ( s_path->st_errno != 0 ) {
+        return( AFPERR_NOOBJ );
+    }
+
     if ((u_long)ibuf & 1 ) {
         ibuf++;
     }
     if ((u_long)ibuf & 1 ) {
         ibuf++;
     }
@@ -678,132 +724,112 @@ int             ibuflen, *rbuflen;
         setvoltime(obj, vol );
     }
 
         setvoltime(obj, vol );
     }
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end afp_setfilparams:");
-#endif /* DEBUG */
-
     return( rc );
 }
 
 /*
  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
     return( rc );
 }
 
 /*
  * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic  
- *
+ * 
 */
 extern struct path Cur_Path;
 
 int setfilparams(struct vol *vol,
 */
 extern struct path Cur_Path;
 
 int setfilparams(struct vol *vol,
-                 struct path *path, u_int16_t bitmap, char *buf )
+                 struct path *path, u_int16_t f_bitmap, char *buf )
 {
     struct adouble     ad, *adp;
 {
     struct adouble     ad, *adp;
-    struct ofork        *of;
     struct extmap      *em;
     struct extmap      *em;
-    int                        bit = 0, isad = 1, err = AFP_OK;
+    int                        bit, isad = 1, err = AFP_OK;
     char                *upath;
     char                *upath;
-    u_char              achar, *fdType, xyy[4];
+    u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
     u_int16_t          ashort, bshort;
     u_int32_t          aint;
     u_int16_t          ashort, bshort;
     u_int32_t          aint;
+    u_int32_t          upriv;
+    u_int16_t           upriv_bit = 0;
+    
     struct utimbuf     ut;
 
     int                 change_mdate = 0;
     int                 change_parent_mdate = 0;
     int                 newdate = 0;
     struct timeval      tv;
     struct utimbuf     ut;
 
     int                 change_mdate = 0;
     int                 change_parent_mdate = 0;
     int                 newdate = 0;
     struct timeval      tv;
-
+    uid_t              f_uid;
+    gid_t              f_gid;
+    u_int16_t           bitmap = f_bitmap;
+    u_int32_t           cdate,bdate;
+    u_char              finder_buf[32];
 
 #ifdef DEBUG
 
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin setfilparams:");
+    LOG(log_debug9, logtype_afpd, "begin setfilparams:");
 #endif /* DEBUG */
 
 #endif /* DEBUG */
 
+    adp = of_ad(vol, path, &ad);
     upath = path->u_name;
     upath = path->u_name;
-    if ((of = of_findname(path))) {
-        adp = of->of_ad;
-    } else {
-        memset(&ad, 0, sizeof(ad));
-        adp = &ad;
-    }
 
 
-    if (check_access(upath, OPENACC_WR ) < 0) {
+    if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
         return AFPERR_ACCESS;
     }
 
         return AFPERR_ACCESS;
     }
 
-    if (ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF,
-                 O_RDWR|O_CREAT, 0666, adp) < 0) {
-        /* for some things, we don't need an adouble header */
-        if (bitmap & ~(1<<FILPBIT_MDATE)) {
-            return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
-        }
-        isad = 0;
-    } else if ((ad_getoflags( adp, ADFLAGS_HF ) & O_CREAT) ) {
-        ad_setentrylen( adp, ADEID_NAME, strlen( path->m_name ));
-        memcpy(ad_entry( adp, ADEID_NAME ), path->m_name,
-               ad_getentrylen( adp, ADEID_NAME ));
-    }
-
+    /* with unix priv maybe we have to change adouble file priv first */
+    bit = 0;
     while ( bitmap != 0 ) {
         while (( bitmap & 1 ) == 0 ) {
             bitmap = bitmap>>1;
             bit++;
         }
     while ( bitmap != 0 ) {
         while (( bitmap & 1 ) == 0 ) {
             bitmap = bitmap>>1;
             bit++;
         }
-
         switch(  bit ) {
         case FILPBIT_ATTR :
             change_mdate = 1;
             memcpy(&ashort, buf, sizeof( ashort ));
         switch(  bit ) {
         case FILPBIT_ATTR :
             change_mdate = 1;
             memcpy(&ashort, buf, sizeof( ashort ));
-            ad_getattr(adp, &bshort);
-            if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
-                bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
-            } else {
-                bshort &= ~ashort;
-            }
-            if ((ashort & htons(ATTRBIT_INVISIBLE)))
-                change_parent_mdate = 1;
-            ad_setattr(adp, bshort);
             buf += sizeof( ashort );
             break;
             buf += sizeof( ashort );
             break;
-
         case FILPBIT_CDATE :
             change_mdate = 1;
         case FILPBIT_CDATE :
             change_mdate = 1;
-            memcpy(&aint, buf, sizeof(aint));
-            ad_setdate(adp, AD_DATE_CREATE, aint);
-            buf += sizeof( aint );
+            memcpy(&cdate, buf, sizeof(cdate));
+            buf += sizeof( cdate );
             break;
             break;
-
         case FILPBIT_MDATE :
             memcpy(&newdate, buf, sizeof( newdate ));
             buf += sizeof( newdate );
             break;
         case FILPBIT_MDATE :
             memcpy(&newdate, buf, sizeof( newdate ));
             buf += sizeof( newdate );
             break;
-
         case FILPBIT_BDATE :
             change_mdate = 1;
         case FILPBIT_BDATE :
             change_mdate = 1;
-            memcpy(&aint, buf, sizeof(aint));
-            ad_setdate(adp, AD_DATE_BACKUP, aint);
-            buf += sizeof( aint );
+            memcpy(&bdate, buf, sizeof( bdate));
+            buf += sizeof( bdate );
             break;
             break;
-
         case FILPBIT_FINFO :
             change_mdate = 1;
         case FILPBIT_FINFO :
             change_mdate = 1;
-
-            if (!memcmp( ad_entry( adp, ADEID_FINDERI ), ufinderi, 8 )
-                    && ( 
-                     ((em = getextmap( path->m_name )) &&
-                      !memcmp(buf, em->em_type, sizeof( em->em_type )) &&
-                      !memcmp(buf + 4, em->em_creator,sizeof( em->em_creator)))
-                     || ((em = getdefextmap()) &&
-                      !memcmp(buf, em->em_type, sizeof( em->em_type )) &&
-                      !memcmp(buf + 4, em->em_creator,sizeof( em->em_creator)))
-            )) {
-                memcpy(buf, ufinderi, 8 );
-            }
-
-            memcpy(ad_entry( adp, ADEID_FINDERI ), buf, 32 );
+            memcpy(finder_buf, buf, 32 );
             buf += 32;
             break;
             buf += 32;
             break;
+        case FILPBIT_UNIXPR :
+            if (!vol_unix_priv(vol)) {
+               /* this volume doesn't use unix priv */
+               err = AFPERR_BITMAP;
+               bitmap = 0;
+               break;
+            }
+            change_mdate = 1;
+            change_parent_mdate = 1;
+
+            memcpy( &aint, buf, sizeof( aint ));
+            f_uid = ntohl (aint);
+            buf += sizeof( aint );
+            memcpy( &aint, buf, sizeof( aint ));
+            f_gid = ntohl (aint);
+            buf += sizeof( aint );
+            setfilowner(vol, f_uid, f_gid, path);
 
 
-            /* Client needs to set the ProDOS file info for this file.
-               Use a defined string for TEXT to support crlf
-               translations and convert all else into pXYY per Inside
-               Appletalk.  Always set the creator as "pdos".  Changes
-               from original by Marsha Jackson. */
+            memcpy( &upriv, buf, sizeof( upriv ));
+            buf += sizeof( upriv );
+            upriv = ntohl (upriv);
+            if ((upriv & S_IWUSR)) {
+               setfilunixmode(vol, path, upriv);
+            }
+            else {
+               /* do it later */
+               upriv_bit = 1;
+            }
+            break;
         case FILPBIT_PDINFO :
             if (afp_version < 30) { /* else it's UTF8 name */
                 achar = *buf;
         case FILPBIT_PDINFO :
             if (afp_version < 30) { /* else it's UTF8 name */
                 achar = *buf;
@@ -819,6 +845,89 @@ int setfilparams(struct vol *vol,
                    xyy[2] = *buf++;
                    fdType = xyy;
                }
                    xyy[2] = *buf++;
                    fdType = xyy;
                }
+                break;
+            }
+            /* fallthrough */
+        default :
+            err = AFPERR_BITMAP;
+            /* break while loop */
+            bitmap = 0;
+            break;
+        }
+
+        bitmap = bitmap>>1;
+        bit++;
+    }
+
+    /* second try with adouble open 
+    */
+    if ( ad_open_metadata( upath, vol_noadouble(vol), O_CREAT, adp) < 0) {
+        LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
+        /*
+         * For some things, we don't need an adouble header:
+         * - change of modification date
+         * - UNIX privs (Bug-ID #2863424)
+         */
+        if ( (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
+            LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
+            return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
+        }
+        LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
+        isad = 0;
+    } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
+        ad_setname(adp, path->m_name);
+    }
+    
+    bit = 0;
+    bitmap = f_bitmap;
+    while ( bitmap != 0 ) {
+        while (( bitmap & 1 ) == 0 ) {
+            bitmap = bitmap>>1;
+            bit++;
+        }
+
+        switch(  bit ) {
+        case FILPBIT_ATTR :
+            ad_getattr(adp, &bshort);
+            if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
+                (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
+                change_parent_mdate = 1;
+            if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
+                bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
+            } else {
+                bshort &= ~ashort;
+            }
+            ad_setattr(adp, bshort);
+            break;
+        case FILPBIT_CDATE :
+            ad_setdate(adp, AD_DATE_CREATE, cdate);
+            break;
+        case FILPBIT_MDATE :
+            break;
+        case FILPBIT_BDATE :
+            ad_setdate(adp, AD_DATE_BACKUP, bdate);
+            break;
+        case FILPBIT_FINFO :
+            if (default_type( ad_entry( adp, ADEID_FINDERI ))
+                    && ( 
+                     ((em = getextmap( path->m_name )) &&
+                      !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
+                      !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
+                     || ((em = getdefextmap()) &&
+                      !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
+                      !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
+            )) {
+                memcpy(finder_buf, ufinderi, 8 );
+            }
+            memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
+            break;
+        case FILPBIT_UNIXPR :
+            if (upriv_bit) {
+               setfilunixmode(vol, path, upriv);
+            }
+            break;
+        case FILPBIT_PDINFO :
+            if (afp_version < 30) { /* else it's UTF8 name */
                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
                 break;
                 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
                 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
                 break;
@@ -828,7 +937,6 @@ int setfilparams(struct vol *vol,
             err = AFPERR_BITMAP;
             goto setfilparam_done;
         }
             err = AFPERR_BITMAP;
             goto setfilparam_done;
         }
-
         bitmap = bitmap>>1;
         bit++;
     }
         bitmap = bitmap>>1;
         bit++;
     }
@@ -845,8 +953,8 @@ setfilparam_done:
     }
 
     if (isad) {
     }
 
     if (isad) {
-        ad_flush( adp, ADFLAGS_HF );
-        ad_close( adp, ADFLAGS_HF );
+        ad_flush( adp);
+        ad_close_metadata( adp);
 
     }
 
 
     }
 
@@ -857,7 +965,7 @@ setfilparam_done:
     }
 
 #ifdef DEBUG
     }
 
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end setfilparams:");
+    LOG(log_debug9, logtype_afpd, "end setfilparams:");
 #endif /* DEBUG */
     return err;
 }
 #endif /* DEBUG */
     return err;
 }
@@ -865,8 +973,6 @@ setfilparam_done:
 /*
  * renamefile and copyfile take the old and new unix pathnames
  * and the new mac name.
 /*
  * renamefile and copyfile take the old and new unix pathnames
  * and the new mac name.
- * NOTE: if we have to copy a file instead of renaming it, locks
- *       will break. Anyway it's an error because then we have 2 files.
  *
  * src         the source path 
  * dst         the dest filename in current dir
  *
  * src         the source path 
  * dst         the dest filename in current dir
@@ -874,25 +980,12 @@ setfilparam_done:
  * adp         adouble struct of src file, if open, or & zeroed one
  *
  */
  * adp         adouble struct of src file, if open, or & zeroed one
  *
  */
-int renamefile(src, dst, newname, noadouble, adp )
-char   *src, *dst, *newname;
-const int         noadouble;
-struct adouble    *adp;
+int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struct adouble *adp)
 {
 {
-    char               adsrc[ MAXPATHLEN + 1];
-    int                        len, rc;
-
-    /*
-     * Note that this is only checking the existance of the data file,
-     * not the header file.  The thinking is that if the data file doesn't
-     * exist, but the header file does, the right thing to do is remove
-     * the data file silently.
-     */
-
-    /* existence check moved to afp_moveandrename */
+    int                rc;
 
 #ifdef DEBUG
 
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin renamefile:");
+    LOG(log_debug9, logtype_afpd, "begin renamefile:");
 #endif /* DEBUG */
 
     if ( unix_rename( src, dst ) < 0 ) {
 #endif /* DEBUG */
 
     if ( unix_rename( src, dst ) < 0 ) {
@@ -905,69 +998,77 @@ struct adouble    *adp;
         case EROFS:
             return AFPERR_VLOCK;
         case EXDEV :                   /* Cross device move -- try copy */
         case EROFS:
             return AFPERR_VLOCK;
         case EXDEV :                   /* Cross device move -- try copy */
-            if (( rc = copyfile(src, dst, newname, noadouble )) != AFP_OK ) {
-                deletefile( dst, 0 );
+           /* NOTE: with open file it's an error because after the copy we will 
+            * get two files, it's fixable for our process (eg reopen the new file, get the
+            * locks, and so on. But it doesn't solve the case with a second process
+            */
+           if (adp->ad_open_forks) {
+               /* FIXME  warning in syslog so admin'd know there's a conflict ?*/
+               return AFPERR_OLOCK; /* little lie */
+           }
+            if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) {
+                /* on error copyfile delete dest */
                 return( rc );
             }
                 return( rc );
             }
-            return deletefile( src, 0);
+            return deletefile(vol, src, 0);
         default :
             return( AFPERR_PARAM );
         }
     }
 
         default :
             return( AFPERR_PARAM );
         }
     }
 
-    strcpy( adsrc, ad_path( src, 0 ));
-    rc = 0;
-rename_retry:
-    if (unix_rename( adsrc, ad_path( dst, 0 )) < 0 ) {
-        struct stat st;
-
-        switch ( errno ) {
-        case ENOENT :
-            /* check for a source appledouble header. if it exists, make
-             * a dest appledouble directory and do the rename again. */
-            if (rc || stat(adsrc, &st) ||
-                    (ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, adp) < 0))
-                return AFP_OK;
-            rc++;
-            ad_close(adp, ADFLAGS_HF);
-            goto rename_retry;
-        case EPERM:
-        case EACCES :
-            return( AFPERR_ACCESS );
-        case EROFS:
-            return AFPERR_VLOCK;
-        default :
-            return( AFPERR_PARAM );
+    if (vol->vfs->vfs_renamefile(vol, src, dst) < 0 ) {
+        int err;
+        
+        err = errno;        
+       /* try to undo the data fork rename,
+        * we know we are on the same device 
+       */
+       if (err) {
+           unix_rename( dst, src ); 
+           /* return the first error */
+           switch ( err) {
+            case ENOENT :
+                return AFPERR_NOOBJ;
+            case EPERM:
+            case EACCES :
+                return AFPERR_ACCESS ;
+            case EROFS:
+                return AFPERR_VLOCK;
+            default :
+                return AFPERR_PARAM ;
+            }
         }
     }
 
         }
     }
 
-    if ( ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp) < 0 ) {
-        switch ( errno ) {
-        case ENOENT :
-            return( AFPERR_NOOBJ );
-        case EACCES :
-            return( AFPERR_ACCESS );
-        case EROFS:
-            return AFPERR_VLOCK;
-        default :
-            return( AFPERR_PARAM );
-        }
+    /* don't care if we can't open the newly renamed ressource fork
+     */
+    if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
+        ad_setname(adp, newname);
+        ad_flush( adp );
+        ad_close( adp, ADFLAGS_HF );
     }
     }
-
-    len = strlen( newname );
-    ad_setentrylen( adp, ADEID_NAME, len );
-    memcpy(ad_entry( adp, ADEID_NAME ), newname, len );
-    ad_flush( adp, ADFLAGS_HF );
-    ad_close( adp, ADFLAGS_HF );
-
 #ifdef DEBUG
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end renamefile:");
+    LOG(log_debug9, logtype_afpd, "end renamefile:");
 #endif /* DEBUG */
 
     return( AFP_OK );
 }
 
 #endif /* DEBUG */
 
     return( AFP_OK );
 }
 
-int copy_path_name(char *newname, char *ibuf)
+/* ---------------- 
+   convert a Mac long name to an utf8 name,
+*/
+size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
+{
+size_t    outlen;
+
+    if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
+       return -1;
+    }
+    return outlen;
+}
+
+/* ---------------- */
+int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
 {
 char        type = *ibuf;
 size_t      plen = 0;
 {
 char        type = *ibuf;
 size_t      plen = 0;
@@ -981,8 +1082,16 @@ u_int32_t   hint;
     switch (type) {
     case 2:
         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
     switch (type) {
     case 2:
         if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
-            strncpy( newname, ibuf, plen );
-            newname[ plen ] = '\0';
+            if (afp_version >= 30) {
+                /* convert it to UTF8 
+                */
+                if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
+                   return -1;
+            }
+            else {
+                strncpy( newname, ibuf, plen );
+                newname[ plen ] = '\0';
+            }
             if (strlen(newname) != plen) {
                 /* there's \0 in newname, e.g. it's a pathname not
                  * only a filename. 
             if (strlen(newname) != plen) {
                 /* there's \0 in newname, e.g. it's a pathname not
                  * only a filename. 
@@ -998,10 +1107,14 @@ u_int32_t   hint;
         memcpy(&len16, ibuf, sizeof(len16));
         ibuf += sizeof(len16);
         plen = ntohs(len16);
         memcpy(&len16, ibuf, sizeof(len16));
         ibuf += sizeof(len16);
         plen = ntohs(len16);
+        
         if (plen) {
         if (plen) {
+            if (plen > AFPOBJ_TMPSIZ) {
+               return -1;
+            }
             strncpy( newname, ibuf, plen );
             newname[ plen ] = '\0';
             strncpy( newname, ibuf, plen );
             newname[ plen ] = '\0';
-            if (strchr(newname,'/')) {
+            if (strlen(newname) != plen) {
                return -1;
             }
         }
                return -1;
             }
         }
@@ -1012,12 +1125,9 @@ u_int32_t   hint;
 
 /* -----------------------------------
 */
 
 /* -----------------------------------
 */
-int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj      *obj;
-char   *ibuf, *rbuf;
-int            ibuflen, *rbuflen;
+int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
 {
-    struct vol *vol;
+    struct vol *s_vol, *d_vol;
     struct dir *dir;
     char       *newname, *p, *upath;
     struct path *s_path;
     struct dir *dir;
     char       *newname, *p, *upath;
     struct path *s_path;
@@ -1025,22 +1135,21 @@ int             ibuflen, *rbuflen;
     int         err, retvalue = AFP_OK;
     u_int16_t  svid, dvid;
 
     int         err, retvalue = AFP_OK;
     u_int16_t  svid, dvid;
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin afp_copyfile:");
-#endif /* DEBUG */
-
+    struct adouble ad, *adp;
+    int denyreadset;
+    
     *rbuflen = 0;
     ibuf += 2;
 
     memcpy(&svid, ibuf, sizeof( svid ));
     ibuf += sizeof( svid );
     *rbuflen = 0;
     ibuf += 2;
 
     memcpy(&svid, ibuf, sizeof( svid ));
     ibuf += sizeof( svid );
-    if (NULL == ( vol = getvolbyvid( svid )) ) {
+    if (NULL == ( s_vol = getvolbyvid( svid )) ) {
         return( AFPERR_PARAM );
     }
 
     memcpy(&sdid, ibuf, sizeof( sdid ));
     ibuf += sizeof( sdid );
         return( AFPERR_PARAM );
     }
 
     memcpy(&sdid, ibuf, sizeof( sdid ));
     ibuf += sizeof( sdid );
-    if (NULL == ( dir = dirlookup( vol, sdid )) ) {
+    if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
         return afp_errno;
     }
 
         return afp_errno;
     }
 
@@ -1049,8 +1158,8 @@ int               ibuflen, *rbuflen;
     memcpy(&ddid, ibuf, sizeof( ddid ));
     ibuf += sizeof( ddid );
 
     memcpy(&ddid, ibuf, sizeof( ddid ));
     ibuf += sizeof( ddid );
 
-    if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
-        return afp_errno;
+    if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
+        return get_afp_errno(AFPERR_PARAM);
     }
     if ( path_isadir(s_path) ) {
         return( AFPERR_BADTYPE );
     }
     if ( path_isadir(s_path) ) {
         return( AFPERR_BADTYPE );
@@ -1062,13 +1171,22 @@ int             ibuflen, *rbuflen;
      *      and locks need to stay coherent. as a result,
      *      we just balk if the file is opened already. */
 
      *      and locks need to stay coherent. as a result,
      *      we just balk if the file is opened already. */
 
-    newname = obj->newtmp;
-    strcpy( newname, s_path->m_name );
+    adp = of_ad(s_vol, s_path, &ad);
 
 
-    if (of_findname(s_path))
+    if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
+        return AFPERR_DENYCONF;
+    }
+    denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
+                  getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
+    ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
+    if (denyreadset) {
         return AFPERR_DENYCONF;
         return AFPERR_DENYCONF;
+    }
+
+    newname = obj->newtmp;
+    strcpy( newname, s_path->m_name );
 
 
-    p = ctoupath( vol, curdir, newname );
+    p = ctoupath( s_vol, curdir, newname );
     if (!p) {
         return AFPERR_PARAM;
     
     if (!p) {
         return AFPERR_PARAM;
     
@@ -1076,31 +1194,35 @@ int             ibuflen, *rbuflen;
 #ifdef FORCE_UIDGID
     /* FIXME svid != dvid && dvid's user can't read svid */
 #endif
 #ifdef FORCE_UIDGID
     /* FIXME svid != dvid && dvid's user can't read svid */
 #endif
-    if (NULL == ( vol = getvolbyvid( dvid )) ) {
+    if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
         return( AFPERR_PARAM );
     }
 
         return( AFPERR_PARAM );
     }
 
-    if (vol->v_flags & AFPVOL_RO)
+    if (d_vol->v_flags & AFPVOL_RO)
         return AFPERR_VLOCK;
 
         return AFPERR_VLOCK;
 
-    if (NULL == ( dir = dirlookup( vol, ddid )) ) {
+    if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
         return afp_errno;
     }
 
         return afp_errno;
     }
 
-    if (( s_path = cname( vol, dir, &ibuf )) == NULL ) {
-        return afp_errno;
+    if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
+        return get_afp_errno(AFPERR_NOOBJ); 
     }
     if ( *s_path->m_name != '\0' ) {
     }
     if ( *s_path->m_name != '\0' ) {
-        return (path_isadir( s_path))? AFPERR_PARAM:AFPERR_BADTYPE ;
+       path_error(s_path, AFPERR_PARAM);
     }
 
     /* one of the handful of places that knows about the path type */
     }
 
     /* one of the handful of places that knows about the path type */
-    if (copy_path_name(newname, ibuf) < 0) {
+    if (copy_path_name(d_vol, newname, ibuf) < 0) {
         return( AFPERR_PARAM );
     }
         return( AFPERR_PARAM );
     }
-
-    upath = mtoupath(vol, newname);
-    if ( (err = copyfile(p, upath , newname, vol_noadouble(vol))) < 0 ) {
+    /* newname is always only a filename so curdir *is* its
+     * parent folder
+    */
+    if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
+        return( AFPERR_PARAM );
+    }
+    if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
         return err;
     }
     curdir->offcnt++;
         return err;
     }
     curdir->offcnt++;
@@ -1111,23 +1233,19 @@ int             ibuflen, *rbuflen;
     }
 #endif /* DROPKLUDGE */
 
     }
 #endif /* DROPKLUDGE */
 
-    setvoltime(obj, vol );
-
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end afp_copyfile:");
-#endif /* DEBUG */
+    setvoltime(obj, d_vol );
 
     return( retvalue );
 }
 
 
     return( retvalue );
 }
 
-
-static __inline__ int copy_all(const int dfd, const void *buf,
+/* ----------------------- */
+static int copy_all(const int dfd, const void *buf,
                                size_t buflen)
 {
     ssize_t cc;
 
 #ifdef DEBUG
                                size_t buflen)
 {
     ssize_t cc;
 
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin copy_all:");
+    LOG(log_debug9, logtype_afpd, "begin copy_all:");
 #endif /* DEBUG */
 
     while (buflen > 0) {
 #endif /* DEBUG */
 
     while (buflen > 0) {
@@ -1135,273 +1253,274 @@ static __inline__ int copy_all(const int dfd, const void *buf,
             switch (errno) {
             case EINTR:
                 continue;
             switch (errno) {
             case EINTR:
                 continue;
-            case EDQUOT:
-            case EFBIG:
-            case ENOSPC:
-                return AFPERR_DFULL;
-            case EROFS:
-                return AFPERR_VLOCK;
             default:
             default:
-                return AFPERR_PARAM;
+                return -1;
             }
         }
         buflen -= cc;
     }
 
 #ifdef DEBUG
             }
         }
         buflen -= cc;
     }
 
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end copy_all:");
+    LOG(log_debug9, logtype_afpd, "end copy_all:");
 #endif /* DEBUG */
 
 #endif /* DEBUG */
 
-    return AFP_OK;
+    return 0;
 }
 
 }
 
-/* XXX: this needs to use ad_open and ad_lock. so, we need to
- * pass in vol and path */
-int copyfile(src, dst, newname, noadouble )
-char   *src, *dst, *newname;
-const int   noadouble;
+/* -------------------------- 
+ * copy only the fork data stream
+*/
+static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
 {
 {
-    struct adouble     ad;
-#ifdef SENDFILE_FLAVOR_LINUX
+    ssize_t cc;
+    int     err = 0;
+    char    filebuf[8192];
+    int     sfd, dfd;
+    
+    if (eid == ADEID_DFORK) {
+        sfd = ad_data_fileno(ads);
+        dfd = ad_data_fileno(add);
+    }
+    else {
+        sfd = ad_reso_fileno(ads);
+        dfd = ad_reso_fileno(add);
+    }        
+
+    if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
+       return -1;
+
+    if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
+       return -1;
+       
+#if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
+    /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
+    off_t   offset = 0;
+    size_t  size;
     struct stat         st;
     struct stat         st;
-#endif
-    char               filebuf[8192];
-    int                        sfd, dfd, len, err = AFP_OK;
-    ssize_t             cc;
-    char                dpath[ MAXPATHLEN + 1];
-    int                 admode;
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin copyfile:");
-#endif /* DEBUG */
+    #define BUF 128*1024*1024
 
 
-    strcpy(dpath, ad_path( dst, ADFLAGS_HF ));
-    admode = ad_mode( dst, 0666 );
-    if (newname) {
-        if ((sfd = open( ad_path( src, ADFLAGS_HF ), O_RDONLY, 0 )) < 0 ) {
-            switch ( errno ) {
-            case ENOENT :
-                break; /* just copy the data fork */
-            case EACCES :
-                return( AFPERR_ACCESS );
-            default :
-                return( AFPERR_PARAM );
-            }
-        } else {
-            if (( dfd = open( dpath, O_WRONLY|O_CREAT,ad_hf_mode(admode))) < 0 ) {
-                close( sfd );
-                switch ( errno ) {
-                case ENOENT :
-                    return( AFPERR_NOOBJ );
-                case EACCES :
-                    return( AFPERR_ACCESS );
-                case EROFS:
-                    return AFPERR_VLOCK;
-                default :
-                    return( AFPERR_PARAM );
-                }
+    if (fstat(sfd, &st) == 0) {
+        
+        while (1) {
+            if ( offset >= st.st_size) {
+               return 0;
             }
             }
-
-            /* copy the file */
-#ifdef SENDFILE_FLAVOR_LINUX
-            if (fstat(sfd, &st) == 0) {
-                if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
-                    switch (errno) {
-                    case EDQUOT:
-                    case EFBIG:
-                    case ENOSPC:
-                        err = AFPERR_DFULL;
-                        break;
-                    case EROFS:
-                        err = AFPERR_VLOCK;
-                        break;
-                    default:
-                        err = AFPERR_PARAM;
-                    }
+            size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
+            if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
+                switch (errno) {
+                case ENOSYS:
+                case EINVAL:  /* there's no guarantee that all fs support sendfile */
+                    goto no_sendfile;
+                default:
+                    return -1;
                 }
                 }
-                goto copyheader_done;
             }
             }
-#endif /* SENDFILE_FLAVOR_LINUX */
-            while (1) {
-                if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
-                    if (errno == EINTR)
-                        continue;
-                    err = AFPERR_PARAM;
-                    break;
-                }
+        }
+    }
+    no_sendfile:
+    lseek(sfd, offset, SEEK_SET);
+#endif 
 
 
-                if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0))
-                    break;
-            }
+    while (1) {
+        if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
+            if (errno == EINTR)
+                continue;
+            err = -1;
+            break;
+        }
 
 
-copyheader_done:
-            close(sfd);
-            close(dfd);
-            if (err < 0) {
-                unlink(dpath);
-                return err;
-            }
+        if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
+            break;
         }
     }
         }
     }
+    return err;
+}
 
 
-    /* data fork copying */
-    if (( sfd = open( src, O_RDONLY, 0 )) < 0 ) {
-        switch ( errno ) {
-        case ENOENT :
-            return( AFPERR_NOOBJ );
-        case EACCES :
-            return( AFPERR_ACCESS );
-        default :
-            return( AFPERR_PARAM );
-        }
+/* ----------------------------------
+ * if newname is NULL (from directory.c) we don't want to copy the resource fork.
+ * because we are doing it elsewhere.
+ * currently if newname is NULL then adp is NULL. 
+ */
+int copyfile(const struct vol *s_vol, const struct vol*d_vol, 
+    char *src, char *dst, char *newname, struct adouble *adp)
+{
+    struct adouble     ads, add;
+    int                        err = 0;
+    int                 ret_err = 0;
+    int                 adflags;
+    int                 stat_result;
+    struct stat         st;
+    
+#ifdef DEBUG
+    LOG(log_debug9, logtype_afpd, "begin copyfile:");
+#endif /* DEBUG */
+
+    if (adp == NULL) {
+        ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options); 
+        adp = &ads;
+    }
+    ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
+    adflags = ADFLAGS_DF;
+    if (newname) {
+        adflags |= ADFLAGS_HF;
     }
 
     }
 
-    if (( dfd = open( dst, O_WRONLY|O_CREAT, admode)) < 0 ) {
-        close( sfd );
-        switch ( errno ) {
-        case ENOENT :
-            return( AFPERR_NOOBJ );
-        case EACCES :
-            return( AFPERR_ACCESS );
-        case EROFS:
-            return AFPERR_VLOCK;
-        default :
-            return( AFPERR_PARAM );
-        }
+    if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
+        ret_err = errno;
+        goto done;
     }
 
     }
 
-#ifdef SENDFILE_FLAVOR_LINUX
-    if (fstat(sfd, &st) == 0) {
-        if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) {
-            switch (errno) {
-            case EDQUOT:
-            case EFBIG:
-            case ENOSPC:
-                err = AFPERR_DFULL;
-                break;
-            default:
-                err = AFPERR_PARAM;
-            }
-        }
-        goto copydata_done;
+    if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
+        /* no resource fork, don't create one for dst file */
+        adflags &= ~ADFLAGS_HF;
     }
     }
-#endif /* SENDFILE_FLAVOR_LINUX */
 
 
-    while (1) {
-        if ((cc = read( sfd, filebuf, sizeof( filebuf ))) < 0) {
-            if (errno == EINTR)
-                continue;
+    stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
 
 
-            err = AFPERR_PARAM;
-            break;
-        }
+    if (stat_result < 0) {           
+      /* unlikely but if fstat fails, the default file mode will be 0666. */
+      st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+    }
 
 
-        if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
-            break;
+    if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
+        ret_err = errno;
+        ad_close( adp, adflags );
+        if (EEXIST != ret_err) {
+            deletefile(d_vol, dst, 0);
+            goto done;
+        }
+        return AFPERR_EXIST;
+    }
+    
+    /*
+     * XXX if the source and the dest don't use the same resource type it's broken
+     */
+    if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
+        /* copy the data fork */
+        if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
+            err = d_vol->vfs->vfs_copyfile(d_vol, src, dst);
         }
     }
 
         }
     }
 
-copydata_done:
-    close(sfd);
-    close(dfd);
     if (err < 0) {
     if (err < 0) {
-        unlink(dpath);
-        unlink(dst);
-        return err;
+       ret_err = errno;
     }
 
     }
 
-    if (newname) {
-        memset(&ad, 0, sizeof(ad));
-        if ( ad_open( dst, noadouble | ADFLAGS_HF, O_RDWR|O_CREAT,
-                      0666, &ad) < 0 ) {
-            switch ( errno ) {
-            case ENOENT :
-                return noadouble ? AFP_OK : AFPERR_NOOBJ;
-            case EACCES :
-                return( AFPERR_ACCESS );
-            case EROFS:
-                return AFPERR_VLOCK;
-            default :
-                return( AFPERR_PARAM );
-            }
-        }
+    if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
+        /* set the new name in the resource fork */
+        ad_copy_header(&add, adp);
+        ad_setname(&add, newname);
+        ad_flush( &add );
+    }
+    ad_close( adp, adflags );
+
+    if (ad_close( &add, adflags ) <0) {
+       ret_err = errno;
+    } 
 
 
-        len = strlen( newname );
-        ad_setentrylen( &ad, ADEID_NAME, len );
-        memcpy(ad_entry( &ad, ADEID_NAME ), newname, len );
-        ad_flush( &ad, ADFLAGS_HF );
-        ad_close( &ad, ADFLAGS_HF );
+    if (ret_err) {
+        deletefile(d_vol, dst, 0);
+    }
+    else if (stat_result == 0) {
+        /* set dest modification date to src date */
+        struct utimbuf ut;
+
+       ut.actime = ut.modtime = st.st_mtime;
+       utime(dst, &ut);
+       /* FIXME netatalk doesn't use resource fork file date
+        * but maybe we should set its modtime too.
+       */
     }
 
 #ifdef DEBUG
     }
 
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end copyfile:");
+    LOG(log_debug9, logtype_afpd, "end copyfile:");
 #endif /* DEBUG */
 
 #endif /* DEBUG */
 
-    return( AFP_OK );
+done:
+    switch ( ret_err ) {
+    case 0:
+        return AFP_OK;
+    case EDQUOT:
+    case EFBIG:
+    case ENOSPC:
+        return AFPERR_DFULL;
+    case ENOENT:
+        return AFPERR_NOOBJ;
+    case EACCES:
+        return AFPERR_ACCESS;
+    case EROFS:
+        return AFPERR_VLOCK;
+    }
+    return AFPERR_PARAM;
 }
 
 
 /* -----------------------------------
 }
 
 
 /* -----------------------------------
-   checkAttrib:   1 check kFPDeleteInhibitBit 
-   ie deletfile called by afp_delete
+   vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
+   checkAttrib:   1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
 
    when deletefile is called we don't have lock on it, file is closed (for us)
    untrue if called by renamefile
 
    when deletefile is called we don't have lock on it, file is closed (for us)
    untrue if called by renamefile
+   
+   ad_open always try to open file RDWR first and ad_lock takes care of
+   WRITE lock on read only file.
 */
 */
-int deletefile( file, checkAttrib )
-char           *file;
-int         checkAttrib;
+
+static int check_attrib(struct adouble *adp)
+{
+u_int16_t   bshort = 0;
+
+       ad_getattr(adp, &bshort);
+    /*
+     * Does kFPDeleteInhibitBit (bit 8) set?
+     */
+       if ((bshort & htons(ATTRBIT_NODELETE))) {
+               return AFPERR_OLOCK;
+       }
+    if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
+       return AFPERR_BUSY;
+       }
+       return 0;
+}
+
+int deletefile(const struct vol *vol, char *file, int checkAttrib)
 {
     struct adouble     ad;
 {
     struct adouble     ad;
+    struct adouble      *adp = &ad;
     int                        adflags, err = AFP_OK;
     int                        adflags, err = AFP_OK;
-    int                        locktype = ADLOCK_WR;
-    int                        openmode = O_RDWR;
 
 #ifdef DEBUG
 
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin deletefile:");
+    LOG(log_debug9, logtype_afpd, "begin deletefile:");
 #endif /* DEBUG */
 
 #endif /* DEBUG */
 
+    /* try to open both forks at once */
+    adflags = ADFLAGS_DF|ADFLAGS_HF;
+    if (checkAttrib) {
+        /* was EACCESS error try to get only metadata */
+        ad_init(&ad, vol->v_adouble, vol->v_ad_options);
+        if ( ad_metadata( file , ADFLAGS_OPENFORKS, &ad) == 0 ) {
+            ad_close( &ad, adflags );
+            if ((err = check_attrib(&ad))) {
+               return err;
+            }
+        }
+    }
     while(1) {
     while(1) {
-        /*
-         * If can't open read/write then try again read-only.  If it's open
-         * read-only, we must do a read lock instead of a write lock.
-         */
-        /* try to open both at once */
-        adflags = ADFLAGS_DF|ADFLAGS_HF;
-        memset(&ad, 0, sizeof(ad));
-        if ( ad_open( file, adflags, openmode, 0, &ad ) < 0 ) {
+       ad_init(&ad, vol->v_adouble, vol->v_ad_options);  /* OK */
+        if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) {
             switch (errno) {
             case ENOENT:
             switch (errno) {
             case ENOENT:
-                adflags = ADFLAGS_DF;
+                if (adflags == ADFLAGS_DF)
+                    return AFPERR_NOOBJ;
+                   
                 /* that failed. now try to open just the data fork */
                 /* that failed. now try to open just the data fork */
-                memset(&ad, 0, sizeof(ad));
-                if ( ad_open( file, adflags, openmode, 0, &ad ) < 0 ) {
-                    switch (errno) {
-                    case ENOENT:
-                        return AFPERR_NOOBJ;
-                    case EACCES:
-                        if(openmode == O_RDWR) {
-                            openmode = O_RDONLY;
-                            locktype = ADLOCK_RD;
-                            continue;
-                        } else {
-                            return AFPERR_ACCESS;
-                        }
-                    case EROFS:
-                        return AFPERR_VLOCK;
-                    default:
-                        return AFPERR_PARAM;
-                    }
-                }
-                break;
+                adflags = ADFLAGS_DF;
+                continue;
 
             case EACCES:
 
             case EACCES:
-                if(openmode == O_RDWR) {
-                    openmode = O_RDONLY;
-                    locktype = ADLOCK_RD;
-                    continue;
-                } else {
-                    return AFPERR_ACCESS;
-                }
+                adp = NULL; /* maybe it's a file with no write mode for us */
+                break;      /* was return AFPERR_ACCESS;*/
             case EROFS:
                 return AFPERR_VLOCK;
             default:
             case EROFS:
                 return AFPERR_VLOCK;
             default:
@@ -1410,20 +1529,8 @@ int         checkAttrib;
         }
         break; /* from the while */
     }
         }
         break; /* from the while */
     }
-    /*
-     * Does kFPDeleteInhibitBit (bit 8) set?
-     */
-    if (checkAttrib && (adflags & ADFLAGS_HF)) {
-        u_int16_t   bshort;
 
 
-        ad_getattr(&ad, &bshort);
-        if ((bshort & htons(ATTRBIT_NODELETE))) {
-            ad_close( &ad, adflags );
-            return(AFPERR_OLOCK);
-        }
-    }
-    
-    if ((adflags & ADFLAGS_HF) ) {
+    if (adp && (adflags & ADFLAGS_HF) ) {
         /* FIXME we have a pb here because we want to know if a file is open 
          * there's a 'priority inversion' if you can't open the ressource fork RW
          * you can delete it if it's open because you can't get a write lock.
         /* FIXME we have a pb here because we want to know if a file is open 
          * there's a 'priority inversion' if you can't open the ressource fork RW
          * you can delete it if it's open because you can't get a write lock.
@@ -1431,78 +1538,39 @@ int         checkAttrib;
          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
          * metadatas
          *
          * ADLOCK_FILELOCK means the whole ressource fork, not only after the 
          * metadatas
          *
-         * FIXME it doesn't for RFORK open read only and fork open without deny mode
+         * FIXME it doesn't work for RFORK open read only and fork open without deny mode
          */
          */
-        if (ad_tmplock(&ad, ADEID_RFORK, locktype |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
+        if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
             ad_close( &ad, adflags );
             return( AFPERR_BUSY );
         }
     }
 
             ad_close( &ad, adflags );
             return( AFPERR_BUSY );
         }
     }
 
-    if (ad_tmplock( &ad, ADEID_DFORK, locktype, 0, 0, 0 ) < 0) {
+    if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
         err = AFPERR_BUSY;
         err = AFPERR_BUSY;
-        goto delete_unlock;
     }
     }
-
-    if ( unlink( ad_path( file, ADFLAGS_HF )) < 0 ) {
-        switch ( errno ) {
-        case EPERM:
-        case EACCES :
-            err = AFPERR_ACCESS;
-            goto delete_unlock;
-        case EROFS:
-            err = AFPERR_VLOCK;
-            goto delete_unlock;
-        case ENOENT :
-            break;
-        default :
-            err = AFPERR_PARAM;
-            goto delete_unlock;
+    else if (!(err = vol->vfs->vfs_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
+        cnid_t id;
+        if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) 
+        {
+            cnid_delete(vol->v_cdb, id);
         }
     }
         }
     }
-
-    if ( unlink( file ) < 0 ) {
-        switch ( errno ) {
-        case EPERM:
-        case EACCES :
-            err = AFPERR_ACCESS;
-            break;
-        case EROFS:
-            err = AFPERR_VLOCK;
-            break;
-        case ENOENT :
-            break;
-        default :
-            err = AFPERR_PARAM;
-            break;
-        }
-    }
-
-delete_unlock:
-    if (adflags & ADFLAGS_HF)
-        ad_tmplock(&ad, ADEID_RFORK, ADLOCK_CLR |ADLOCK_FILELOCK, 0, 0, 0);
-    ad_tmplock(&ad, ADEID_DFORK, ADLOCK_CLR, 0, 0, 0);
-    ad_close( &ad, adflags );
+    if (adp)
+        ad_close( &ad, adflags );  /* ad_close removes locks if any */
 
 #ifdef DEBUG
 
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end deletefile:");
+    LOG(log_debug9, logtype_afpd, "end deletefile:");
 #endif /* DEBUG */
 
     return err;
 }
 
 /* ------------------------------------ */
 #endif /* DEBUG */
 
     return err;
 }
 
 /* ------------------------------------ */
-#ifdef CNID_DB
 /* return a file id */
 /* return a file id */
-int afp_createid(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj      *obj;
-char   *ibuf, *rbuf;
-int            ibuflen, *rbuflen;
+int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
 {
     struct stat         *st;
 {
     struct stat         *st;
-#if AD_VERSION > AD_VERSION1
-    struct adouble     ad;
-#endif
     struct vol         *vol;
     struct dir         *dir;
     char               *upath;
     struct vol         *vol;
     struct dir         *dir;
     char               *upath;
@@ -1511,11 +1579,8 @@ int              ibuflen, *rbuflen;
     u_short            vid;
     struct path         *s_path;
 
     u_short            vid;
     struct path         *s_path;
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin afp_createid:");
-#endif /* DEBUG */
-
     *rbuflen = 0;
     *rbuflen = 0;
+
     ibuf += 2;
 
     memcpy(&vid, ibuf, sizeof(vid));
     ibuf += 2;
 
     memcpy(&vid, ibuf, sizeof(vid));
@@ -1525,6 +1590,10 @@ int              ibuflen, *rbuflen;
         return( AFPERR_PARAM);
     }
 
         return( AFPERR_PARAM);
     }
 
+    if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
+        return AFPERR_NOOP;
+    }
+
     if (vol->v_flags & AFPVOL_RO)
         return AFPERR_VLOCK;
 
     if (vol->v_flags & AFPVOL_RO)
         return AFPERR_VLOCK;
 
@@ -1532,11 +1601,11 @@ int             ibuflen, *rbuflen;
     ibuf += sizeof(did);
 
     if (NULL == ( dir = dirlookup( vol, did )) ) {
     ibuf += sizeof(did);
 
     if (NULL == ( dir = dirlookup( vol, did )) ) {
-        return( AFPERR_PARAM );
+        return afp_errno; /* was AFPERR_PARAM */
     }
 
     }
 
-    if (( s_path = cname( vol, dir, &ibuf )) == NULL ) {
-        return afp_errno; /* was AFPERR_PARAM */
+    if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
+        return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
     }
 
     if ( path_isadir(s_path) ) {
     }
 
     if ( path_isadir(s_path) ) {
@@ -1556,66 +1625,115 @@ int            ibuflen, *rbuflen;
             return AFPERR_PARAM;
     }
     st = &s_path->st;
             return AFPERR_PARAM;
     }
     st = &s_path->st;
-    if ((id = cnid_lookup(vol->v_db, st, did, upath, len = strlen(upath)))) {
+    if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
         memcpy(rbuf, &id, sizeof(id));
         *rbuflen = sizeof(id);
         return AFPERR_EXISTID;
     }
 
         memcpy(rbuf, &id, sizeof(id));
         *rbuflen = sizeof(id);
         return AFPERR_EXISTID;
     }
 
-#if AD_VERSION > AD_VERSION1
-    memset(&ad, 0, sizeof(ad));
-    if (ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, &ad ) >= 0) {
-        memcpy(&id, ad_entry(&ad, ADEID_DID), sizeof(id));
-        ad_close(&ad, ADFLAGS_HF);
-    }
-#endif /* AD_VERSION > AD_VERSION1 */
-
-    if ((id = cnid_add(vol->v_db, st, did, upath, len, id)) != CNID_INVALID) {
+    if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
         memcpy(rbuf, &id, sizeof(id));
         *rbuflen = sizeof(id);
         return AFP_OK;
     }
 
         memcpy(rbuf, &id, sizeof(id));
         *rbuflen = sizeof(id);
         return AFP_OK;
     }
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "ending afp_createid...:");
-#endif /* DEBUG */
+    return afp_errno;
+}
 
 
-    switch (errno) {
-    case EROFS:
-        return AFPERR_VLOCK;
-        break;
-    case EPERM:
-    case EACCES:
-        return AFPERR_ACCESS;
-        break;
-    default:
-        LOG(log_error, logtype_afpd, "afp_createid: cnid_add: %s", strerror(errno));
-        return AFPERR_PARAM;
+/* ------------------------------- */
+struct reenum {
+    struct vol *vol;
+    cnid_t     did;
+};
+
+static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
+{
+    struct path   path;
+    struct reenum *param = data;
+    struct vol    *vol = param->vol;  
+    cnid_t        did  = param->did;
+    cnid_t       aint;
+    
+    if ( stat(de->d_name, &path.st)<0 )
+        return 0;
+    
+    /* update or add to cnid */
+    aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
+
+#if AD_VERSION > AD_VERSION1
+    if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
+        struct adouble  ad, *adp;
+
+        path.st_errno = 0;
+        path.st_valid = 1;
+        path.u_name = de->d_name;
+            
+        adp = of_ad(vol, &path, &ad);
+            
+        if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
+            return 0;
+        }
+        if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
+            ad_flush(adp);
+        }
+        ad_close_metadata(adp);
+    }
+#endif /* AD_VERSION > AD_VERSION1 */
+
+    return 0;
+}
+
+/* --------------------
+ * Ok the db is out of synch with the dir.
+ * but if it's a deleted file we don't want to do it again and again.
+*/
+static int
+reenumerate_id(struct vol *vol, char *name, struct dir *dir)
+{
+    int             ret;
+    struct reenum   data;
+    struct stat     st;
+    
+    if (vol->v_cdb == NULL) {
+       return -1;
+    }
+    
+    /* FIXME use of_statdir ? */
+    if (stat(name, &st)) {
+       return -1;
     }
     }
+
+    if (dirreenumerate(dir, &st)) {
+        /* we already did it once and the dir haven't been modified */
+       return dir->offcnt;
+    }
+    
+    data.vol = vol;
+    data.did = dir->d_did;
+    if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
+        setdiroffcnt(curdir, &st,  ret);
+        dir->d_flags |= DIRF_CNID;
+    }
+
+    return ret;
 }
 
 /* ------------------------------
    resolve a file id */
 }
 
 /* ------------------------------
    resolve a file id */
-int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj      *obj;
-char   *ibuf, *rbuf;
-int            ibuflen, *rbuflen;
+int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
 {
     struct vol         *vol;
     struct dir         *dir;
     char               *upath;
     struct path         path;
 {
     struct vol         *vol;
     struct dir         *dir;
     char               *upath;
     struct path         path;
-    int                 err, buflen;
-    cnid_t             id;
+    int                 err, retry=0;
+    size_t             buflen;
+    cnid_t             id, cnid;
     u_int16_t          vid, bitmap;
 
     static char buffer[12 + MAXPATHLEN + 1];
     int len = 12 + MAXPATHLEN + 1;
 
     u_int16_t          vid, bitmap;
 
     static char buffer[12 + MAXPATHLEN + 1];
     int len = 12 + MAXPATHLEN + 1;
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin afp_resolveid:");
-#endif /* DEBUG */
-
     *rbuflen = 0;
     ibuf += 2;
 
     *rbuflen = 0;
     ibuf += 2;
 
@@ -1626,18 +1744,54 @@ int             ibuflen, *rbuflen;
         return( AFPERR_PARAM);
     }
 
         return( AFPERR_PARAM);
     }
 
+    if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
+        return AFPERR_NOOP;
+    }
+
     memcpy(&id, ibuf, sizeof( id ));
     ibuf += sizeof(id);
     memcpy(&id, ibuf, sizeof( id ));
     ibuf += sizeof(id);
-
-    if (NULL == (upath = cnid_resolve(vol->v_db, &id, buffer, len)) ) {
+    cnid = id;
+    
+    if (!id) {
+        /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
+        return AFPERR_NOID;
+    }
+retry:
+    if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
     }
 
         return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
     }
 
-    if (( dir = dirlookup( vol, id )) == NULL ) {
+    if (NULL == ( dir = dirlookup( vol, id )) ) {
         return AFPERR_NOID; /* idem AFPERR_PARAM */
     }
         return AFPERR_NOID; /* idem AFPERR_PARAM */
     }
+    if (movecwd(vol, dir) < 0) {
+        switch (errno) {
+        case EACCES:
+        case EPERM:
+            return AFPERR_ACCESS;
+        case ENOENT:
+            return AFPERR_NOID;
+        default:
+            return AFPERR_PARAM;
+        }
+    }
+
+    memset(&path, 0, sizeof(path));
     path.u_name = upath;
     path.u_name = upath;
-    if (movecwd(vol, dir) < 0 || of_stat(&path) < 0) {
+    if ( of_stat(&path) < 0 ) {
+#ifdef ESTALE
+        /* with nfs and our working directory is deleted */
+       if (errno == ESTALE) {
+           errno = ENOENT;
+       }
+#endif 
+       if ( errno == ENOENT && !retry) {
+           /* cnid db is out of sync, reenumerate the directory and update ids */
+           reenumerate_id(vol, ".", dir);
+           id = cnid;
+           retry = 1;
+           goto retry;
+        }
         switch (errno) {
         case EACCES:
         case EPERM:
         switch (errno) {
         case EACCES:
         case EPERM:
@@ -1648,32 +1802,31 @@ int             ibuflen, *rbuflen;
             return AFPERR_PARAM;
         }
     }
             return AFPERR_PARAM;
         }
     }
+
     /* directories are bad */
     /* directories are bad */
-    if (S_ISDIR(path.st.st_mode))
-        return AFPERR_BADTYPE;
+    if (S_ISDIR(path.st.st_mode)) {
+        /* OS9 and OSX don't return the same error code  */
+        return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
+    }
 
     memcpy(&bitmap, ibuf, sizeof(bitmap));
     bitmap = ntohs( bitmap );
 
     memcpy(&bitmap, ibuf, sizeof(bitmap));
     bitmap = ntohs( bitmap );
-    path.m_name = utompath(vol, upath);
-    if ((err = getfilparams(vol, bitmap, &path , curdir, 
-                            rbuf + sizeof(bitmap), &buflen)) != AFP_OK) {
+    if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
+        return AFPERR_NOID;
+    }
+    path.id = cnid;
+    if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, 
+                            rbuf + sizeof(bitmap), &buflen))) {
         return err;
     }
     *rbuflen = buflen + sizeof(bitmap);
     memcpy(rbuf, ibuf, sizeof(bitmap));
 
         return err;
     }
     *rbuflen = buflen + sizeof(bitmap);
     memcpy(rbuf, ibuf, sizeof(bitmap));
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end afp_resolveid:");
-#endif /* DEBUG */
-
     return AFP_OK;
 }
 
 /* ------------------------------ */
     return AFP_OK;
 }
 
 /* ------------------------------ */
-int afp_deleteid(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj      *obj;
-char   *ibuf, *rbuf;
-int            ibuflen, *rbuflen;
+int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
     struct stat         st;
     struct vol         *vol;
 {
     struct stat         st;
     struct vol         *vol;
@@ -1686,10 +1839,6 @@ int              ibuflen, *rbuflen;
     static char buffer[12 + MAXPATHLEN + 1];
     int len = 12 + MAXPATHLEN + 1;
 
     static char buffer[12 + MAXPATHLEN + 1];
     int len = 12 + MAXPATHLEN + 1;
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin afp_deleteid:");
-#endif /* DEBUG */
-
     *rbuflen = 0;
     ibuf += 2;
 
     *rbuflen = 0;
     ibuf += 2;
 
@@ -1700,6 +1849,10 @@ int              ibuflen, *rbuflen;
         return( AFPERR_PARAM);
     }
 
         return( AFPERR_PARAM);
     }
 
+    if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
+        return AFPERR_NOOP;
+    }
+
     if (vol->v_flags & AFPVOL_RO)
         return AFPERR_VLOCK;
 
     if (vol->v_flags & AFPVOL_RO)
         return AFPERR_VLOCK;
 
@@ -1707,11 +1860,11 @@ int             ibuflen, *rbuflen;
     ibuf += sizeof(id);
     fileid = id;
 
     ibuf += sizeof(id);
     fileid = id;
 
-    if (NULL == (upath = cnid_resolve(vol->v_db, &id, buffer, len)) ) {
+    if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
         return AFPERR_NOID;
     }
 
         return AFPERR_NOID;
     }
 
-    if (( dir = dirlookup( vol, id )) == NULL ) {
+    if (NULL == ( dir = dirlookup( vol, id )) ) {
         return( AFPERR_PARAM );
     }
 
         return( AFPERR_PARAM );
     }
 
@@ -1721,6 +1874,9 @@ int               ibuflen, *rbuflen;
         case EACCES:
         case EPERM:
             return AFPERR_ACCESS;
         case EACCES:
         case EPERM:
             return AFPERR_ACCESS;
+#ifdef ESTALE
+       case ESTALE:
+#endif 
         case ENOENT:
             /* still try to delete the id */
             err = AFPERR_NOOBJ;
         case ENOENT:
             /* still try to delete the id */
             err = AFPERR_NOOBJ;
@@ -1729,12 +1885,10 @@ int             ibuflen, *rbuflen;
             return AFPERR_PARAM;
         }
     }
             return AFPERR_PARAM;
         }
     }
-
-    /* directories are bad */
-    if (S_ISDIR(st.st_mode))
+    else if (S_ISDIR(st.st_mode)) /* directories are bad */
         return AFPERR_BADTYPE;
 
         return AFPERR_BADTYPE;
 
-    if (cnid_delete(vol->v_db, fileid)) {
+    if (cnid_delete(vol->v_cdb, fileid)) {
         switch (errno) {
         case EROFS:
             return AFPERR_VLOCK;
         switch (errno) {
         case EROFS:
             return AFPERR_VLOCK;
@@ -1746,20 +1900,59 @@ int             ibuflen, *rbuflen;
         }
     }
 
         }
     }
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "end afp_deleteid:");
-#endif /* DEBUG */
-
     return err;
 }
     return err;
 }
-#endif /* CNID_DB */
+
+/* ------------------------------ */
+static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
+{
+    int             ret;
+
+    if (path->st_errno) {
+        switch (path->st_errno) {
+        case ENOENT:
+            afp_errno = AFPERR_NOID;
+            break;
+        case EPERM:
+        case EACCES:
+            afp_errno = AFPERR_ACCESS;
+            break;
+        default:
+            afp_errno = AFPERR_PARAM;
+            break;
+        }
+        return NULL;
+    }
+    /* we use file_access both for legacy Mac perm and
+     * for unix privilege, rename will take care of folder perms
+    */
+    if (file_access(path, OPENACC_WR ) < 0) {
+        afp_errno = AFPERR_ACCESS;
+        return NULL;
+    }
+    
+    if ((*of = of_findname(path))) {
+        /* reuse struct adouble so it won't break locks */
+        adp = (*of)->of_ad;
+    }
+    else {
+        ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
+        /* META and HF */
+        if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
+            /* from AFP spec.
+             * The user must have the Read & Write privilege for both files in order to use this command.
+             */
+            ad_close(adp, ADFLAGS_HF);
+            afp_errno = AFPERR_ACCESS;
+            return NULL;
+        }
+    }
+    return adp;
+}
 
 #define APPLETEMP ".AppleTempXXXXXX"
 
 
 #define APPLETEMP ".AppleTempXXXXXX"
 
-int afp_exchangefiles(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj      *obj;
-char   *ibuf, *rbuf;
-int            ibuflen, *rbuflen;
+int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
     struct stat         srcst, destst;
     struct vol         *vol;
 {
     struct stat         srcst, destst;
     struct vol         *vol;
@@ -1770,20 +1963,18 @@ int             ibuflen, *rbuflen;
     int                 err;
     struct adouble     ads;
     struct adouble     add;
     int                 err;
     struct adouble     ads;
     struct adouble     add;
-    struct adouble     *adsp;
-    struct adouble     *addp;
-    struct ofork       *s_of;
-    struct ofork       *d_of;
+    struct adouble     *adsp = NULL;
+    struct adouble     *addp = NULL;
+    struct ofork       *s_of = NULL;
+    struct ofork       *d_of = NULL;
+    int                 crossdev;
     
     
-#ifdef CNID_DB
     int                 slen, dlen;
     int                 slen, dlen;
-#endif /* CNID_DB */
     u_int32_t          sid, did;
     u_int16_t          vid;
 
     u_int32_t          sid, did;
     u_int16_t          vid;
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin afp_exchangefiles:");
-#endif /* DEBUG */
+    uid_t              uid;
+    gid_t              gid;
 
     *rbuflen = 0;
     ibuf += 2;
 
     *rbuflen = 0;
     ibuf += 2;
@@ -1795,7 +1986,7 @@ int               ibuflen, *rbuflen;
         return( AFPERR_PARAM);
     }
 
         return( AFPERR_PARAM);
     }
 
-    if (vol->v_flags & AFPVOL_RO)
+    if ((vol->v_flags & AFPVOL_RO))
         return AFPERR_VLOCK;
 
     /* source and destination dids */
         return AFPERR_VLOCK;
 
     /* source and destination dids */
@@ -1806,130 +1997,129 @@ int           ibuflen, *rbuflen;
 
     /* source file */
     if (NULL == (dir = dirlookup( vol, sid )) ) {
 
     /* source file */
     if (NULL == (dir = dirlookup( vol, sid )) ) {
-        return( AFPERR_PARAM );
+        return afp_errno; /* was AFPERR_PARAM */
     }
 
     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
     }
 
     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
-        return afp_errno; /* was AFPERR_PARAM */
+        return get_afp_errno(AFPERR_NOOBJ); 
     }
 
     if ( path_isadir(path) ) {
     }
 
     if ( path_isadir(path) ) {
-        return( AFPERR_BADTYPE );   /* it's a dir */
+        return AFPERR_BADTYPE;   /* it's a dir */
     }
 
     }
 
-    upath = path->u_name;
-    switch (path->st_errno) {
-        case 0:
-             break;
-        case ENOENT:
-            return AFPERR_NOID;
-        case EPERM:
-        case EACCES:
-            return AFPERR_ACCESS;
-        default:
-            return AFPERR_PARAM;
-    }
-    memset(&ads, 0, sizeof(ads));
-    adsp = &ads;
-    if ((s_of = of_findname(path))) {
-            /* reuse struct adouble so it won't break locks */
-            adsp = s_of->of_ad;
-    }
-    memcpy(&srcst, &path->st, sizeof(struct stat));
     /* save some stuff */
     /* save some stuff */
+    srcst = path->st;
     sdir = curdir;
     spath = obj->oldtmp;
     supath = obj->newtmp;
     strcpy(spath, path->m_name);
     sdir = curdir;
     spath = obj->oldtmp;
     supath = obj->newtmp;
     strcpy(spath, path->m_name);
-    strcpy(supath, upath); /* this is for the cnid changing */
-    p = absupath( vol, sdir, upath);
+    strcpy(supath, path->u_name); /* this is for the cnid changing */
+    p = absupath( vol, sdir, supath);
     if (!p) {
         /* pathname too long */
         return AFPERR_PARAM ;
     }
     if (!p) {
         /* pathname too long */
         return AFPERR_PARAM ;
     }
+    
+    ad_init(&ads, vol->v_adouble, vol->v_ad_options);
+    if (!(adsp = find_adouble( path, &s_of, &ads))) {
+        return afp_errno;
+    }
 
 
+    /* ***** from here we may have resource fork open **** */
+    
     /* look for the source cnid. if it doesn't exist, don't worry about
      * it. */
     /* look for the source cnid. if it doesn't exist, don't worry about
      * it. */
-#ifdef CNID_DB
-    sid = cnid_lookup(vol->v_db, &srcst, sdir->d_did, supath,
-                      slen = strlen(supath));
-#endif /* CNID_DB */
+    sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
 
     if (NULL == ( dir = dirlookup( vol, did )) ) {
 
     if (NULL == ( dir = dirlookup( vol, did )) ) {
-        return( AFPERR_PARAM );
+        err = afp_errno; /* was AFPERR_PARAM */
+        goto err_exchangefile;
     }
 
     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
     }
 
     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
-        return( AFPERR_PARAM );
+        err = get_afp_errno(AFPERR_NOOBJ); 
+        goto err_exchangefile;
     }
 
     if ( path_isadir(path) ) {
     }
 
     if ( path_isadir(path) ) {
-        return( AFPERR_BADTYPE );
+        err = AFPERR_BADTYPE;
+        goto err_exchangefile;
     }
 
     /* FPExchangeFiles is the only call that can return the SameObj
      * error */
     }
 
     /* FPExchangeFiles is the only call that can return the SameObj
      * error */
-    if ((curdir == sdir) && strcmp(spath, path->m_name) == 0)
-        return AFPERR_SAMEOBJ;
-    memcpy(&srcst, &path->st, sizeof(struct stat));
-
-    switch (errno) {
-        case 0:
-             break;
-        case ENOENT:
-            return AFPERR_NOID;
-        case EPERM:
-        case EACCES:
-            return AFPERR_ACCESS;
-        default:
-            return AFPERR_PARAM;
+    if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
+        err = AFPERR_SAMEOBJ;
+        goto err_exchangefile;
     }
     }
-    memset(&add, 0, sizeof(add));
-    addp = &add;
-    if ((d_of = of_findname( path))) {
-            /* reuse struct adouble so it won't break locks */
-            addp = d_of->of_ad;
+
+    ad_init(&add, vol->v_adouble, vol->v_ad_options);
+    if (!(addp = find_adouble( path, &d_of, &add))) {
+        err = afp_errno;
+        goto err_exchangefile;
     }
     }
-    memcpy(&destst, &path->st, sizeof(struct stat));
+    destst = path->st;
 
     /* they are not on the same device and at least one is open
 
     /* they are not on the same device and at least one is open
+     * FIXME broken for for crossdev and adouble v2
+     * return an error 
     */
     */
-    if ((d_of || s_of)  && srcst.st_dev != destst.st_dev)
-        return AFPERR_MISC;
-    
-    upath = path->u_name;
-#ifdef CNID_DB
+    crossdev = (srcst.st_dev != destst.st_dev);
+    if (/* (d_of || s_of)  && */ crossdev) {
+        err = AFPERR_MISC;
+        goto err_exchangefile;
+    }
+
     /* look for destination id. */
     /* look for destination id. */
-    did = cnid_lookup(vol->v_db, &destst, curdir->d_did, upath,
-                      dlen = strlen(upath));
-#endif /* CNID_DB */
+    upath = path->u_name;
+    did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
 
     /* construct a temp name.
      * NOTE: the temp file will be in the dest file's directory. it
      * will also be inaccessible from AFP. */
     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
 
     /* construct a temp name.
      * NOTE: the temp file will be in the dest file's directory. it
      * will also be inaccessible from AFP. */
     memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
-    if (!mktemp(temp))
-        return AFPERR_MISC;
+    if (!mktemp(temp)) {
+        err = AFPERR_MISC;
+        goto err_exchangefile;
+    }
+    
+    if (crossdev) {
+        /* FIXME we need to close fork for copy, both s_of and d_of are null */
+       ad_close(adsp, ADFLAGS_HF);
+       ad_close(addp, ADFLAGS_HF);
+    }
 
     /* now, quickly rename the file. we error if we can't. */
 
     /* now, quickly rename the file. we error if we can't. */
-    if ((err = renamefile(p, temp, temp, vol_noadouble(vol), adsp)) < 0)
+    if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
         goto err_exchangefile;
     of_rename(vol, s_of, sdir, spath, curdir, temp);
 
     /* rename destination to source */
         goto err_exchangefile;
     of_rename(vol, s_of, sdir, spath, curdir, temp);
 
     /* rename destination to source */
-    if ((err = renamefile(upath, p, spath, vol_noadouble(vol), addp)) < 0)
+    if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
         goto err_src_to_tmp;
     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
 
     /* rename temp to destination */
         goto err_src_to_tmp;
     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
 
     /* rename temp to destination */
-    if ((err = renamefile(temp, upath, path->m_name, vol_noadouble(vol), adsp)) < 0)
+    if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
         goto err_dest_to_src;
     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
 
         goto err_dest_to_src;
     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
 
-#ifdef CNID_DB
-    /* id's need switching. src -> dest and dest -> src. */
-    if (sid && (cnid_update(vol->v_db, sid, &destst, curdir->d_did,
-                            upath, dlen) < 0)) {
+    /* id's need switching. src -> dest and dest -> src. 
+     * we need to re-stat() if it was a cross device copy.
+    */
+    if (sid) {
+       cnid_delete(vol->v_cdb, sid);
+    }
+    if (did) {
+       cnid_delete(vol->v_cdb, did);
+    }
+    if ((did && ( (crossdev && stat( upath, &srcst) < 0) || 
+                cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
+       ||
+       (sid && ( (crossdev && stat(p, &destst) < 0) ||
+                cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
+    ) {
         switch (errno) {
         case EPERM:
         case EACCES:
         switch (errno) {
         case EPERM:
         case EACCES:
@@ -1940,50 +2130,82 @@ int             ibuflen, *rbuflen;
         }
         goto err_temp_to_dest;
     }
         }
         goto err_temp_to_dest;
     }
+    
+    /* here we need to reopen if crossdev */
+    if (sid && ad_setid(addp, destst.st_dev, destst.st_ino,  sid, sdir->d_did, vol->v_stamp)) 
+    {
+       ad_flush( addp );
+    }
+        
+    if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino,  did, curdir->d_did, vol->v_stamp)) 
+    {
+       ad_flush( adsp );
+    }
 
 
-    if (did && (cnid_update(vol->v_db, did, &srcst, sdir->d_did,
-                            supath, slen) < 0)) {
-        switch (errno) {
-        case EPERM:
-        case EACCES:
-            err = AFPERR_ACCESS;
-            break;
-        default:
-            err = AFPERR_PARAM;
-        }
+    /* change perms, src gets dest perm and vice versa */
 
 
-        if (sid)
-            cnid_update(vol->v_db, sid, &srcst, sdir->d_did, supath, slen);
+    uid = geteuid();
+    gid = getegid();
+    if (seteuid(0)) {
+        LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
+        err = AFP_OK; /* ignore error */
         goto err_temp_to_dest;
     }
         goto err_temp_to_dest;
     }
-#endif /* CNID_DB */
 
 
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "ending afp_exchangefiles:");
-#endif /* DEBUG */
+    /*
+     * we need to exchange ACL entries as well
+     */
+    /* exchange_acls(vol, p, upath); */
 
 
-    return AFP_OK;
+    path->st = srcst;
+    path->st_valid = 1;
+    path->st_errno = 0;
+    path->m_name = NULL;
+    path->u_name = upath;
+
+    setfilunixmode(vol, path, destst.st_mode);
+    setfilowner(vol, destst.st_uid, destst.st_gid, path);
+
+    path->st = destst;
+    path->st_valid = 1;
+    path->st_errno = 0;
+    path->u_name = p;
 
 
+    setfilunixmode(vol, path, srcst.st_mode);
+    setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
+
+    if ( setegid(gid) < 0 || seteuid(uid) < 0) {
+        LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
+        exit(EXITERR_SYS);
+    }
+
+    err = AFP_OK;
+    goto err_exchangefile;
 
     /* all this stuff is so that we can unwind a failed operation
      * properly. */
 
     /* all this stuff is so that we can unwind a failed operation
      * properly. */
-#ifdef CNID_DB
 err_temp_to_dest:
 err_temp_to_dest:
-#endif
     /* rename dest to temp */
     /* rename dest to temp */
-    renamefile(upath, temp, temp, vol_noadouble(vol), adsp);
+    renamefile(vol, upath, temp, temp, adsp);
     of_rename(vol, s_of, curdir, upath, curdir, temp);
 
 err_dest_to_src:
     /* rename source back to dest */
     of_rename(vol, s_of, curdir, upath, curdir, temp);
 
 err_dest_to_src:
     /* rename source back to dest */
-    renamefile(p, upath, path->m_name, vol_noadouble(vol), addp);
+    renamefile(vol, p, upath, path->m_name, addp);
     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
 
 err_src_to_tmp:
     /* rename temp back to source */
     of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
 
 err_src_to_tmp:
     /* rename temp back to source */
-    renamefile(temp, p, spath, vol_noadouble(vol), adsp);
+    renamefile(vol, temp, p, spath, adsp);
     of_rename(vol, s_of, curdir, temp, sdir, spath);
 
 err_exchangefile:
     of_rename(vol, s_of, curdir, temp, sdir, spath);
 
 err_exchangefile:
+    if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
+       ad_close(adsp, ADFLAGS_HF);
+    }
+    if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
+       ad_close(addp, ADFLAGS_HF);
+    }
+
     return err;
 }
     return err;
 }