]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/file.c
Merge symlink branch
[netatalk.git] / etc / afpd / file.c
index 239dfb1f8505a6adddd8c92104ab9b5789aba0ca..915219f595d11d7ea53876adc269a26a8b484b0b 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * $Id: file.c,v 1.61 2002-10-04 15:15:05 srittau Exp $
+ * $Id: file.c,v 1.134 2010-02-10 14:05:37 franklahm 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,37 +68,219 @@ 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
                           };
 
+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, int islink)
+{
+    struct extmap      *em;
+    void                *ad_finder = NULL;
+    int                 chk_ext = 0;
+    
+    if (adp)
+        ad_finder = ad_entry(adp, ADEID_FINDERI);
+
+    if (ad_finder) {
+        memcpy(data, ad_finder, ADEDLEN_FINDERI);
+        /* default type ? */
+        if (default_type(ad_finder)) 
+            chk_ext = 1;
+    }
+    else {
+        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 (islink){
+        u_int16_t linkflag;
+        memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
+        linkflag |= htons(FINDERINFO_ISALIAS);
+        memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
+        memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4); 
+        memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4); 
+        chk_ext = 0;
+    }
+
+    /** 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((char *)data + 4, em->em_creator, sizeof(em->em_creator));
+    }
+    return data;
+}
+
+/* ---------------------
+*/
+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;
+    char        *tp = NULL;
+    char        *src = name;
+    aint = strlen( name );
+
+    if (!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;
+        *data++ = aint;
+    }
+    else {
+        u_int16_t temp;
+
+        if (aint > 255)  /* FIXME safeguard, anyway if no ascii char it's game over*/
+           aint = 255;
+
+        utf8 = vol->v_kTextEncoding;
+        memcpy(data, &utf8, sizeof(utf8));
+        data += sizeof(utf8);
+        
+        temp = htons(aint);
+        memcpy(data, &temp, sizeof(temp));
+        data += sizeof(temp);
+    }
+
+    memcpy( data, src, aint );
+    data += aint;
+    if (tp) {
+        strcpy(name, tp);
+        free(tp);
+    }
+    return data;
+}
+
+/*
+ * FIXME: PDINFO is UTF8 and doesn't need adp
+*/
+#define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR)  |\
+                                 (1 << FILPBIT_CDATE) |\
+                                 (1 << FILPBIT_MDATE) |\
+                                 (1 << FILPBIT_BDATE) |\
+                                 (1 << FILPBIT_FINFO) |\
+                                 (1 << FILPBIT_RFLEN) |\
+                                 (1 << FILPBIT_EXTRFLEN) |\
+                                 (1 << FILPBIT_PDINFO) |\
+                                 (1 << FILPBIT_FNUM) |\
+                                 (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 adcnid;
+    u_int32_t dbcnid = CNID_INVALID;
+
+    if (vol->v_cdb != NULL) {
+        /* prime aint with what we think is the cnid, set did to zero for
+           catching moved files */
+        adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
+
+           dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
+           /* Throw errors if cnid_add fails. */
+           if (dbcnid == 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;
+            }
+        }
+        else if (adp && (adcnid != dbcnid)) {
+            /* Update the ressource fork. For a folder adp is always null */
+            LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid));
+            if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
+                ad_flush(adp);
+            }
+        }
+    }
+    return dbcnid;
+}
+             
+/* -------------------------- */
 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 */
-    struct stat                hst;
-    struct extmap      *em;
-    char               *data, *nameoff = NULL, *upath;
+    char               *data, *l_nameoff = NULL, *upath;
+    char                *utf_nameoff = NULL;
     int                        bit = 0;
     u_int32_t          aint;
     int                        bit = 0;
     u_int32_t          aint;
+    cnid_t              id = 0;
     u_int16_t          ashort;
     u_char              achar, fdType[4];
     u_int16_t          ashort;
     u_char              achar, fdType[4];
+    u_int32_t           utf8 = 0;
+    struct stat         *st;
     struct maccess     ma;
 
 #ifdef DEBUG
     struct maccess     ma;
 
 #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 == CNID_INVALID)
+            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;
@@ -119,23 +291,22 @@ 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;
 #if 0
             /* FIXME do we want a visual clue if the file is read only
              */
                 ashort = htons(ATTRBIT_INVISIBLE);
             } else
                 ashort = 0;
 #if 0
             /* FIXME do we want a visual clue if the file is read only
              */
