]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/file.c
Merge master
[netatalk.git] / etc / afpd / file.c
index aaf4704fe18e5db6da77dd7cb53d1ff02c11d57b..bd0e935c0fe8d2e44049b7471578cdf69e969e9f 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: file.c,v 1.136 2010-02-17 01:19:51 didg Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * All Rights Reserved.  See COPYRIGHT.
  */
 #include <stdio.h>
 #include <stdlib.h>
 
-/* STDC check */
-#if STDC_HEADERS
 #include <string.h>
-#else /* STDC_HEADERS */
-#ifndef HAVE_STRCHR
-#define strchr index
-#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))
-#endif /* ! HAVE_MEMCPY */
-#endif /* STDC_HEADERS */
-
 #include <utime.h>
 #include <errno.h>
 #include <sys/param.h>
@@ -41,6 +24,7 @@ char *strchr (), *strrchr ();
 #include <atalk/unix.h>
 
 #include "directory.h"
+#include "dircache.h"
 #include "desktop.h"
 #include "volume.h"
 #include "fork.h"
@@ -201,19 +185,39 @@ char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t
                                  (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) 
+/*!
+ * @brief Get CNID for did/upath args both from database and adouble file
+ *
+ * 1. Get the objects CNID as stored in its adouble file
+ * 2. Get the objects CNID from the database
+ * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
+ * 4. In case 2 and 3 differ, store 3 in the adouble file
+ *
+ * @param vol    (rw) volume
+ * @param adp    (rw) adouble struct of object upath, might be NULL
+ * @param st     (r) stat of upath, must NOT be NULL
+ * @param did    (r) parent CNID of upath
+ * @param upath  (r) name of object
+ * @param len    (r) strlen of upath
+ */
+uint32_t get_id(struct vol *vol,
+                struct adouble *adp, 
+                const struct stat *st,
+                const cnid_t did,
+                const char *upath,
+                const int len) 
 {
+    static int first = 1;       /* mark if this func is called the first time */
     u_int32_t adcnid;
     u_int32_t dbcnid = CNID_INVALID;
 
+restart:
     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);
+        adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
 
-           dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
+           dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
            /* Throw errors if cnid_add fails. */
            if (dbcnid == CNID_INVALID) {
             switch (errno) {
@@ -222,23 +226,61 @@ u_int32_t get_id(struct vol *vol, struct adouble *adp,  const struct stat *st,
             case CNID_ERR_PARAM:
                 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
                 afp_errno = AFPERR_PARAM;
-                return CNID_INVALID;
+                goto exit;
             case CNID_ERR_PATH:
                 afp_errno = AFPERR_PARAM;
-                return CNID_INVALID;
+                goto exit;
             default:
+                /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
+                /* we have to do it here for "dbd" because it uses "lazy opening" */
+                /* In order to not end in a loop somehow with goto restart below  */
+                /*  */
+                if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
+                    cnid_close(vol->v_cdb);
+                    free(vol->v_cnidscheme);
+                    vol->v_cnidscheme = strdup("tdb");
+
+                    int flags = CNID_FLAG_MEMORY;
+                    if ((vol->v_flags & AFPVOL_NODEV)) {
+                        flags |= CNID_FLAG_NODEV;
+                    }
+                    LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
+                        vol->v_path);
+                    vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
+                    if (vol->v_cdb) {
+                        /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
+                        vol->v_flags &= ~AFPVOL_CACHE;
+                        vol->v_flags |= AFPVOL_RO;
+#ifdef SERVERTEXT
+                        /* kill ourself with SIGUSR2 aka msg pending */
+                        setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
+                                   "Check server messages for details. Switching to read-only mode.");
+                        kill(getpid(), SIGUSR2);
+#endif
+                        goto restart; /* not try again with the temp CNID db */
+                    } else {
+#ifdef SERVERTEXT
+                        setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
+                                   "Check server messages for details, can't recover from this state!");
+#endif
+                    }
+                }
                 afp_errno = AFPERR_MISC;
-                return CNID_INVALID;
+                goto exit;
             }
         }
-        else if (adp && (adcnid != dbcnid)) {
+        else if (adp && (adcnid != dbcnid)) { /* 4 */
             /* 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));
+            LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
+                getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
             if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
                 ad_flush(adp);
             }
         }
     }
+
+exit:
+    first = 0;
     return dbcnid;
 }
              
@@ -259,24 +301,50 @@ int getmetadata(struct vol *vol,
     struct stat         *st;
     struct maccess     ma;
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "begin getmetadata:");
-#endif /* DEBUG */
-
     upath = path->u_name;
     st = &path->st;
-
     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 
+        if (!path->id) {
+            struct dir *cachedfile;
+            int len = strlen(upath);
+            if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != NULL)
+                id = cachedfile->d_did;
+            else {
+                id = get_id(vol, adp, st, dir->d_did, upath, len);
+
+                /* Add it to the cache */
+                LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
+                    ntohl(dir->d_did), upath, ntohl(id));
+
+                /* Get macname from unixname first */
+                if (path->m_name == NULL) {
+                    if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
+                        LOG(log_error, logtype_afpd, "getmetadata: utompath error");
+                        exit(EXITERR_SYS);
+                    }
+                }
+                
+                if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
+                    LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
+                    exit(EXITERR_SYS);
+                }
+
+                if ((dircache_add(cachedfile)) != 0) {
+                    LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
+                    exit(EXITERR_SYS);
+                }
+            }
+        } else {
             id = path->id;
+        }
+
         if (id == CNID_INVALID)
             return afp_errno;
+
         if (!path->m_name) {
             path->m_name = utompath(vol, upath, id, utf8_encoding());
         }
@@ -309,11 +377,15 @@ int getmetadata(struct vol *vol,
 #endif
             memcpy(data, &ashort, sizeof( ashort ));
             data += sizeof( ashort );
+            LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
+                path->u_name, ntohs(ashort));
             break;
 
         case FILPBIT_PDID :
             memcpy(data, &dir->d_did, sizeof( u_int32_t ));
             data += sizeof( u_int32_t );
+            LOG(log_debug, logtype_afpd, "metadata('%s'):     Parent DID: %u",
+                path->u_name, ntohl(dir->d_did));
             break;
 
         case FILPBIT_CDATE :
@@ -360,6 +432,8 @@ int getmetadata(struct vol *vol,
         case FILPBIT_FNUM :
             memcpy(data, &id, sizeof( id ));
             data += sizeof( id );
+            LOG(log_debug, logtype_afpd, "metadata('%s'):           CNID: %u",
+                path->u_name, ntohl(id));
             break;
 
         case FILPBIT_DFLEN :
@@ -524,21 +598,17 @@ int getfilparams(struct vol *vol,
     int                 opened = 0;
     int rc;    
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_default, "begin getfilparams:");
-#endif /* DEBUG */
-
     opened = PARAM_NEED_ADP(bitmap);
     adp = NULL;
 
     if (opened) {
         char *upath;
-        int  flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
+        int  flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
 
         adp = of_ad(vol, path, &ad);
         upath = path->u_name;
 
-        if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
+        if ( ad_metadata( upath, flags, adp) < 0 ) {
             switch (errno) {
             case EACCES:
                 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
@@ -555,12 +625,7 @@ int getfilparams(struct vol *vol,
         }
     }
     rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
-    if ( adp ) {
-        ad_close_metadata( adp);
-    }
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "end getfilparams:");
-#endif /* DEBUG */
+    ad_close_metadata( adp);
 
     return( rc );
 }
@@ -568,7 +633,7 @@ int getfilparams(struct vol *vol,
 /* ----------------------------- */
 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct adouble     ad, *adp;
+    struct adouble     ad;
     struct vol         *vol;
     struct dir         *dir;
     struct ofork        *of = NULL;
@@ -584,9 +649,8 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
     memcpy(&vid, ibuf, sizeof( vid ));
     ibuf += sizeof( vid );
 
-    if (NULL == ( vol = getvolbyvid( vid )) ) {
+    if (NULL == ( vol = getvolbyvid( vid )) )
         return( AFPERR_PARAM );
-    }
 
     if (vol->v_flags & AFPVOL_RO)
         return AFPERR_VLOCK;
@@ -594,44 +658,35 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
     memcpy(&did, ibuf, sizeof( did));
     ibuf += sizeof( did );
 
-    if (NULL == ( dir = dirlookup( vol, did )) ) {
+    if (NULL == ( dir = dirlookup( vol, did )) )
         return afp_errno;
-    }
 
-    if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
+    if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
         return get_afp_errno(AFPERR_PARAM);
-    }
 
-    if ( *s_path->m_name == '\0' ) {
+    if ( *s_path->m_name == '\0' )
         return( AFPERR_BADTYPE );
-    }
 
     upath = s_path->u_name;
+    ad_init(&ad, vol->v_adouble, vol->v_ad_options);
     
     /* if upath is deleted we already in trouble anyway */
     if ((of = of_findname(s_path))) {
-        adp = of->of_ad;
-    } else {
-        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 */
-        if (of)
+        if (creatf)
             return AFPERR_BUSY;
+        else
+            return AFPERR_EXIST;
+    }
+
+    if ( creatf)
         openf = O_RDWR|O_CREAT|O_TRUNC;
-    } else {
+    else
        /* on a soft create, if the file is open then ad_open won't fail
-          because open syscall is not called
-       */
-       if (of) {
-               return AFPERR_EXIST;
-       }
+          because open syscall is not called */
         openf = O_RDWR|O_CREAT|O_EXCL;
-    }
 