+            struct maccess     ma;
             accessmode( ".", &ma, dir , NULL);
             if ((ma.ma_user & AR_UWRITE)) {
                accessmode( upath, &ma, dir , st);
                if (!(ma.ma_user & AR_UWRITE)) {
             accessmode( ".", &ma, dir , NULL);
             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;
@@ -172,27 +343,12 @@ int getmetadata(struct vol *vol,
             break;
 
         case FILPBIT_FINFO :
             break;
 
         case FILPBIT_FINFO :
-            if (adp)
-                memcpy(data, ad_entry(adp, ADEID_FINDERI), 32);
-            else {
-                memcpy(data, ufinderi, 32);
-                if (*upath == '.') { /* make it invisible */
-                    ashort = htons(FINDERINFO_INVISIBLE);
-                    memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
-                }
-            }
-
-            if ((!adp  || !memcmp(ad_entry(adp, ADEID_FINDERI),ufinderi , 8 )) 
-               && (em = getextmap( path ))
-            ) {
-                memcpy(data, em->em_type, sizeof( em->em_type ));
-                memcpy(data + 4, em->em_creator, sizeof(em->em_creator));
-            }
-            data += 32;
+               get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
+            data += ADEDLEN_FINDERI;
             break;
 
         case FILPBIT_LNAME :
             break;
 
         case FILPBIT_LNAME :
-            nameoff = data;
+            l_nameoff = data;
             data += sizeof( u_int16_t );
             break;
 
             data += sizeof( u_int16_t );
             break;
 
@@ -202,79 +358,25 @@ 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 :
-            aint = htonl( st->st_size );
+            if  (st->st_size > 0xffffffff)
+               aint = 0xffffffff;
+            else
+               aint = htonl( st->st_size );
             memcpy(data, &aint, sizeof( aint ));
             data += sizeof( aint );
             break;
 
         case FILPBIT_RFLEN :
             if ( adp ) {
             memcpy(data, &aint, sizeof( aint ));
             data += sizeof( aint );
             break;
 
         case FILPBIT_RFLEN :
             if ( adp ) {
-                aint = htonl( ad_getentrylen( adp, ADEID_RFORK ));
+                if (adp->ad_rlen > 0xffffffff)
+                    aint = 0xffffffff;
+                else
+                    aint = htonl( adp->ad_rlen);
             } else {
                 aint = 0;
             }
             } else {
                 aint = 0;
             }
@@ -287,63 +389,126 @@ int getmetadata(struct vol *vol,
                us what the PD file code should be.  Everything gets a
                subtype of 0x0000 unless the original value was hashed
                to "pXYZ" when we created it.  See IA, Ver 2.
                us what the PD file code should be.  Everything gets a
                subtype of 0x0000 unless the original value was hashed
                to "pXYZ" when we created it.  See IA, Ver 2.
-               <shirsch@ibm.net> */
+               <shirsch@adelphia.net> */
         case FILPBIT_PDINFO :
         case FILPBIT_PDINFO :
-            if ( adp ) {
-                memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
+            if (afp_version >= 30) { /* UTF8 name */
+                utf8 = kTextEncodingUTF8;
+                utf_nameoff = data;
+                data += sizeof( u_int16_t );
+                aint = 0;
+                memcpy(data, &aint, sizeof( aint ));
+                data += sizeof( aint );
+            }
+            else {
+                if ( adp ) {
+                    memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
 
 
-                if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
-                    achar = '\x04';
-                    ashort = 0x0000;
-                }
-                else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
-                    achar = '\xff';
-                    ashort = 0x0000;
-                }
-                else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
-                    achar = '\xb3';
-                    ashort = 0x0000;
-                }
-                else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
-                    achar = '\x00';
-                    ashort = 0x0000;
-                }
-                else if ( fdType[0] == 'p' ) {
-                    achar = fdType[1];
-                    ashort = (fdType[2] * 256) + fdType[3];
+                    if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
+                        achar = '\x04';
+                        ashort = 0x0000;
+                    }
+                    else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
+                        achar = '\xff';
+                        ashort = 0x0000;
+                    }
+                    else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
+                        achar = '\xb3';
+                        ashort = 0x0000;
+                    }
+                    else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
+                        achar = '\x00';
+                        ashort = 0x0000;
+                    }
+                    else if ( fdType[0] == 'p' ) {
+                        achar = fdType[1];
+                        ashort = (fdType[2] * 256) + fdType[3];
+                    }
+                    else {
+                        achar = '\x00';
+                        ashort = 0x0000;
+                    }
                 }
                 else {
                     achar = '\x00';
                     ashort = 0x0000;
                 }
                 }
                 else {
                     achar = '\x00';
                     ashort = 0x0000;
                 }
-            }
-            else {
-                achar = '\x00';
-                ashort = 0x0000;
-            }
 
 
-            *data++ = achar;
-            *data++ = 0;
-            memcpy(data, &ashort, sizeof( ashort ));
-            data += sizeof( ashort );
-            memset(data, 0, sizeof( ashort ));
-            data += sizeof( ashort );
+                *data++ = achar;
+                *data++ = 0;
+                memcpy(data, &ashort, sizeof( ashort ));
+                data += sizeof( ashort );
+                memset(data, 0, sizeof( ashort ));
+                data += sizeof( ashort );
+            }
+            break;
+        case FILPBIT_EXTDFLEN:
+            aint = htonl(st->st_size >> 32);
+            memcpy(data, &aint, sizeof( aint ));
+            data += sizeof( aint );
+            aint = htonl(st->st_size);
+            memcpy(data, &aint, sizeof( aint ));
+            data += sizeof( aint );
+            break;
+        case FILPBIT_EXTRFLEN:
+            aint = 0;
+            if (adp) 
+                aint = htonl(adp->ad_rlen >> 32);
+            memcpy(data, &aint, sizeof( aint ));
+            data += sizeof( aint );
+            if (adp) 
+                aint = htonl(adp->ad_rlen);
+            memcpy(data, &aint, sizeof( aint ));
+            data += sizeof( aint );
             break;
             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 );
         }
         bitmap = bitmap>>1;
         bit++;
     }
         default :
             return( AFPERR_BITMAP );
         }
         bitmap = bitmap>>1;
         bit++;
     }
-    if ( nameoff ) {
+    if ( l_nameoff ) {
         ashort = htons( data - buf );
         ashort = htons( data - buf );
-        memcpy(nameoff, &ashort, sizeof( ashort ));
-        if ((aint = strlen( path )) > MACFILELEN)
-            aint = MACFILELEN;
-        *data++ = aint;
-        memcpy(data, path, aint );
-        data += aint;
+        memcpy(l_nameoff, &ashort, sizeof( ashort ));
+        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 ));
+        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);
@@ -352,66 +517,57 @@ int getmetadata(struct vol *vol,
 /* ----------------------- */
 int getfilparams(struct vol *vol,
                  u_int16_t bitmap,
 /* ----------------------- */
 int getfilparams(struct vol *vol,
                  u_int16_t bitmap,
-                 char *path, struct dir *dir, struct stat *st,
-                 char *buf, int *buflen )
+                 struct path *path, struct dir *dir, 
+                 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;    
     int rc;    
+
 #ifdef DEBUG
 #ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin getfilparams:");
+    LOG(log_debug9, logtype_default, "begin getfilparams:");
 #endif /* DEBUG */
 
 #endif /* DEBUG */
 
-    upath = mtoupath(vol, path);
-    if ((of = of_findname(upath, st))) {
-        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);
+    opened = PARAM_NEED_ADP(bitmap);
+    adp = NULL;
 
 
-    } else {
-        memset(&ad, 0, sizeof(ad));
-        adp = &ad;
-    }
+    if (opened) {
+        char *upath;
+        int  flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
 
 
-    if ( ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, adp) < 0 ) {
-        adp = NULL;
-    }
-    else {
-#if 0
-       /* 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)) {
-               }
-       }
-#endif         
+        adp = of_ad(vol, path, &ad);
+        upath = path->u_name;
+
+        if ( ad_metadata( upath, flags|ADFLAGS_CREATE, 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;
+            }
+        }
     }
     }
-    rc = getmetadata(vol, bitmap, path, dir, 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;
@@ -419,11 +575,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;
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin afp_createfile:");
-#endif /* DEBUG */
-
+    struct path                *s_path;
+    
     *rbuflen = 0;
     ibuf++;
     creatf = (unsigned char) *ibuf++;
     *rbuflen = 0;
     ibuf++;
     creatf = (unsigned char) *ibuf++;
@@ -431,7 +584,7 @@ int         ibuflen, *rbuflen;
     memcpy(&vid, ibuf, sizeof( vid ));
     ibuf += sizeof( vid );
 
     memcpy(&vid, ibuf, sizeof( vid ));
     ibuf += sizeof( vid );
 
-    if (( vol = getvolbyvid( vid )) == NULL ) {
+    if (NULL == ( vol = getvolbyvid( vid )) ) {
         return( AFPERR_PARAM );
     }
 
         return( AFPERR_PARAM );
     }
 
@@ -441,29 +594,30 @@ int               ibuflen, *rbuflen;
     memcpy(&did, ibuf, sizeof( did));
     ibuf += sizeof( did );
 
     memcpy(&did, ibuf, sizeof( did));
     ibuf += sizeof( did );
 
-    if (( dir = dirsearch( vol, did )) == NULL ) {
-        return( AFPERR_NOOBJ );
+    if (NULL == ( dir = dirlookup( vol, did )) ) {
+        return afp_errno;
     }
 
     }
 
-    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
-        return( AFPERR_NOOBJ );
+    if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
+        return get_afp_errno(AFPERR_PARAM);
     }
 
     }
 