-    if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
-                  openf, 0666, adp) < 0 ) {
+    if ( ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF,
+                 openf, 0666, openf, 0666) < 0 ) {
         switch ( errno ) {
         case EROFS:
             return AFPERR_VLOCK;
@@ -648,34 +703,28 @@ int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_,
             return( AFPERR_PARAM );
         }
     }
-    if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
+    if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
          /* on noadouble volumes, just creating the data fork is ok */
          if (vol_noadouble(vol)) {
-             ad_close( adp, ADFLAGS_DF );
+             ad_close( &ad, 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 );
+         ad_close( &ad, ADFLAGS_DF );
          return AFPERR_ACCESS;
     }
 
     path = s_path->m_name;
-    ad_setname(adp, path);
-    ad_flush( adp);
-    ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
+    ad_setname(&ad, path);
+    ad_flush(&ad);
+    ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
 
 createfile_done:
     curdir->offcnt++;
 
-#ifdef DROPKLUDGE
-    if (vol->v_flags & AFPVOL_DROPBOX) {
-        retvalue = matchfile2dirperms(upath, vol, did);
-    }
-#endif /* DROPKLUDGE */
-
     setvoltime(obj, vol );
 
     return (retvalue);
@@ -816,8 +865,9 @@ int setfilparams(struct vol *vol,
                     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));
+                            erc = symlink(buf, path->u_name);
+                            if (!erc)
+                                of_stat(path);
                         }
                     }
                     close(fp);