-    upath = mtoupath(vol, path);
-    if (0 != (ret = check_name(vol, upath))) 
-       return  ret;
+    if ( *s_path->m_name == '\0' ) {
+        return( AFPERR_BADTYPE );
+    }
 
 
-    ret = stat(upath, &st);
+    upath = s_path->u_name;
+    
     /* if upath is deleted we already in trouble anyway */
     /* if upath is deleted we already in trouble anyway */
-    if (!ret && (of = of_findname(upath, &st))) {
+    if ((of = of_findname(s_path))) {
         adp = of->of_ad;
     } else {
         adp = of->of_ad;
     } else {
-        memset(&ad, 0, sizeof(ad));
+        ad_init(&ad, vol->v_adouble, vol->v_ad_options);
         adp = &ad;
     }
     if ( creatf) {
         /* on a hard create, fail if file exists and is open */
         adp = &ad;
     }
     if ( creatf) {
         /* on a hard create, fail if file exists and is open */
-        if (!ret && of)
+        if (of)
             return AFPERR_BUSY;
         openf = O_RDWR|O_CREAT|O_TRUNC;
     } else {
             return AFPERR_BUSY;
         openf = O_RDWR|O_CREAT|O_TRUNC;
     } else {
@@ -476,30 +630,45 @@ 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, 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 */
-            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 );
         }
     }
-
-    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 );
+    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;
+    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:
+    curdir->offcnt++;
 
 #ifdef DROPKLUDGE
     if (vol->v_flags & AFPVOL_DROPBOX) {
 
 #ifdef DROPKLUDGE
     if (vol->v_flags & AFPVOL_DROPBOX) {
@@ -509,34 +678,23 @@ 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;
-    char       *path;
+    struct path *s_path;
     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;
 
     memcpy(&vid, ibuf, sizeof( vid ));
     ibuf += sizeof( vid );
     *rbuflen = 0;
     ibuf += 2;
 
     memcpy(&vid, ibuf, sizeof( vid ));
     ibuf += sizeof( vid );
-    if (( vol = getvolbyvid( vid )) == NULL ) {
+    if (NULL == ( vol = getvolbyvid( vid )) ) {
         return( AFPERR_PARAM );
     }
 
         return( AFPERR_PARAM );
     }
 
@@ -545,227 +703,297 @@ int             ibuflen, *rbuflen;
 
     memcpy(&did, ibuf, sizeof( did ));
     ibuf += sizeof( did );
 
     memcpy(&did, ibuf, sizeof( did ));
     ibuf += sizeof( did );
-    if (( dir = dirsearch( vol, did )) == NULL ) {
-        return( AFPERR_NOOBJ );
+    if (NULL == ( dir = dirlookup( vol, did )) ) {
+        return afp_errno; /* was AFPERR_NOOBJ */
     }
 
     memcpy(&bitmap, ibuf, sizeof( bitmap ));
     bitmap = ntohs( bitmap );
     ibuf += sizeof( bitmap );
 
     }
 
     memcpy(&bitmap, ibuf, sizeof( bitmap ));
     bitmap = ntohs( bitmap );
     ibuf += sizeof( bitmap );
 
-    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
-        return( AFPERR_NOOBJ );
+    if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
+        return get_afp_errno(AFPERR_PARAM);
     }
 
     }
 
-    if ( *path == '\0' ) {
+    if (path_isadir(s_path)) {
         return( AFPERR_BADTYPE ); /* it's a directory */
     }
 
         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++;
     }
 
-    if (( rc = setfilparams(vol, path, bitmap, ibuf )) == AFP_OK ) {
+    if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
         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,
 
 int setfilparams(struct vol *vol,
-                 char *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_int16_t          ashort, bshort;
+    u_char              achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
+    u_int16_t          ashort, bshort, oshort;
     u_int32_t          aint;
     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 */
 
-    upath = mtoupath(vol, path);
-    if ((of = of_findname(upath, NULL))) {
-        adp = of->of_ad;
-    } else {
-        memset(&ad, 0, sizeof(ad));
-        adp = &ad;
-    }
+    adp = of_ad(vol, path, &ad);
+    upath = path->u_name;
 
 
-    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 ));
-        memcpy(ad_entry( adp, ADEID_NAME ), path,
-               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 )) &&
-                      !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(finder_buf, buf, 32 );
+            if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
+            // SLFINFO
+                int fp;
+                ssize_t len;
+                int erc=1;
+                char buf[PATH_MAX+1];
+                if ((fp=open(path->u_name,O_RDONLY))>=0){
+                    if (len=read(fp,buf,PATH_MAX+1)){
+                        if (unlink(path->u_name)==0){
+                            buf[len]=0;
+                            erc=symlink(buf,path->u_name);  
+                            lstat(path->u_name,&(path->st));
+                        }
+                    }
+                    close(fp);
+                }
+                if (erc!=0){
+                    err=AFPERR_BITMAP;
+                    goto setfilparam_done;
+                }
             }
             }