@@ -889,7 +939,7 @@ int setfilparams(struct vol *vol,
 
     /* second try with adouble open 
     */
-    if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
+    if ( ad_open(adp, upath, ADFLAGS_HF, O_RDWR | O_CREAT, 0666) < 0) {
         LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
         /*
          * For some things, we don't need an adouble header:
@@ -902,7 +952,7 @@ int setfilparams(struct vol *vol,
         }
         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) ) {
+    } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
         ad_setname(adp, path->m_name);
     }
     
@@ -983,7 +1033,6 @@ setfilparam_done:
     if (isad) {
         ad_flush( adp);
         ad_close_metadata( adp);
-
     }
 
     if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
@@ -1002,21 +1051,19 @@ setfilparam_done:
  * renamefile and copyfile take the old and new unix pathnames
  * and the new mac name.
  *
+ * sdir_fd     source dir fd to which src path is relative (for openat et al semantics)
+ *             passing -1 means this is not used, src path is a full path
  * src         the source path 
  * dst         the dest filename in current dir
  * newname     the dest mac name
  * adp         adouble struct of src file, if open, or & zeroed one
  *
  */
-int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struct adouble *adp)
+int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
 {
     int                rc;
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "begin renamefile:");
-#endif /* DEBUG */
-
-    if ( unix_rename( src, dst ) < 0 ) {
+    if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
         switch ( errno ) {
         case ENOENT :
             return( AFPERR_NOOBJ );
@@ -1034,17 +1081,17 @@ int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struc
                /* 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 )) ) {
+            if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
                 /* on error copyfile delete dest */
                 return( rc );
             }
-            return deletefile(vol, src, 0);
+            return deletefile(vol, sdir_fd, src, 0);
         default :
             return( AFPERR_PARAM );
         }
     }
 
-    if (vol->vfs->vfs_renamefile(vol, src, dst) < 0 ) {
+    if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
         int err;
         
         err = errno;        
@@ -1052,7 +1099,7 @@ int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struc
         * we know we are on the same device 
        */
        if (err) {
-           unix_rename( dst, src ); 
+        unix_rename(-1, dst, sdir_fd, src ); 
            /* return the first error */
            switch ( err) {
             case ENOENT :
@@ -1068,16 +1115,12 @@ int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struc
         }
     }
 
-    /* don't care if we can't open the newly renamed ressource fork
-     */
-    if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
+    /* don't care if we can't open the newly renamed ressource fork */
+    if (ad_open(adp, dst, ADFLAGS_HF, O_RDWR) == 0) {
         ad_setname(adp, newname);
         ad_flush( adp );
         ad_close( adp, ADFLAGS_HF );
     }
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "end renamefile:");
-#endif /* DEBUG */
 
     return( AFP_OK );
 }
@@ -1201,14 +1244,15 @@ int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, si
 
     adp = of_ad(s_vol, s_path, &ad);
 
-    if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
+    if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 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 );
+    denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || 
+                  ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
+
     if (denyreadset) {
-        return AFPERR_DENYCONF;
+        retvalue = AFPERR_DENYCONF;
+        goto copy_exit;
     }
 
     newname = obj->newtmp;
@@ -1216,53 +1260,58 @@ int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, si
 
     p = ctoupath( s_vol, curdir, newname );
     if (!p) {
-        return AFPERR_PARAM;
-    
+        retvalue = AFPERR_PARAM;
+        goto copy_exit;
     }
-#ifdef FORCE_UIDGID
-    /* FIXME svid != dvid && dvid's user can't read svid */
-#endif
+
     if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
-        return( AFPERR_PARAM );
+        retvalue = AFPERR_PARAM;
+        goto copy_exit;
     }
 
-    if (d_vol->v_flags & AFPVOL_RO)
-        return AFPERR_VLOCK;
+    if (d_vol->v_flags & AFPVOL_RO) {
+        retvalue = AFPERR_VLOCK;
+        goto copy_exit;
+    }
 
     if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
-        return afp_errno;
+        retvalue = afp_errno;
+        goto copy_exit;
     }
 
     if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
-        return get_afp_errno(AFPERR_NOOBJ); 
+        retvalue = get_afp_errno(AFPERR_NOOBJ);
+        goto copy_exit;
     }
+    
     if ( *s_path->m_name != '\0' ) {
-       path_error(s_path, AFPERR_PARAM);
+       retvalue =path_error(s_path, AFPERR_NOOBJ);
+        goto copy_exit;
     }
 
     /* one of the handful of places that knows about the path type */
     if (copy_path_name(d_vol, newname, ibuf) < 0) {
-        return( AFPERR_PARAM );
+        retvalue = AFPERR_PARAM;
+        goto copy_exit;
     }
     /* 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 );
+        retvalue =AFPERR_PARAM;
+        goto copy_exit;
     }
-    if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
-        return err;
-    }
-    curdir->offcnt++;
 
-#ifdef DROPKLUDGE
-    if (vol->v_flags & AFPVOL_DROPBOX) {
-        retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
+    if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
+        retvalue = err;
+        goto copy_exit;
     }
-#endif /* DROPKLUDGE */
+    curdir->offcnt++;
 
     setvoltime(obj, d_vol );
 
+copy_exit:
+    ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
     return( retvalue );
 }
 
@@ -1369,8 +1418,13 @@ static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
  * 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)
+int copyfile(const struct vol *s_vol,
+             const struct vol *d_vol, 
+             int sfd,
+             char *src,
+             char *dst,
+             char *newname,
+             struct adouble *adp)
 {
     struct adouble     ads, add;
     int                        err = 0;
@@ -1379,21 +1433,20 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol,
     int                 stat_result;
     struct stat         st;
     
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "begin copyfile:");
-#endif /* DEBUG */
+    LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
+        sfd, src, dst, newname);
 
     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 (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
+    if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
         ret_err = errno;
         goto done;
     }
@@ -1410,11 +1463,12 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol,
       st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
     }
 
-    if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
+    ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
+    if (ad_open(&add, dst, adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, O_RDWR|O_CREAT|O_EXCL, st.st_mode) < 0) {
         ret_err = errno;
         ad_close( adp, adflags );
         if (EEXIST != ret_err) {
-            deletefile(d_vol, dst, 0);
+            deletefile(d_vol, -1, dst, 0);
             goto done;
         }
         return AFPERR_EXIST;
@@ -1426,7 +1480,7 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol,
     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);
+            err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
         }
     }
 
@@ -1447,7 +1501,7 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol,
     } 
 
     if (ret_err) {
-        deletefile(d_vol, dst, 0);
+        deletefile(d_vol, -1, dst, 0);
     }
     else if (stat_result == 0) {
         /* set dest modification date to src date */
@@ -1460,10 +1514,6 @@ int copyfile(const struct vol *s_vol, const struct vol*d_vol,
        */
     }
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "end copyfile:");
-#endif /* DEBUG */
-
 done:
     switch ( ret_err ) {
     case 0:
@@ -1510,59 +1560,57 @@ u_int16_t   bshort = 0;
        }
        return 0;
 }
-
-int deletefile(const struct vol *vol, char *file, int checkAttrib)
+/* 
+ * dirfd can be used for unlinkat semantics
+ */
+int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
 {
     struct adouble     ad;
-    struct adouble      *adp = &ad;
+    struct adouble      *adp = NULL;
     int                        adflags, err = AFP_OK;
+    int                        meta = 0;
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "begin deletefile:");
-#endif /* DEBUG */
+    LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
 
-    /* try to open both forks at once */
-    adflags = ADFLAGS_DF|ADFLAGS_HF;
+    ad_init(&ad, vol->v_adouble, vol->v_ad_options);
     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 ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
             if ((err = check_attrib(&ad))) {
+               ad_close_metadata(&ad);
                return err;
             }
+            meta = 1;
         }
     }
  
-    while(1) {
-       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:
-                if (adflags == ADFLAGS_DF)
-                    return AFPERR_NOOBJ;
-                   
-                /* that failed. now try to open just the data fork */
-                adflags = ADFLAGS_DF;
-                continue;
-
-            case EACCES:
-                adp = NULL; /* maybe it's a file with no write mode for us */
-                break;      /* was return AFPERR_ACCESS;*/
-            case EROFS:
-                return AFPERR_VLOCK;
-            default:
-                return( AFPERR_PARAM );
-            }
+    /* try to open both forks at once */
+    adflags = ADFLAGS_DF;
+    if ( ad_openat(&ad, dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0 ) {
+        switch (errno) {
+        case ENOENT:
+            err = AFPERR_NOOBJ;
+            goto end;
+        case EACCES: /* maybe it's a file with no write mode for us */
+            break;   /* was return AFPERR_ACCESS;*/
+        case EROFS:
+            err = AFPERR_VLOCK;
+            goto end;
+        default:
+            err = AFPERR_PARAM;
+            goto end;
         }
-        break; /* from the while */
+    }
+    else {
+        adp = &ad;
     }
 
-    if (adp && (adflags & ADFLAGS_HF) ) {
+    if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
+        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.
@@ -1573,28 +1621,27 @@ int deletefile(const struct vol *vol, char *file, int checkAttrib)
          * FIXME it doesn't work for RFORK open read only and fork open without deny mode
          */
         if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
-            ad_close( &ad, adflags );
-            return( AFPERR_BUSY );
+            err = AFPERR_BUSY;
+            goto end;
         }
     }
 
     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
         err = AFPERR_BUSY;