-
-            memcpy(ad_entry( adp, ADEID_FINDERI ), 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;
 
 
-            /* Client needs to set the ProDOS file info for this file.
-               Use defined strings for the simple cases, and convert
-               all else into pXYY per Inside Appletalk.  Always set
-               the creator as "pdos". <shirsch@ibm.net> */
-        case FILPBIT_PDINFO :
-            achar = *buf;
-            buf += 2;
-            memcpy(&ashort, buf, sizeof( ashort ));
-            ashort = ntohs( ashort );
-            buf += 2;
-
-            switch ( (unsigned int) achar )
-            {
-            case 0x04 :
-                fdType = ( u_char *) "TEXT";
-                break;
-
-            case 0xff :
-                fdType = ( u_char *) "PSYS";
-                break;
-
-            case 0xb3 :
-                fdType = ( u_char *) "PS16";
-                break;
-
-            case 0x00 :
-                fdType = ( u_char *) "BINA";
-                break;
+            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);
 
 
-            default :
-                xyy[0] = ( u_char ) 'p';
-                xyy[1] = achar;
-                xyy[2] = ( u_char ) ( ashort >> 8 ) & 0xff;
-                xyy[3] = ( u_char ) ashort & 0xff;
-                fdType = xyy;
-                break;
+            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;
             }
             }
-
-            memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
-            memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
             break;
             break;
-
-
+        case FILPBIT_PDINFO :
+            if (afp_version < 30) { /* else it's UTF8 name */
+                achar = *buf;
+                buf += 2;
+                /* Keep special case to support crlf translations */
+                if ((unsigned int) achar == 0x04) {
+                   fdType = (u_char *)"TEXT";
+                   buf += 2;
+                } else {
+                   xyy[0] = ( u_char ) 'p';
+                   xyy[1] = achar;
+                   xyy[3] = *buf++;
+                   xyy[2] = *buf++;
+                   fdType = xyy;
+               }
+                break;
+            }
+            /* fallthrough */
         default :
             err = AFPERR_BITMAP;
         default :
             err = AFPERR_BITMAP;
-            goto setfilparam_done;
+            /* break while loop */
+            bitmap = 0;
+            break;
         }
 
         bitmap = bitmap>>1;
         bit++;
     }
 
         }
 
         bitmap = bitmap>>1;
         bit++;
     }
 
-setfilparam_done:
-    if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
-       newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
-    }
-    if (newdate) {
-       if (isad)
-          ad_setdate(adp, AD_DATE_MODIFY, newdate);
-       ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
-       utime(upath, &ut);
-    }
-
-    if (isad) {
-        ad_flush( adp, ADFLAGS_HF );
-        ad_close( adp, ADFLAGS_HF );
-
+    /* second try with adouble open 
+    */
+    if ( ad_open_metadata( upath, 0, 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);
+            oshort = bshort;
+            if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
+                bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
+            } else {
+                bshort &= ~ashort;
+            }
+            if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
+                change_parent_mdate = 1;
+            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;
+            }
+            /* fallthrough */
+        default :
+            err = AFPERR_BITMAP;
+            goto setfilparam_done;
+        }
+        bitmap = bitmap>>1;
+        bit++;
+    }
+
+setfilparam_done:
+    if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
+       newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
+    }
+    if (newdate) {
+       if (isad)
+          ad_setdate(adp, AD_DATE_MODIFY, newdate);
+       ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
+       utime(upath, &ut);
+    }
+
+    if (isad) {
+        ad_flush( adp);
+        ad_close_metadata( adp);
+
     }
 
     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
         bitmap = 1<<FILPBIT_MDATE;
     }
 
     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
         bitmap = 1<<FILPBIT_MDATE;
-        setdirparams(vol, "", bitmap, (char *)&newdate);
+        setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
     }
 
 #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;
 }
@@ -773,8 +1001,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
@@ -782,28 +1008,15 @@ 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 */
 
 #endif /* DEBUG */
 
-    if ( rename( src, dst ) < 0 ) {
+    if ( unix_rename( src, dst ) < 0 ) {
         switch ( errno ) {
         case ENOENT :
             return( AFPERR_NOOBJ );
         switch ( errno ) {
         case ENOENT :
             return( AFPERR_NOOBJ );
@@ -813,97 +1026,159 @@ 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 (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 afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj      *obj;
-char   *ibuf, *rbuf;
-int            ibuflen, *rbuflen;
+/* ---------------- 
+   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)
 {
 {
-    struct vol *vol;
+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;
+u_int16_t   len16;
+u_int32_t   hint;
+
+    if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
+        return -1;
+    }
+    ibuf++;
+    switch (type) {
+    case 2:
+        if (( plen = (unsigned char)*ibuf++ ) != 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. 
+                */
+               return -1;
+            }
+        }
+        break;
+    case 3:
+        memcpy(&hint, ibuf, sizeof(hint));
+        ibuf += sizeof(hint);
+           
+        memcpy(&len16, ibuf, sizeof(len16));
+        ibuf += sizeof(len16);
+        plen = ntohs(len16);
+        
+        if (plen) {
+            if (plen > AFPOBJ_TMPSIZ) {
+               return -1;
+            }
+            strncpy( newname, ibuf, plen );
+            newname[ plen ] = '\0';
+            if (strlen(newname) != plen) {
+               return -1;
+            }
+        }
+        break;
+    }
+    return plen;
+}
+
+/* -----------------------------------
+*/
+int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
+{
+    struct vol *s_vol, *d_vol;
     struct dir *dir;
     struct dir *dir;
-    char       *newname, *path, *p, *upath;
+    char       *newname, *p, *upath;
+    struct path *s_path;
     u_int32_t  sdid, ddid;
     u_int32_t  sdid, ddid;
-    int                plen, err, retvalue = AFP_OK;
+    int         err, retvalue = AFP_OK;
     u_int16_t  svid, dvid;
 
     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 (( vol = getvolbyvid( svid )) == NULL ) {
+    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 (( dir = dirsearch( vol, sdid )) == NULL ) {
-        return( AFPERR_PARAM );
+    if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
+        return afp_errno;
     }
 
     memcpy(&dvid, ibuf, sizeof( dvid ));
     }
 
     memcpy(&dvid, ibuf, sizeof( dvid ));
@@ -911,10 +1186,10 @@ int              ibuflen, *rbuflen;
     memcpy(&ddid, ibuf, sizeof( ddid ));
     ibuf += sizeof( ddid );
 
     memcpy(&ddid, ibuf, sizeof( ddid ));
     ibuf += sizeof( ddid );
 
-    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
-        return( AFPERR_NOOBJ );
+    if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
+        return get_afp_errno(AFPERR_PARAM);
     }
     }
-    if ( *path == '\0' ) {
+    if ( path_isadir(s_path) ) {
         return( AFPERR_BADTYPE );
     }
 
         return( AFPERR_BADTYPE );
     }
 
@@ -924,53 +1199,61 @@ 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, path );
+    adp = of_ad(s_vol, s_path, &ad);
 
 
-    upath = mtoupath(vol, newname );
-    if (of_findname(upath, NULL))
+    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;
+    }
 
 
-    p = ctoupath( vol, curdir, newname );
+    newname = obj->newtmp;
+    strcpy( newname, s_path->m_name );
+
+    p = ctoupath( s_vol, curdir, newname );
+    if (!p) {
+        return AFPERR_PARAM;
+    
+    }
 #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 (( vol = getvolbyvid( dvid )) == NULL ) {
+    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 (( dir = dirsearch( vol, ddid )) == NULL ) {
-        return( AFPERR_PARAM );
+    if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
+        return afp_errno;
     }
 
     }
 
-    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
-        return( AFPERR_NOOBJ );
+    if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
+        return get_afp_errno(AFPERR_NOOBJ); 
     }
     }