-    }
-    else if (!(err = vol->vfs->vfs_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
+    } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
         cnid_t id;
-        if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) 
-        {
+        if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
             cnid_delete(vol->v_cdb, id);
         }
     }
+
+end:
+    if (meta)
+        ad_close_metadata(&ad);
+
     if (adp)
         ad_close( &ad, adflags );  /* ad_close removes locks if any */
 
-#ifdef DEBUG
-    LOG(log_debug9, logtype_afpd, "end deletefile:");
-#endif /* DEBUG */
-
     return err;
 }
 
@@ -1686,32 +1733,12 @@ static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
     cnid_t        did  = param->did;
     cnid_t       aint;
     
-    if ( lstat(de->d_name, &path.st)<0 )
+    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;
 }
 
@@ -1897,6 +1924,10 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     }
 
     if (NULL == ( dir = dirlookup( vol, id )) ) {
+        if (afp_errno == AFPERR_NOOBJ) {
+            err = AFPERR_NOOBJ;
+            goto delete;
+        }
         return( AFPERR_PARAM );
     }
 
@@ -1920,6 +1951,7 @@ int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
     else if (S_ISDIR(st.st_mode)) /* directories are bad */
         return AFPERR_BADTYPE;
 
+delete:
     if (cnid_delete(vol->v_cdb, fileid)) {
         switch (errno) {
         case EROFS:
@@ -1968,7 +2000,7 @@ static struct adouble *find_adouble(struct path *path, struct ofork **of, struct
         adp = (*of)->of_ad;
     }
     else {
-        ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
+        ret = ad_open(adp, path->u_name, ADFLAGS_HF, O_RDONLY);
         /* META and HF */
         if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
             /* from AFP spec.
@@ -2123,29 +2155,28 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
     }
 
     /* now, quickly rename the file. we error if we can't. */
-    if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
+    if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
         goto err_exchangefile;
     of_rename(vol, s_of, sdir, spath, curdir, temp);
 
     /* rename destination to source */
-    if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
+    if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
         goto err_src_to_tmp;
     of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
 
     /* rename temp to destination */
-    if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
+    if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
         goto err_dest_to_src;
     of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
 
     /* 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 (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))
        ||
@@ -2218,17 +2249,17 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U
      * properly. */
 err_temp_to_dest:
     /* rename dest to temp */
-    renamefile(vol, upath, temp, temp, adsp);
+    renamefile(vol, -1, upath, temp, temp, adsp);
     of_rename(vol, s_of, curdir, upath, curdir, temp);
 
 err_dest_to_src:
     /* rename source back to dest */
-    renamefile(vol, p, upath, path->m_name, addp);
+    renamefile(vol, -1, 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 */
-    renamefile(vol, temp, p, spath, adsp);
+    renamefile(vol, -1, temp, p, spath, adsp);
     of_rename(vol, s_of, curdir, temp, sdir, spath);
 
 err_exchangefile:
@@ -2239,5 +2270,11 @@ err_exchangefile:
        ad_close(addp, ADFLAGS_HF);
     }
 
+    struct dir *cached;
+    if ((cached = dircache_search_by_did(vol, sid)) != NULL)
+        (void)dir_remove(vol, cached);
+    if ((cached = dircache_search_by_did(vol, did)) != NULL)
+        (void)dir_remove(vol, cached);
+
     return err;
 }