-    if ( *path != '\0' ) {
-        return( AFPERR_BADTYPE ); /* not a directory. AFPERR_PARAM? */
+    if ( *s_path->m_name != '\0' ) {
+       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 ( *ibuf++ != 2 ) {
+    if (copy_path_name(d_vol, newname, ibuf) < 0) {
         return( AFPERR_PARAM );
     }
         return( AFPERR_PARAM );
     }
-    if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
-        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. 
-            */
-            return( AFPERR_PARAM );
-        }
+    /* 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 );
     }
     }
-    upath = mtoupath(vol, newname);
-    if ( (err = copyfile(p, upath , newname, vol_noadouble(vol))) < 0 ) {
+    if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
         return err;
     }
         return err;
     }
+    curdir->offcnt++;
 
 #ifdef DROPKLUDGE
     if (vol->v_flags & AFPVOL_DROPBOX) {
 
 #ifdef DROPKLUDGE
     if (vol->v_flags & AFPVOL_DROPBOX) {
@@ -978,23 +1261,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) {
@@ -1002,271 +1281,278 @@ 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;
+    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;
-    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);
+        /* we never want to create a resource fork here, we are going to delete it 
+         * moreover sometimes deletefile is called with a no existent file and 
+         * ad_open would create a 0 byte resource fork
+        */
+        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:
@@ -1275,20 +1561,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.
@@ -1296,118 +1570,84 @@ 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 ) {
+        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) {
+    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);
-    ad_tmplock(&ad, ADEID_DFORK, ADLOCK_CLR, 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 adouble     ad;
+    struct stat         *st;
     struct vol         *vol;
     struct dir         *dir;
     struct vol         *vol;
     struct dir         *dir;
-    char               *path, *upath;
+    char               *upath;
     int                 len;
     cnid_t             did, id;
     u_short            vid;
     int                 len;
     cnid_t             did, id;
     u_short            vid;
-
-#ifdef DEBUG
-    LOG(log_info, logtype_afpd, "begin afp_createid:");
-#endif /* DEBUG */
+    struct path         *s_path;
 
     *rbuflen = 0;
 
     *rbuflen = 0;
+
     ibuf += 2;
 
     memcpy(&vid, ibuf, sizeof(vid));
     ibuf += sizeof(vid);
 
     ibuf += 2;
 
     memcpy(&vid, ibuf, sizeof(vid));
     ibuf += sizeof(vid);
 
-    if (( vol = getvolbyvid( vid )) == NULL ) {
+    if (NULL == ( vol = getvolbyvid( vid )) ) {
         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;
 
     memcpy(&did, ibuf, sizeof( did ));
     ibuf += sizeof(did);
 
     if (vol->v_flags & AFPVOL_RO)
         return AFPERR_VLOCK;
 
     memcpy(&did, ibuf, sizeof( did ));
     ibuf += sizeof(did);
 
-    if (( dir = dirsearch( vol, did )) == NULL ) {
-        return( AFPERR_PARAM );
+    if (NULL == ( dir = dirlookup( vol, did )) ) {
+        return afp_errno; /* was AFPERR_PARAM */
     }
 
     }
 
-    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
-        return( AFPERR_PARAM );
+    if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
+        return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
     }
 
     }
 
-    if ( *path == '\0' ) {
+    if ( path_isadir(s_path) ) {
         return( AFPERR_BADTYPE );
     }
 
         return( AFPERR_BADTYPE );
     }
 
-    upath = mtoupath(vol, path);
-    if (stat(upath, &st) < 0) {
-        switch (errno) {
+    upath = s_path->u_name;
+    switch (s_path->st_errno) {
+        case 0:
+             break; /* success */
         case EPERM:
         case EACCES:
             return AFPERR_ACCESS;
         case EPERM:
         case EACCES:
             return AFPERR_ACCESS;
@@ -1415,90 +1655,175 @@ int            ibuflen, *rbuflen;
             return AFPERR_NOOBJ;
         default:
             return AFPERR_PARAM;
             return AFPERR_NOOBJ;
         default:
             return AFPERR_PARAM;
-        }
     }
     }
-
-    if (id = cnid_lookup(vol->v_db, &st, did, upath, len = strlen(upath))) {
+    st = &s_path->st;
+    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 ( lstat(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;
 }
 
 }
 
-/* resolve a file id */
-int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
-AFPObj      *obj;
-char   *ibuf, *rbuf;
-int            ibuflen, *rbuflen;
+/* --------------------
+ * 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 (lstat(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 */
+int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
 {
 {
-    struct stat         st;
     struct vol         *vol;
     struct dir         *dir;
     char               *upath;
     struct vol         *vol;
     struct dir         *dir;
     char               *upath;
-    int                 err, buflen;
-    cnid_t             id;
+    struct path         path;
+    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;
 
     memcpy(&vid, ibuf, sizeof(vid));
     ibuf += sizeof(vid);
 
     *rbuflen = 0;
     ibuf += 2;
 
     memcpy(&vid, ibuf, sizeof(vid));
     ibuf += sizeof(vid);
 
-    if (( vol = getvolbyvid( vid )) == NULL ) {
+    if (NULL == ( vol = getvolbyvid( vid )) ) {
         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 ((upath = cnid_resolve(vol->v_db, &id, buffer, len)) == NULL) {
+    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;
+        }
+    }
 
 
-    if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
+    memset(&path, 0, sizeof(path));
+    path.u_name = upath;
+    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:
@@ -1511,30 +1836,29 @@ int             ibuflen, *rbuflen;
     }
 
     /* directories are bad */
     }
 
     /* directories are bad */
-    if (S_ISDIR(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 );
-
-    if ((err = getfilparams(vol, bitmap, utompath(vol, upath), curdir, &st,
-                            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;
         return err;
-
+    }
     *rbuflen = buflen + sizeof(bitmap);
     memcpy(rbuf, ibuf, sizeof(bitmap));
 
     *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;
@@ -1547,20 +1871,20 @@ 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;
 
     memcpy(&vid, ibuf, sizeof(vid));
     ibuf += sizeof(vid);
 
     *rbuflen = 0;
     ibuf += 2;
 
     memcpy(&vid, ibuf, sizeof(vid));
     ibuf += sizeof(vid);
 
-    if (( vol = getvolbyvid( vid )) == NULL ) {
+    if (NULL == ( vol = getvolbyvid( vid )) ) {
         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;
 
@@ -1568,20 +1892,23 @@ int             ibuflen, *rbuflen;
     ibuf += sizeof(id);
     fileid = id;
 
     ibuf += sizeof(id);
     fileid = id;
 
-    if ((upath = cnid_resolve(vol->v_db, &id, buffer, len)) == NULL) {
+    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 );
     }
 
     err = AFP_OK;
         return( AFPERR_PARAM );
     }
 
     err = AFP_OK;
-    if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
+    if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
         switch (errno) {
         case EACCES:
         case EPERM:
             return AFPERR_ACCESS;
         switch (errno) {
         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;
@@ -1590,12 +1917,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;
@@ -1607,43 +1932,81 @@ 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 dir         *dir, *sdir;
 {
     struct stat         srcst, destst;
     struct vol         *vol;
     struct dir         *dir, *sdir;
-    char               *spath, temp[17], *path, *p;
+    char               *spath, temp[17], *p;
     char                *supath, *upath;
     char                *supath, *upath;
+    struct path         *path;
     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;
@@ -1651,11 +2014,11 @@ int             ibuflen, *rbuflen;
     memcpy(&vid, ibuf, sizeof(vid));
     ibuf += sizeof(vid);
 
     memcpy(&vid, ibuf, sizeof(vid));
     ibuf += sizeof(vid);
 
-    if (( vol = getvolbyvid( vid )) == NULL ) {
+    if (NULL == ( vol = getvolbyvid( vid )) ) {
         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 */
@@ -1665,124 +2028,130 @@ int           ibuflen, *rbuflen;
     ibuf += sizeof(did);
 
     /* source file */
     ibuf += sizeof(did);
 
     /* source file */
-    if ((dir = dirsearch( vol, sid )) == NULL ) {
-        return( AFPERR_PARAM );
+    if (NULL == (dir = dirlookup( vol, sid )) ) {
+        return afp_errno; /* was AFPERR_PARAM */
     }
 
     }
 
-    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
-        return( AFPERR_PARAM );
+    if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
+        return get_afp_errno(AFPERR_NOOBJ); 
     }
 
     }
 
-    if ( *path == '\0' ) {
-        return( AFPERR_BADTYPE );   /* it's a dir */
+    if ( path_isadir(path) ) {
+        return AFPERR_BADTYPE;   /* it's a dir */
     }
 
     }
 
-    upath = mtoupath(vol, path);
-    if (stat(upath, &srcst) < 0) {
-        switch (errno) {
-        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(upath, &srcst))) {
-            /* reuse struct adouble so it won't break locks */
-            adsp = s_of->of_ad;
-    }
     /* save some stuff */
     /* save some stuff */
+    srcst = path->st;
     sdir = curdir;
     spath = obj->oldtmp;
     supath = obj->newtmp;
     sdir = curdir;
     spath = obj->oldtmp;
     supath = obj->newtmp;
-    strcpy(spath, path);
-    strcpy(supath, upath); /* this is for the cnid changing */
-    p = ctoupath( vol, sdir, spath);
+    strcpy(spath, path->m_name);
+    strcpy(supath, path->u_name); /* this is for the cnid changing */
+    p = absupath( vol, sdir, supath);
+    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 (( dir = dirsearch( vol, did )) == NULL ) {
-        return( AFPERR_PARAM );
+    if (NULL == ( dir = dirlookup( vol, did )) ) {
+        err = afp_errno; /* was AFPERR_PARAM */
+        goto err_exchangefile;
     }
 
     }
 
-    if (( path = cname( vol, dir, &ibuf )) == NULL ) {
-        return( AFPERR_PARAM );
+    if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
+        err = get_afp_errno(AFPERR_NOOBJ); 
+        goto err_exchangefile;
     }
 
     }
 
-    if ( *path == '\0' ) {
-        return( AFPERR_BADTYPE );
+    if ( path_isadir(path) ) {
+        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) == 0)
-        return AFPERR_SAMEOBJ;
-
-    upath = mtoupath(vol, path);
-    if (stat(upath, &destst) < 0) {
-        switch (errno) {
-        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( upath, &destst))) {
-            /* 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;
     }
     }
+    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;
-    
-#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;
         goto err_src_to_tmp;
-    of_rename(vol, d_of, curdir, path, sdir, spath);
+    of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
 
     /* rename temp to destination */
 
     /* rename temp to destination */
-    if ((err = renamefile(temp, upath, path, vol_noadouble(vol), adsp)) < 0)
+    if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
         goto err_dest_to_src;
         goto err_dest_to_src;
-    of_rename(vol, s_of, curdir, temp, curdir, path);
+    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 && lstat( upath, &srcst) < 0) || 
+                cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
+       ||
+       (sid && ( (crossdev && lstat(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:
@@ -1793,50 +2162,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, vol_noadouble(vol), addp);
-    of_rename(vol, d_of, sdir, spath, curdir, path);
+    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 */
 
 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;
 }