]> arthur.barton.de Git - netatalk.git/blobdiff - etc/afpd/fork.c
Add recvfile support with splice() on Linux
[netatalk.git] / etc / afpd / fork.c
index 2e235888590e5eed2ebe143ccb91b732f8361bf4..73abb1e9a102e5a5143303a08011b976cdc982f8 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * $Id: fork.c,v 1.73 2010-03-30 12:55:26 franklahm Exp $
- *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
+ * Copyright (c) 2010      Frank Lahm
+ *
  * All Rights Reserved.  See COPYRIGHT.
  */
 
 #endif /* HAVE_CONFIG_H */
 
 #include <stdio.h>
-
 #include <string.h>
 #include <errno.h>
-
-#include <atalk/adouble.h>
-#include <atalk/logger.h>
-
 #include <sys/param.h>
 #include <sys/socket.h>
-
-#include <netatalk/at.h>
+#include <inttypes.h>
 
 #include <atalk/dsi.h>
-#include <atalk/atp.h>
-#include <atalk/asp.h>
 #include <atalk/afp.h>
+#include <atalk/adouble.h>
+#include <atalk/logger.h>
 #include <atalk/util.h>
 #include <atalk/cnid.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
 #include <atalk/globals.h>
+#include <atalk/netatalk_conf.h>
+#include <atalk/ea.h>
 
 #include "fork.h"
 #include "file.h"
 #include "desktop.h"
 #include "volume.h"
 
-#ifdef DEBUG1
-#define Debug(a) ((a)->options.flags & OPTION_DEBUG)
-#else
-#define Debug(a) (0)
-#endif
-
 #ifdef AFS
 struct ofork *writtenfork;
 #endif
 
-static int getforkparams(struct ofork *ofork, u_int16_t bitmap, char *buf, size_t *buflen)
+static int getforkparams(const AFPObj *obj, struct ofork *ofork, uint16_t bitmap, char *buf, size_t *buflen)
 {
     struct path         path;
-    struct stat                *st;
+    struct stat     *st;
+
+    struct adouble  *adp;
+    struct dir      *dir;
+    struct vol      *vol;
 
-    struct adouble     *adp;
-    struct dir         *dir;
-    struct vol         *vol;
-    
 
     /* can only get the length of the opened fork */
-    if ( ( (bitmap & ((1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN))) 
-                  && (ofork->of_flags & AFPFORK_RSRC)) 
-        ||
-          ( (bitmap & ((1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN))) 
-                  && (ofork->of_flags & AFPFORK_DATA))) {
+    if ( ( (bitmap & ((1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN)))
+           && (ofork->of_flags & AFPFORK_RSRC))
+         ||
+         ( (bitmap & ((1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN)))
+           && (ofork->of_flags & AFPFORK_DATA))) {
         return( AFPERR_BITMAP );
     }
 
-    if ( ad_reso_fileno( ofork->of_ad ) == -1 ) { /* META ? */
+    if (! AD_META_OPEN(ofork->of_ad)) {
         adp = NULL;
     } else {
         adp = ofork->of_ad;
@@ -74,14 +66,14 @@ static int getforkparams(struct ofork *ofork, u_int16_t bitmap, char *buf, size_
     vol = ofork->of_vol;
     dir = dirlookup(vol, ofork->of_did);
 
-    if (NULL == (path.u_name = mtoupath(vol, of_name(ofork), dir->d_did, utf8_encoding()))) {
+    if (NULL == (path.u_name = mtoupath(vol, of_name(ofork), dir->d_did, utf8_encoding(obj)))) {
         return( AFPERR_MISC );
     }
     path.m_name = of_name(ofork);
     path.id = 0;
     st = &path.st;
-    if ( bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) | 
-                    (1<<FILPBIT_FNUM) | (1 << FILPBIT_CDATE) | 
+    if ( bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) |
+                    (1<<FILPBIT_FNUM) | (1 << FILPBIT_CDATE) |
                     (1 << FILPBIT_MDATE) | (1 << FILPBIT_BDATE))) {
         if ( ad_data_fileno( ofork->of_ad ) <= 0 ) {
             /* 0 is for symlink */
@@ -95,13 +87,12 @@ static int getforkparams(struct ofork *ofork, u_int16_t bitmap, char *buf, size_
             }
         }
     }
-    return getmetadata(vol, bitmap, &path, dir, buf, buflen, adp );
+    return getmetadata(obj, vol, bitmap, &path, dir, buf, buflen, adp );
 }
 
-/* ---------------------------- */
 static off_t get_off_t(char **ibuf, int is64)
 {
-    u_int32_t             temp;
+    uint32_t             temp;
     off_t                 ret;
 
     ret = 0;
@@ -115,15 +106,14 @@ static off_t get_off_t(char **ibuf, int is64)
         ret = ntohl(temp)| (ret << 32);
     }
     else {
-       ret = (int)ret; /* sign extend */
+        ret = (int)ret; /* sign extend */
     }
     return ret;
 }
 
-/* ---------------------- */
 static int set_off_t(off_t offset, char *rbuf, int is64)
 {
-    u_int32_t  temp;
+    uint32_t  temp;
     int        ret;
 
     ret = 0;
@@ -141,39 +131,21 @@ static int set_off_t(off_t offset, char *rbuf, int is64)
     return ret;
 }
 
-/* ------------------------ 
-*/
 static int is_neg(int is64, off_t val)
 {
     if (val < 0 || (sizeof(off_t) == 8 && !is64 && (val & 0x80000000U)))
-       return 1;
+        return 1;
     return 0;
 }
 
-static int sum_neg(int is64, off_t offset, off_t reqcount) 
+static int sum_neg(int is64, off_t offset, off_t reqcount)
 {
-    if (is_neg(is64, offset +reqcount) ) 
-       return 1;
+    if (is_neg(is64, offset +reqcount) )
+        return 1;
     return 0;
 }
 
-/* -------------------------
-*/
-static int setforkmode(struct adouble *adp, int eid, int ofrefnum, off_t what)
-{
-    return ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, what, 1, ofrefnum);
-}
-
-/* -------------------------
-*/
-int getforkmode(struct adouble *adp, int eid, off_t what)
-{
-    return ad_testlock(adp, eid,  what);
-}
-
-/* -------------------------
-*/
-static int fork_setmode(struct adouble *adp, int eid, int access, int ofrefnum)
+static int fork_setmode(const AFPObj *obj, struct adouble *adp, int eid, int access, int ofrefnum)
 {
     int ret;
     int readset;
@@ -181,14 +153,35 @@ static int fork_setmode(struct adouble *adp, int eid, int access, int ofrefnum)
     int denyreadset;
     int denywriteset;
 
+#ifdef HAVE_FSHARE_T
+    fshare_t shmd;
+
+    if (obj->options.flags & OPTION_SHARE_RESERV) {
+        shmd.f_access = (access & OPENACC_RD ? F_RDACC : 0) | (access & OPENACC_WR ? F_WRACC : 0);
+        if (shmd.f_access == 0)
+            /* we must give an access mode, otherwise fcntl will complain */
+            shmd.f_access = F_RDACC;
+        shmd.f_deny = (access & OPENACC_DRD ? F_RDDNY : F_NODNY) | (access & OPENACC_DWR) ? F_WRDNY : 0;
+        shmd.f_id = ofrefnum;
+
+        int fd = (eid == ADEID_DFORK) ? ad_data_fileno(adp) : ad_reso_fileno(adp);
+
+        if (fd != -1 && fd != AD_SYMLINK && fcntl(fd, F_SHARE, &shmd) != 0) {
+            LOG(log_debug, logtype_afpd, "fork_setmode: fcntl: %s", strerror(errno));
+            errno = EACCES;
+            return -1;
+        }
+    }
+#endif
+
     if (! (access & (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD))) {
-        return setforkmode(adp, eid, ofrefnum, AD_FILELOCK_OPEN_NONE);
+        return ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_NONE, 1, ofrefnum);
     }
 
     if ((access & (OPENACC_RD | OPENACC_DRD))) {
-        if ((readset = getforkmode(adp, eid, AD_FILELOCK_OPEN_RD)) <0)
+        if ((readset = ad_testlock(adp, eid, AD_FILELOCK_OPEN_RD)) <0)
             return readset;
-        if ((denyreadset = getforkmode(adp, eid, AD_FILELOCK_DENY_RD)) <0)
+        if ((denyreadset = ad_testlock(adp, eid, AD_FILELOCK_DENY_RD)) <0)
             return denyreadset;
 
         if ((access & OPENACC_RD) && denyreadset) {
@@ -198,26 +191,26 @@ static int fork_setmode(struct adouble *adp, int eid, int access, int ofrefnum)
         if ((access & OPENACC_DRD) && readset) {
             errno = EACCES;
             return -1;
-        }   
+        }
         /* boolean logic is not enough, because getforkmode is not always telling the
-         * true 
+         * true
          */
         if ((access & OPENACC_RD)) {
-            ret = setforkmode(adp, eid, ofrefnum, AD_FILELOCK_OPEN_RD);
+            ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_RD, 1, ofrefnum);
             if (ret)
                 return ret;
         }
         if ((access & OPENACC_DRD)) {
-            ret = setforkmode(adp, eid, ofrefnum, AD_FILELOCK_DENY_RD);
+            ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_DENY_RD, 1, ofrefnum);
             if (ret)
                 return ret;
         }
     }
     /* ------------same for writing -------------- */
     if ((access & (OPENACC_WR | OPENACC_DWR))) {
-        if ((writeset = getforkmode(adp, eid, AD_FILELOCK_OPEN_WR)) <0)
+        if ((writeset = ad_testlock(adp, eid, AD_FILELOCK_OPEN_WR)) <0)
             return writeset;
-        if ((denywriteset = getforkmode(adp, eid, AD_FILELOCK_DENY_WR)) <0)
+        if ((denywriteset = ad_testlock(adp, eid, AD_FILELOCK_DENY_WR)) <0)
             return denywriteset;
 
         if ((access & OPENACC_WR) && denywriteset) {
@@ -227,40 +220,38 @@ static int fork_setmode(struct adouble *adp, int eid, int access, int ofrefnum)
         if ((access & OPENACC_DWR) && writeset) {
             errno = EACCES;
             return -1;
-        }   
+        }
         if ((access & OPENACC_WR)) {
-            ret = setforkmode(adp, eid, ofrefnum, AD_FILELOCK_OPEN_WR);
+            ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_WR, 1, ofrefnum);
             if (ret)
                 return ret;
         }
         if ((access & OPENACC_DWR)) {
-            ret = setforkmode(adp, eid, ofrefnum, AD_FILELOCK_DENY_WR);
+            ret = ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_DENY_WR, 1, ofrefnum);
             if (ret)
                 return ret;
         }
     }
-    if ( access == (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD)) {
-        return ad_excl_lock(adp, eid);
-    }
+
     return 0;
 }
 
 /* ----------------------- */
 int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
 {
-    struct vol         *vol;
-    struct dir         *dir;
-    struct ofork       *ofork, *opened;
-    struct adouble      *adsame = NULL;
-    size_t             buflen;
-    int                        ret, adflags, eid;
-    u_int32_t           did;
-    u_int16_t          vid, bitmap, access, ofrefnum;
-    char               fork, *path, *upath;
-    struct stat         *st;
-    u_int16_t           bshort;
-    struct path         *s_path;
-    
+    struct vol      *vol;
+    struct dir      *dir;
+    struct ofork    *ofork, *opened;
+    struct adouble  *adsame = NULL;
+    size_t          buflen;
+    int             ret, adflags, eid;
+    uint32_t        did;
+    uint16_t        vid, bitmap, access, ofrefnum;
+    char            fork, *path, *upath;
+    struct stat     *st;
+    uint16_t        bshort;
+    struct path     *s_path;
+
     ibuf++;
     fork = *ibuf++;
     memcpy(&vid, ibuf, sizeof( vid ));
@@ -275,7 +266,7 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     ibuf += sizeof( int );
 
     if (NULL == ( dir = dirlookup( vol, did ))) {
-       return afp_errno;    
+        return afp_errno;
     }
 
     memcpy(&bitmap, ibuf, sizeof( bitmap ));
@@ -290,12 +281,12 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     }
 
     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
-       return get_afp_errno(AFPERR_PARAM);    
+        return get_afp_errno(AFPERR_PARAM);
     }
 
     if (*s_path->m_name == '\0') {
-       /* it's a dir ! */
-       return  AFPERR_BADTYPE;
+        /* it's a dir ! */
+        return  AFPERR_BADTYPE;
     }
 
     /* stat() data fork st is set because it's not a dir */
@@ -307,168 +298,150 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     case EACCES:
         return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
     default:
-        LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) );
+        LOG(log_error, logtype_afpd, "afp_openfork(%s): %s", s_path->m_name, strerror(errno));
         return AFPERR_PARAM;
     }
-    /* FIXME should we check it first ? */
+
     upath = s_path->u_name;
+    path = s_path->m_name;
+    st = &s_path->st;
+
     if (!vol_unix_priv(vol)) {
-        if (check_access(upath, access ) < 0) {
+        if (check_access(obj, vol, upath, access ) < 0) {
             return AFPERR_ACCESS;
         }
-    }
-    else {
-        if (file_access(s_path, access ) < 0) {
+    } else {
+        if (file_access(obj, vol, s_path, access ) < 0) {
             return AFPERR_ACCESS;
         }
     }
 
-    st   = &s_path->st;
-    /* XXX: this probably isn't the best way to do this. the already
-       open bits should really be set if the fork is opened by any
-       program, not just this one. however, that's problematic to do
-       if we can't write lock files somewhere. opened is also passed to 
-       ad_open so that we can keep file locks together.
-       FIXME: add the fork we are opening? 
-    */
-    if ((opened = of_findname(s_path))) {
+    if ((opened = of_findname(vol, s_path))) {
         adsame = opened->of_ad;
     }
 
-    if ( fork == OPENFORK_DATA ) {
+    adflags = ADFLAGS_SETSHRMD;
+
+    if (fork == OPENFORK_DATA) {
         eid = ADEID_DFORK;
-        adflags = ADFLAGS_DF|ADFLAGS_HF;
+        adflags |= ADFLAGS_DF;
     } else {
         eid = ADEID_RFORK;
-        adflags = ADFLAGS_HF;
+        adflags |= ADFLAGS_RF;
     }
 
-    path = s_path->m_name;
-    if (( ofork = of_alloc(vol, curdir, path, &ofrefnum, eid,
-                           adsame, st)) == NULL ) {
-        return( AFPERR_NFILE );
+    if (access & OPENACC_WR) {
+        adflags |= ADFLAGS_RDWR;
+        if (fork != OPENFORK_DATA)
+            /*
+             * We only try to create the resource
+             * fork if the user wants to open it for write acess.
+             */
+            adflags |= ADFLAGS_CREATE;
+    } else {
+        adflags |= ADFLAGS_RDONLY;
     }
 
+    if ((ofork = of_alloc(vol, curdir, path, &ofrefnum, eid, adsame, st)) == NULL)
+        return AFPERR_NFILE;
+
+    LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\", %s, %s)",
+        fullpathname(s_path->u_name),
+        (fork == OPENFORK_DATA) ? "data" : "reso",
+        !(access & OPENACC_WR) ? "O_RDONLY" : "O_RDWR");
+
     ret = AFPERR_NOOBJ;
-    if (access & OPENACC_WR) {
-        /* try opening in read-write mode */
-        if (ad_open(upath, adflags, O_RDWR, 0, ofork->of_ad) < 0) {
-            switch ( errno ) {
-            case EROFS:
-                ret = AFPERR_VLOCK;
-            case EACCES:
-                goto openfork_err;
-                break;
-            case ENOENT:
-                if (fork == OPENFORK_DATA) {
-                    /* try to open only the data fork */
-                    if (ad_open(upath, ADFLAGS_DF, O_RDWR, 0, ofork->of_ad) < 0) {
-                        goto openfork_err;
-                    }
-                    adflags = ADFLAGS_DF;
-                }
-                else {
-                    /* here's the deal. we only try to create the resource
-                    * fork if the user wants to open it for write acess. */
-                    if (ad_open(upath, adflags, O_RDWR | O_CREAT, 0666, ofork->of_ad) < 0)
-                        goto openfork_err;
-                    ofork->of_flags |= AFPFORK_OPEN;
-                }
-                break;
-            case EMFILE :
-            case ENFILE :
-                ret = AFPERR_NFILE;
-                goto openfork_err;
-                break;
-            case EISDIR :
-                ret = AFPERR_BADTYPE;
-                goto openfork_err;
-                break;
-            default:
-                LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) );
-                ret = AFPERR_PARAM;
-                goto openfork_err;
-                break;
-            }
-        }
-        else {
-            /* the ressource fork is open too */
-            ofork->of_flags |= AFPFORK_OPEN;
+
+    /* First ad_open(), opens data or ressource fork */
+    if (ad_open(ofork->of_ad, upath, adflags, 0666) < 0) {
+        switch (errno) {
+        case EROFS:
+            ret = AFPERR_VLOCK;
+        case EACCES:
+            goto openfork_err;
+        case ENOENT:
+            goto openfork_err;
+        case EMFILE :
+        case ENFILE :
+            ret = AFPERR_NFILE;
+            goto openfork_err;
+        case EISDIR :
+            ret = AFPERR_BADTYPE;
+            goto openfork_err;
+        default:
+            LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) );
+            ret = AFPERR_PARAM;
+            goto openfork_err;
         }
+    }
+
+    /*
+     * Create metadata if we open rw, otherwise only open existing metadata
+     */
+    if (access & OPENACC_WR) {
+        adflags = ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE;
     } else {
-        /* try opening in read-only mode */
-        ret = AFPERR_NOOBJ;
-        if (ad_open(upath, adflags, O_RDONLY, 0, ofork->of_ad) < 0) {
-            switch ( errno ) {
-            case EROFS:
-                ret = AFPERR_VLOCK;
-            case EACCES:
-                goto openfork_err;
-                break;
-            case ENOENT:
-                /* see if client asked for a read only data fork */
-                if (fork == OPENFORK_DATA) {
-                    if (ad_open(upath, ADFLAGS_DF, O_RDONLY, 0, ofork->of_ad) < 0) {
-                        goto openfork_err;
-                    }
-                    adflags = ADFLAGS_DF;
-                }
-                /* else we don't set AFPFORK_OPEN because there's no ressource fork file 
-                 * We need to check AFPFORK_OPEN in afp_closefork(). eg fork open read-only
-                 * then create in open read-write.
-                 * FIXME , it doesn't play well with byte locking example:
-                 * ressource fork open read only
-                 * locking set on it (no effect, there's no file!)
-                 * ressource fork open read write now
-                */
-                break;
-            case EMFILE :
-            case ENFILE :
-                ret = AFPERR_NFILE;
-                goto openfork_err;
-                break;
-            case EISDIR :
-                ret = AFPERR_BADTYPE;
-                goto openfork_err;
-                break;
-            default:
-                LOG(log_error, logtype_afpd, "afp_openfork('%s/%s'): ad_open: errno: %i (%s)",
-                    getcwdpath, s_path->m_name, errno, strerror(errno) );
+        adflags = ADFLAGS_HF | ADFLAGS_RDONLY;
+    }
+
+    if (ad_open(ofork->of_ad, upath, adflags, 0666) == 0) {
+        ofork->of_flags |= AFPFORK_META;
+        if (ad_get_MD_flags(ofork->of_ad) & O_CREAT) {
+            LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\"): setting CNID", upath);
+            cnid_t id;
+            if ((id = get_id(vol, ofork->of_ad, st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
+                LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
                 goto openfork_err;
-                break;
             }
+            (void)ad_setid(ofork->of_ad, st->st_dev, st->st_ino, id, dir->d_did, vol->v_stamp);
+            ad_flush(ofork->of_ad);
         }
-        else {
-            /* the ressource fork is open too */
-            ofork->of_flags |= AFPFORK_OPEN;
+    } else {
+        switch (errno) {
+        case EACCES:
+        case ENOENT:
+            /* no metadata? We don't care! */
+            break;
+        case EROFS:
+            ret = AFPERR_VLOCK;
+        case EMFILE :
+        case ENFILE :
+            ret = AFPERR_NFILE;
+            goto openfork_err;
+        case EISDIR :
+            ret = AFPERR_BADTYPE;
+            goto openfork_err;
+        default:
+            LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) );
+            ret = AFPERR_PARAM;
+            goto openfork_err;
         }
     }
 
-    if ((adflags & ADFLAGS_HF) && (ad_get_HF_flags( ofork->of_ad) & O_CREAT)) {
+    if ((adflags & ADFLAGS_RF) && (ad_get_RF_flags( ofork->of_ad) & O_CREAT)) {
         if (ad_setname(ofork->of_ad, path)) {
             ad_flush( ofork->of_ad );
         }
     }
 
-    if (( ret = getforkparams(ofork, bitmap, rbuf + 2 * sizeof( u_int16_t ),
-                              &buflen )) != AFP_OK ) {
-        ad_close( ofork->of_ad, adflags );
+    if ((ret = getforkparams(obj, ofork, bitmap, rbuf + 2 * sizeof(int16_t), &buflen)) != AFP_OK) {
+        ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD);
         goto openfork_err;
     }
 
-    *rbuflen = buflen + 2 * sizeof( u_int16_t );
+    *rbuflen = buflen + 2 * sizeof( uint16_t );
     bitmap = htons( bitmap );
-    memcpy(rbuf, &bitmap, sizeof( u_int16_t ));
-    rbuf += sizeof( u_int16_t );
+    memcpy(rbuf, &bitmap, sizeof( uint16_t ));
+    rbuf += sizeof( uint16_t );
 
     /* check  WriteInhibit bit if we have a ressource fork
-     * the test is done here, after some Mac trafic capture 
+     * the test is done here, after some Mac trafic capture
      */
     if (ad_meta_fileno(ofork->of_ad) != -1) {   /* META */
         ad_getattr(ofork->of_ad, &bshort);
         if ((bshort & htons(ATTRBIT_NOWRITE)) && (access & OPENACC_WR)) {
-            ad_close( ofork->of_ad, adflags );
-            of_dealloc( ofork );
+            ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD);
+            of_dealloc(ofork);
             ofrefnum = 0;
             memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
             return(AFPERR_OLOCK);
@@ -480,14 +453,16 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
      */
 
     /* don't try to lock non-existent rforks. */
-    if ((eid == ADEID_DFORK) || (ad_meta_fileno(ofork->of_ad) != -1)) { /* META */
-
-        ret = fork_setmode(ofork->of_ad, eid, access, ofrefnum);
+    if ((eid == ADEID_DFORK)
+        || (ad_reso_fileno(ofork->of_ad) != -1)
+        || (ofork->of_ad->ad_vers == AD_VERSION_EA)) {
+        ret = fork_setmode(obj, ofork->of_ad, eid, access, ofrefnum);
         /* can we access the fork? */
         if (ret < 0) {
+            ofork->of_flags |= AFPFORK_ERROR;
             ret = errno;
-            ad_close( ofork->of_ad, adflags );
-            of_dealloc( ofork );
+            ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD);
+            of_dealloc(ofork);
             switch (ret) {
             case EAGAIN: /* return data anyway */
             case EACCES:
@@ -509,26 +484,29 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     if ((access & OPENACC_RD))
         ofork->of_flags |= AFPFORK_ACCRD;
 
+    LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\"): fork: %" PRIu16,
+        fullpathname(s_path->m_name), ofork->of_refnum);
+
     memcpy(rbuf, &ofrefnum, sizeof(ofrefnum));
     return( AFP_OK );
 
 openfork_err:
-    of_dealloc( ofork );
+    of_dealloc(ofork);
     if (errno == EACCES)
         return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS;
     return ret;
 }
 
-int afp_setforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen, char *rbuf _U_, size_t *rbuflen)
+int afp_setforkparams(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf _U_, size_t *rbuflen)
 {
-    struct ofork       *ofork;
-    off_t              size;
-    u_int16_t          ofrefnum, bitmap;
+    struct ofork    *ofork;
+    off_t       size;
+    uint16_t       ofrefnum, bitmap;
     int                 err;
     int                 is64;
     int                 eid;
-    off_t              st_size;
-    
+    off_t       st_size;
+
     ibuf += 2;
 
     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
@@ -557,26 +535,26 @@ int afp_setforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen, char *rbuf _U
     } else
         return AFPERR_PARAM;
 
-    if ( ( (bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) )) 
-                  && eid == ADEID_RFORK 
-         ) ||
-         ( (bitmap & ( (1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN) )) 
-                  && eid == ADEID_DFORK)) {
+    if ( ( (bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) ))
+           && eid == ADEID_RFORK
+             ) ||
+         ( (bitmap & ( (1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN) ))
+           && eid == ADEID_DFORK)) {
         return AFPERR_BITMAP;
     }
-    
+
     is64 = 0;
     if ((bitmap & ( (1<<FILPBIT_EXTDFLEN) | (1<<FILPBIT_EXTRFLEN) ))) {
-        if (afp_version >= 30) {
+        if (obj->afp_version >= 30) {
             is64 = 4;
         }
-        else 
-           return AFPERR_BITMAP;
+        else
+            return AFPERR_BITMAP;
     }
 
     if (ibuflen < 2+ sizeof(ofrefnum) + sizeof(bitmap) + is64 +4)
         return AFPERR_PARAM ;
-    
+
     size = get_off_t(&ibuf, is64);
 
     if (size < 0)
@@ -584,29 +562,30 @@ int afp_setforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen, char *rbuf _U
 
 
     if (bitmap == (1<<FILPBIT_DFLEN) || bitmap == (1<<FILPBIT_EXTDFLEN)) {
-       st_size = ad_size(ofork->of_ad, eid);
-       err = -2;
-       if (st_size > size && 
-             ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0) 
+        st_size = ad_size(ofork->of_ad, eid);
+        err = -2;
+        if (st_size > size &&
+            ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0)
             goto afp_setfork_err;
 
         err = ad_dtruncate( ofork->of_ad, size );
         if (st_size > size)
-           ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);  
+            ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
         if (err < 0)
             goto afp_setfork_err;
     } else if (bitmap == (1<<FILPBIT_RFLEN) || bitmap == (1<<FILPBIT_EXTRFLEN)) {
-        ad_refresh( ofork->of_ad );
+        ad_refresh(NULL, ofork->of_ad );
 
-       st_size = ad_size(ofork->of_ad, eid);
-       err = -2;
-       if (st_size > size && 
-              ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0) {
+        st_size = ad_size(ofork->of_ad, eid);
+        err = -2;
+        if (st_size > size &&
+            ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, size, st_size -size, ofork->of_refnum) < 0) {
             goto afp_setfork_err;
-       }
-        err = ad_rtruncate(ofork->of_ad, size);
+        }
+
+        err = ad_rtruncate(ofork->of_ad, mtoupath(ofork->of_vol, of_name(ofork), ofork->of_did, utf8_encoding(obj)), size);
         if (st_size > size)
-           ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);  
+            ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum);
         if (err < 0)
             goto afp_setfork_err;
 
@@ -638,6 +617,7 @@ afp_setfork_err:
         case EDQUOT:
         case EFBIG:
         case ENOSPC:
+            LOG(log_error, logtype_afpd, "afp_setforkparams: DISK FULL");
             return AFPERR_DFULL;
         default:
             return AFPERR_PARAM;
@@ -649,7 +629,7 @@ afp_setfork_err:
  * read and write. that's most easily handled by always doing an
  * appropriate check before each ad_read/ad_write. other things
  * that can change files like truncate are handled internally to those
- * functions. 
+ * functions.
  */
 #define ENDBIT(a)  ((a) & 0x80)
 #define UNLOCKBIT(a) ((a) & 0x01)
@@ -658,13 +638,13 @@ afp_setfork_err:
 /* ---------------------- */
 static int byte_lock(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
 {
-    struct ofork       *ofork;
+    struct ofork    *ofork;
     off_t               offset, length;
     int                 eid;
-    u_int16_t          ofrefnum;
-    u_int8_t            flags;
+    uint16_t       ofrefnum;
+    uint8_t            flags;
     int                 lockop;
-    
+
     *rbuflen = 0;
 
     /* figure out parameters */
@@ -675,7 +655,7 @@ static int byte_lock(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf
     ibuf += sizeof(ofrefnum);
 
     if (NULL == ( ofork = of_find( ofrefnum )) ) {
-        LOG(log_error, logtype_afpd, "afp_bytelock: of_find(%d) could not locate fork", ofrefnum );
+        LOG(log_error, logtype_afpd, "byte_lock: of_find(%d) could not locate fork", ofrefnum );
         return( AFPERR_PARAM );
     }
 
@@ -689,18 +669,17 @@ static int byte_lock(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf
     offset = get_off_t(&ibuf, is64);
     length = get_off_t(&ibuf, is64);
 
-    /* FIXME AD_FILELOCK test is surely wrong */
     if (length == -1)
         length = BYTELOCK_MAX;
-     else if (!length || is_neg(is64, length)) {
-       return AFPERR_PARAM;
-     } else if ((length >= AD_FILELOCK_BASE) && -1 == (ad_reso_fileno(ofork->of_ad))) { /* HF ?*/
+    else if (!length || is_neg(is64, length)) {
+        return AFPERR_PARAM;
+    } else if ((length >= AD_FILELOCK_BASE) && -1 == (ad_reso_fileno(ofork->of_ad))) { /* HF ?*/
         return AFPERR_LOCK;
     }
 
     if (ENDBIT(flags)) {
         offset += ad_size(ofork->of_ad, eid);
-        /* FIXME what do we do if file size > 2 GB and 
+        /* FIXME what do we do if file size > 2 GB and
            it's not byte_lock_ext?
         */
     }
@@ -737,45 +716,31 @@ static int byte_lock(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf
 /* --------------------------- */
 int afp_bytelock(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
 {
-   return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 0);
+    return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 0);
 }
 
 /* --------------------------- */
 int afp_bytelock_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
 {
-   return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 1);
+    return byte_lock ( obj, ibuf, ibuflen, rbuf, rbuflen , 1);
 }
 
 #undef UNLOCKBIT
 
-/* --------------------------- */
-static int crlf(struct ofork *of)
-{
-    struct extmap      *em;
-
-    if ( ad_meta_fileno( of->of_ad ) == -1 || !memcmp( ufinderi, ad_entry( of->of_ad, ADEID_FINDERI),8)) { /* META */
-        /* no resource fork or no finderinfo, use our files extension mapping */
-        if (!( em = getextmap( of_name(of) )) || memcmp( "TEXT", em->em_type, sizeof( em->em_type ))) {
-            return 0;
-        } 
-        /* file type is TEXT */
-        return 1;
-
-    } else if ( !memcmp( "TEXT", ad_entry( of->of_ad, ADEID_FINDERI ), 4 )) {
-        return 1;
-    }
-    return 0;
-}
-
-
-static ssize_t read_file(struct ofork *ofork, int eid,
-                                    off_t offset, u_char nlmask,
-                                    u_char nlchar, char *rbuf,
-                                    size_t *rbuflen, const int xlate)
+/*!
+ * Read *rbuflen bytes from fork at offset
+ *
+ * @param ofork    (r)  fork handle
+ * @param eid      (r)  data fork or ressource fork entry id
+ * @param offset   (r)  offset
+ * @param rbuf     (r)  data buffer
+ * @param rbuflen  (rw) in: number of bytes to read, out: bytes read
+ *
+ * @return         AFP status code
+ */
+static int read_file(const struct ofork *ofork, int eid, off_t offset, char *rbuf, size_t *rbuflen)
 {
     ssize_t cc;
-    int eof = 0;
-    char *p, *q;
 
     cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen);
     if ( cc < 0 ) {
@@ -783,69 +748,23 @@ static ssize_t read_file(struct ofork *ofork, int eid,
         *rbuflen = 0;
         return( AFPERR_PARAM );
     }
-    if ( (size_t)cc < *rbuflen ) {
-        eof = 1;
-    }
-
-    /*
-     * Do Newline check.
-     */
-    if ( nlmask != 0 ) {
-        for ( p = rbuf, q = p + cc; p < q; ) {
-            if (( *p++ & nlmask ) == nlchar ) {
-                break;
-            }
-        }
-        if ( p != q ) {
-            cc = p - rbuf;
-            eof = 0;
-        }
-    }
-
-    /*
-     * If this file is of type TEXT, then swap \012 to \015.
-     */
-    if (xlate) {
-        for ( p = rbuf, q = p + cc; p < q; p++ ) {
-            if ( *p == '\012' ) {
-                *p = '\015';
-            } else if ( *p == '\015' ) {
-                *p = '\012';
-            }
-
-        }
-    }
-
     *rbuflen = cc;
-    if ( eof ) {
-        return( AFPERR_EOF );
-    }
+
+    if ((size_t)cc < *rbuflen)
+        return AFPERR_EOF;
     return AFP_OK;
 }
 
-/* -----------------------------
- * with ddp, afp_read can return fewer bytes than in reqcount 
- * so return EOF only if read actually past end of file not
- * if offset +reqcount > size of file
- * e.g.:
- * getfork size ==> 10430
- * read fork offset 0 size 10752 ????  ==> 4264 bytes (without EOF)
- * read fork offset 4264 size 6128 ==> 4264 (without EOF)
- * read fork offset 9248 size 1508 ==> 1182 (EOF)
- * 10752 is a bug in Mac 7.5.x finder 
- *
- * with dsi, should we check that reqcount < server quantum? 
-*/
 static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
 {
-    struct ofork       *ofork;
-    off_t              offset, saveoff, reqcount, savereqcount;
-    ssize_t            cc, err;
-    int                        eid, xlate = 0;
-    u_int16_t          ofrefnum;
-    u_char             nlmask, nlchar;
+    DSI          *dsi = obj->dsi;
+    struct ofork *ofork;
+    off_t        offset, saveoff, reqcount, savereqcount, size;
+    ssize_t      cc, err;
+    int          eid;
+    uint16_t     ofrefnum;
 
-    *rbuflen = 0;
+    /* we break the AFP spec here by not supporting nlmask and nlchar anymore */
 
     ibuf += 2;
     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
@@ -861,27 +780,9 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         err = AFPERR_ACCESS;
         goto afp_read_err;
     }
-    offset   = get_off_t(&ibuf, is64);
-    reqcount = get_off_t(&ibuf, is64);
-
-    if (is64) {
-        nlmask = nlchar = 0;
-    }
-    else {
-        nlmask = *ibuf++;
-        nlchar = *ibuf++;
-    }
-    /* if we wanted to be picky, we could add in the following
-     * bit: if (afp_version == 11 && !(nlmask == 0xFF || !nlmask))
-     */
-    if (reqcount < 0 || offset < 0) {
-        err = AFPERR_PARAM;
-        goto afp_read_err;
-    }
 
     if ( ofork->of_flags & AFPFORK_DATA) {
         eid = ADEID_DFORK;
-        xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
     } else if (ofork->of_flags & AFPFORK_RSRC) {
         eid = ADEID_RFORK;
     } else { /* fork wasn't opened. this should never really happen. */
@@ -889,103 +790,117 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         goto afp_read_err;
     }
 
+    offset   = get_off_t(&ibuf, is64);
+    reqcount = get_off_t(&ibuf, is64);
+
     /* zero request count */
     err = AFP_OK;
     if (!reqcount) {
         goto afp_read_err;
     }
 
+    AFP_READ_START((long)reqcount);
+
+    /* reqcount isn't always truthful. we need to deal with that. */
+    size = ad_size(ofork->of_ad, eid);
+
+    LOG(log_debug, logtype_afpd,
+        "afp_read(fork: %" PRIu16 " [%s], off: %" PRIu64 ", len: %" PRIu64 ", size: %" PRIu64 ")",
+        ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, reqcount, size);
+
+    if (offset >= size) {
+        err = AFPERR_EOF;
+        goto afp_read_err;
+    }
+
+    /* subtract off the offset */
+    if (reqcount + offset > size) {
+        reqcount = size - offset;
+        err = AFPERR_EOF;
+    }
+
     savereqcount = reqcount;
     saveoff = offset;
-    if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, saveoff, savereqcount,ofork->of_refnum) < 0) {
-        err = AFPERR_LOCK;
+
+    if (reqcount < 0 || offset < 0) {
+        err = AFPERR_PARAM;
         goto afp_read_err;
     }
 
-#ifndef WITH_SENDFILE
-    *rbuflen = MIN(reqcount, *rbuflen);
-    err = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, rbuflen, xlate);
-    if (err < 0)
+    if (obj->options.flags & OPTION_AFP_READ_LOCK) {
+        if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, offset, reqcount, ofork->of_refnum) < 0) {
+            err = AFPERR_LOCK;
+            goto afp_read_err;
+        }
+    }
+
+#ifdef WITH_SENDFILE
+    if (!(eid == ADEID_DFORK && ad_data_fileno(ofork->of_ad) == AD_SYMLINK) &&
+        !(obj->options.flags & OPTION_NOSENDFILE)) {
+        int fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0);
+        if (dsi_stream_read_file(dsi, fd, offset, reqcount, err) < 0) {
+            LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s",
+                of_name(ofork), strerror(errno));
+            goto afp_read_exit;
+        }
         goto afp_read_done;
+    }
 #endif
-    /* dsi can stream requests. we can only do this if we're not checking
-     * for an end-of-line character. oh well. */
-    if ((obj->proto == AFPPROTO_DSI) && (*rbuflen < reqcount) && !nlmask) {
-        DSI    *dsi = obj->handle;
-        off_t  size;
-
-        /* reqcount isn't always truthful. we need to deal with that. */
-        size = ad_size(ofork->of_ad, eid);
-
-        /* subtract off the offset */
-        size -= offset;
-        if (reqcount > size) {
-          reqcount = size;
-           err = AFPERR_EOF;
-        }
 
-        offset += *rbuflen;
+    *rbuflen = MIN(reqcount, dsi->server_quantum);
 
-        /* dsi_readinit() returns size of next read buffer. by this point,
-         * we know that we're sending some data. if we fail, something
-         * horrible happened. */
-        if ((cc = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0)
-            goto afp_read_exit;
-        *rbuflen = cc;
-        /* due to the nature of afp packets, we have to exit if we get
-           an error. we can't do this with translation on. */
-#ifdef WITH_SENDFILE 
-        if (!(xlate || Debug(obj) )) {
-            int fd;
-                        
-            fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0);
-            if (dsi_stream_read_file(dsi, fd, offset, dsi->datasize) < 0) {
-                if (errno == EINVAL || errno == ENOSYS)
-                    goto afp_read_loop;
-                else {
-                    LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s", of_name(ofork), strerror(errno));
-                    goto afp_read_exit;
-                }
-            }
+    cc = read_file(ofork, eid, offset, ibuf, rbuflen);
+    if (cc < 0) {
+        err = cc;
+        goto afp_read_done;
+    }
 
-            dsi_readdone(dsi);
-            goto afp_read_done;
-        }
+    LOG(log_debug, logtype_afpd,
+        "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file",
+        of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen);
 
-afp_read_loop:
-#endif 
+    offset += *rbuflen;
 
-        /* fill up our buffer. */
-        while (*rbuflen > 0) {
-            cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,rbuflen, xlate);
-            if (cc < 0)
-                goto afp_read_exit;
+    /*
+     * dsi_readinit() returns size of next read buffer. by this point,
+     * we know that we're sending some data. if we fail, something
+     * horrible happened.
+     */
+    if ((cc = dsi_readinit(dsi, ibuf, *rbuflen, reqcount, err)) < 0)
+        goto afp_read_exit;
+    *rbuflen = cc;
 
-            offset += *rbuflen;
-#ifdef DEBUG1
-            if (obj->options.flags & OPTION_DEBUG) {
-                printf( "(read) reply: %d, %d\n", *rbuflen, dsi->clientID);
-                bprint(rbuf, *rbuflen);
-            }
-#endif
-            /* dsi_read() also returns buffer size of next allocation */
-            cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */
-            if (cc < 0)
-                goto afp_read_exit;
-            *rbuflen = cc;
-        }
-        dsi_readdone(dsi);
-        goto afp_read_done;
+    while (*rbuflen > 0) {
+        /*
+         * This loop isn't really entered anymore, we've already
+         * sent the whole requested block in dsi_readinit().
+         */
+        cc = read_file(ofork, eid, offset, ibuf, rbuflen);
+        if (cc < 0)
+            goto afp_read_exit;
 
-afp_read_exit:
-        LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno));
-        dsi_readdone(dsi);
-        ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
-        obj->exit(EXITERR_CLNT);
+        offset += *rbuflen;
+        /* dsi_read() also returns buffer size of next allocation */
+        cc = dsi_read(dsi, ibuf, *rbuflen); /* send it off */
+        if (cc < 0)
+            goto afp_read_exit;
+        *rbuflen = cc;
     }
+    dsi_readdone(dsi);
+    goto afp_read_done;
+
+afp_read_exit:
+    LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno));
+    dsi_readdone(dsi);
+   if (obj->options.flags & OPTION_AFP_READ_LOCK)
+       ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum);
+    obj->exit(EXITERR_CLNT);
 
 afp_read_done:
-    ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum);
+    if (obj->options.flags & OPTION_AFP_READ_LOCK)
+        ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum);
+
+    AFP_READ_DONE();
     return err;
 
 afp_read_err:
@@ -1009,7 +924,7 @@ int afp_read_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rb
 int afp_flush(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
     struct vol *vol;
-    u_int16_t vid;
+    uint16_t vid;
 
     *rbuflen = 0;
     ibuf += 2;
@@ -1023,10 +938,10 @@ int afp_flush(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, s
     return( AFP_OK );
 }
 
-int afp_flushfork(AFPObj *obj _U_, char        *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
+int afp_flushfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct ofork       *ofork;
-    u_int16_t          ofrefnum;
+    struct ofork    *ofork;
+    uint16_t       ofrefnum;
 
     *rbuflen = 0;
     ibuf += 2;
@@ -1037,6 +952,9 @@ int afp_flushfork(AFPObj *obj _U_, char    *ibuf, size_t ibuflen _U_, char *rbuf _U
         return( AFPERR_PARAM );
     }
 
+    LOG(log_debug, logtype_afpd, "afp_flushfork(fork: %s)",
+        (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
+
     if ( flushfork( ofork ) < 0 ) {
         LOG(log_error, logtype_afpd, "afp_flushfork(%s): %s", of_name(ofork), strerror(errno) );
     }
@@ -1049,11 +967,11 @@ int afp_flushfork(AFPObj *obj _U_, char  *ibuf, size_t ibuflen _U_, char *rbuf _U
   There is a lot to tell about fsync, fdatasync, F_FULLFSYNC.
   fsync(2) on OSX is implemented differently than on other platforms.
   see: http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf.
- */
+*/
 int afp_syncfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
     struct ofork        *ofork;
-    u_int16_t           ofrefnum;
+    uint16_t           ofrefnum;
 
     *rbuflen = 0;
     ibuf += 2;
@@ -1066,9 +984,12 @@ int afp_syncfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_
         return( AFPERR_PARAM );
     }
 
+    LOG(log_debug, logtype_afpd, "afp_syncfork(fork: %s)",
+        (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
+
     if ( flushfork( ofork ) < 0 ) {
-       LOG(log_error, logtype_afpd, "flushfork(%s): %s", of_name(ofork), strerror(errno) );
-       return AFPERR_MISC;
+        LOG(log_error, logtype_afpd, "flushfork(%s): %s", of_name(ofork), strerror(errno) );
+        return AFPERR_MISC;
     }
 
     return( AFP_OK );
@@ -1082,17 +1003,17 @@ int flushfork(struct ofork *ofork)
     int err = 0, doflush = 0;
 
     if ( ad_data_fileno( ofork->of_ad ) != -1 &&
-            fsync( ad_data_fileno( ofork->of_ad )) < 0 ) {
+         fsync( ad_data_fileno( ofork->of_ad )) < 0 ) {
         LOG(log_error, logtype_afpd, "flushfork(%s): dfile(%d) %s",
             of_name(ofork), ad_data_fileno(ofork->of_ad), strerror(errno) );
         err = -1;
     }
 
     if ( ad_reso_fileno( ofork->of_ad ) != -1 &&  /* HF */
-        (ofork->of_flags & AFPFORK_RSRC)) {
+         (ofork->of_flags & AFPFORK_RSRC)) {
 
         /* read in the rfork length */
-        ad_refresh(ofork->of_ad);
+        ad_refresh(NULL, ofork->of_ad);
 
         /* set the date if we're dirty */
         if ((ofork->of_flags & AFPFORK_DIRTY) && !gettimeofday(&tv, NULL)) {
@@ -1103,7 +1024,7 @@ int flushfork(struct ofork *ofork)
 
         /* flush the header */
         if (doflush && ad_flush(ofork->of_ad) < 0)
-                err = -1;
+            err = -1;
 
         if (fsync( ad_reso_fileno( ofork->of_ad )) < 0)
             err = -1;
@@ -1116,10 +1037,10 @@ int flushfork(struct ofork *ofork)
     return( err );
 }
 
-int afp_closefork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
+int afp_closefork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
 {
-    struct ofork       *ofork;
-    u_int16_t          ofrefnum;
+    struct ofork    *ofork;
+    uint16_t       ofrefnum;
 
     *rbuflen = 0;
     ibuf += 2;
@@ -1129,8 +1050,12 @@ int afp_closefork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U
         LOG(log_error, logtype_afpd, "afp_closefork: of_find(%d) could not locate fork", ofrefnum );
         return( AFPERR_PARAM );
     }
-    if ( of_closefork( ofork ) < 0 ) {
-        LOG(log_error, logtype_afpd, "afp_closefork(%s): of_closefork: %s", of_name(ofork), strerror(errno) );
+
+    LOG(log_debug, logtype_afpd, "afp_closefork(fork: %" PRIu16 " [%s])",
+        ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "rsrc");
+
+    if (of_closefork(obj, ofork) < 0 ) {
+        LOG(log_error, logtype_afpd, "afp_closefork: of_closefork: %s", strerror(errno) );
         return( AFPERR_PARAM );
     }
 
@@ -1139,24 +1064,13 @@ int afp_closefork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U
 
 
 static ssize_t write_file(struct ofork *ofork, int eid,
-                                     off_t offset, char *rbuf,
-                                     size_t rbuflen, const int xlate)
+                          off_t offset, char *rbuf,
+                          size_t rbuflen)
 {
-    char *p, *q;
     ssize_t cc;
 
-    /*
-     * If this file is of type TEXT, swap \015 to \012.
-     */
-    if (xlate) {
-        for ( p = rbuf, q = p + rbuflen; p < q; p++ ) {
-            if ( *p == '\015' ) {
-                *p = '\012';
-            } else if ( *p == '\012' ) {
-                *p = '\015';
-            }
-        }
-    }
+    LOG(log_debug, logtype_afpd, "write_file(off: %ju, size: %zu)",
+        (uintmax_t)offset, rbuflen);
 
     if (( cc = ad_write(ofork->of_ad, eid, offset, 0,
                         rbuf, rbuflen)) < 0 ) {
@@ -1164,6 +1078,7 @@ static ssize_t write_file(struct ofork *ofork, int eid,
         case EDQUOT :
         case EFBIG :
         case ENOSPC :
+            LOG(log_error, logtype_afpd, "write_file: DISK FULL");
             return( AFPERR_DFULL );
         case EACCES:
             return AFPERR_ACCESS;
@@ -1177,16 +1092,21 @@ static ssize_t write_file(struct ofork *ofork, int eid,
 }
 
 
-/* FPWrite. NOTE: on an error, we always use afp_write_err as
- * the client may have sent us a bunch of data that's not reflected 
- * in reqcount et al. */
+/*
+ * FPWrite. NOTE: on an error, we always use afp_write_err as
+ * the client may have sent us a bunch of data that's not reflected
+ * in reqcount et al.
+ */
 static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64)
 {
-    struct ofork       *ofork;
-    off_t              offset, saveoff, reqcount;
-    int                        endflag, eid, xlate = 0, err = AFP_OK;
-    u_int16_t          ofrefnum;
-    ssize_t             cc;
+    struct ofork    *ofork;
+    off_t           offset, saveoff, reqcount, oldsize, newsize;
+    int             endflag, eid, err = AFP_OK;
+    uint16_t        ofrefnum;
+    ssize_t         cc;
+    DSI             *dsi = obj->dsi;
+    char            *rcvbuf = (char *)dsi->commands;
+    size_t          rcvbuflen = dsi->server_quantum;
 
     /* figure out parameters */
     ibuf++;
@@ -1204,6 +1124,9 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
         goto afp_write_err;
     }
 
+    LOG(log_debug, logtype_afpd, "afp_write(fork: %" PRIu16 " [%s], off: %" PRIu64 ", size: %" PRIu64 ")",
+        ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, reqcount);
+
     if ((ofork->of_flags & AFPFORK_ACCWR) == 0) {
         err = AFPERR_ACCESS;
         goto afp_write_err;
@@ -1215,7 +1138,6 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
 
     if ( ofork->of_flags & AFPFORK_DATA) {
         eid = ADEID_DFORK;
-        xlate = (ofork->of_vol->v_flags & AFPVOL_CRLF) ? crlf(ofork) : 0;
     } else if (ofork->of_flags & AFPFORK_RSRC) {
         eid = ADEID_RFORK;
     } else {
@@ -1223,8 +1145,9 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
         goto afp_write_err;
     }
 
+    oldsize = ad_size(ofork->of_ad, eid);
     if (endflag)
-        offset += ad_size(ofork->of_ad, eid);
+        offset += oldsize;
 
     /* handle bogus parameters */
     if (reqcount < 0 || offset < 0) {
@@ -1232,9 +1155,12 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
         goto afp_write_err;
     }
 
+    newsize = ((offset + reqcount) > oldsize) ? (offset + reqcount) : oldsize;
+
     /* offset can overflow on 64-bit capable filesystems.
      * report disk full if that's going to happen. */
-     if (sum_neg(is64, offset, reqcount)) {
+    if (sum_neg(is64, offset, reqcount)) {
+        LOG(log_error, logtype_afpd, "write_fork: DISK FULL");
         err = AFPERR_DFULL;
         goto afp_write_err;
     }
@@ -1245,96 +1171,86 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
         goto afp_write_err;
     }
 
+    AFP_WRITE_START((long)reqcount);
+
     saveoff = offset;
-    if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff,
-                   reqcount, ofork->of_refnum) < 0) {
-        err = AFPERR_LOCK;
-        goto afp_write_err;
+    if (obj->options.flags & OPTION_AFP_READ_LOCK) {
+        if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff, reqcount, ofork->of_refnum) < 0) {
+            err = AFPERR_LOCK;
+            goto afp_write_err;
+        }
     }
 
-    /* this is yucky, but dsi can stream i/o and asp can't */
-    switch (obj->proto) {
-#ifndef NO_DDP
-    case AFPPROTO_ASP:
-        if (asp_wrtcont(obj->handle, rbuf, rbuflen) < 0) {
+    /* find out what we have already */
+    if ((cc = dsi_writeinit(dsi, rcvbuf, rcvbuflen)) > 0) {
+        ssize_t written;
+        if ((written = write_file(ofork, eid, offset, rcvbuf, cc)) != cc) {
+            dsi_writeflush(dsi);
             *rbuflen = 0;
-            LOG(log_error, logtype_afpd, "afp_write: asp_wrtcont: %s", strerror(errno) );
-            return( AFPERR_PARAM );
+            if (obj->options.flags & OPTION_AFP_READ_LOCK)
+                ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
+            if (written > 0)
+                /* It's used for the read size and as error code in write_file(), ugh */
+                written = AFPERR_MISC;
+            return written;
         }
+    }
 
-#ifdef DEBUG1
-        if (obj->options.flags & OPTION_DEBUG) {
-            printf("(write) len: %d\n", *rbuflen);
-            bprint(rbuf, *rbuflen);
-        }
-#endif
-        if ((cc = write_file(ofork, eid, offset, rbuf, *rbuflen,
-                             xlate)) < 0) {
+    offset += cc;
+
+#ifdef WITH_RECVFILE
+    if (obj->options.flags & OPTION_RECVFILE) {
+        LOG(log_maxdebug, logtype_afpd, "afp_write(fork: %" PRIu16 " [%s], off: %" PRIu64 ", size: %" PRIu32 ")",
+            ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, dsi->datasize);
+
+        if ((cc = ad_recvfile(ofork->of_ad, eid, dsi->socket, offset, dsi->datasize, obj->options.splice_size)) < dsi->datasize) {
+            switch (errno) {
+            case EDQUOT:
+            case EFBIG:
+            case ENOSPC:
+                cc = AFPERR_DFULL;
+                dsi_writeflush(dsi);
+                break;
+            case ENOSYS:
+                goto afp_write_loop;
+            default:
+                /* Low level error, can't do much to back up */
+                cc = AFPERR_MISC;
+                LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno));
+            }
             *rbuflen = 0;
-            ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
+            if (obj->options.flags & OPTION_AFP_READ_LOCK)
+                ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
             return cc;
         }
+
         offset += cc;
-        break;
-#endif /* no afp/asp */
+        goto afp_write_done;
+    }
+#endif
 
-    case AFPPROTO_DSI:
-        {
-            DSI *dsi = obj->handle;
+afp_write_loop:
+    /* loop until everything gets written. currently
+     * dsi_write handles the end case by itself. */
+    while ((cc = dsi_write(dsi, rcvbuf, rcvbuflen))) {
 
-            /* find out what we have already and write it out. */
-            cc = dsi_writeinit(dsi, rbuf, *rbuflen);
-            if (!cc || (cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
-                dsi_writeflush(dsi);
-                *rbuflen = 0;
-                ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum);
-                return cc;
-            }
-            offset += cc;
-
-#if 0 /*def HAVE_SENDFILE_WRITE*/
-            if (!(xlate || obj->options.flags & OPTION_DEBUG)) {
-                if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket,
-                                       offset, dsi->datasize)) < 0) {
-                    switch (errno) {
-                    case EDQUOT :
-                    case EFBIG :
-                    case ENOSPC :
-                        cc = AFPERR_DFULL;
-                        break;
-                    default :
-                        LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) );
-                        goto afp_write_loop;
-                    }
-                    dsi_writeflush(dsi);
-                    *rbuflen = 0;
-                    ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
-                               reqcount,  ofork->of_refnum);
-                    return cc;
-                }
-
-                offset += cc;
-                goto afp_write_done;
-            }
-#endif /* 0, was HAVE_SENDFILE_WRITE */
-
-            /* loop until everything gets written. currently
-                    * dsi_write handles the end case by itself. */
-            while ((cc = dsi_write(dsi, rbuf, *rbuflen))) {
-                if ((cc = write_file(ofork, eid, offset, rbuf, cc, xlate)) < 0) {
-                    dsi_writeflush(dsi);
-                    *rbuflen = 0;
-                    ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff,
-                               reqcount,  ofork->of_refnum);
-                    return cc;
-                }
-                offset += cc;
-            }
+        if ((cc = write_file(ofork, eid, offset, rcvbuf, cc)) < 0) {
+            dsi_writeflush(dsi);
+            *rbuflen = 0;
+            if (obj->options.flags & OPTION_AFP_READ_LOCK)
+                ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
+            return cc;
         }
-        break;
+
+        LOG(log_debug, logtype_afpd, "afp_write: wrote: %jd, offset: %jd",
+            (intmax_t)cc, (intmax_t)offset);
+
+        offset += cc;
     }
 
-    ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
+afp_write_done:
+    if (obj->options.flags & OPTION_AFP_READ_LOCK)
+        ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount,  ofork->of_refnum);
     if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */
         ofork->of_flags |= AFPFORK_DIRTY;
 
@@ -1342,16 +1258,16 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s
     ofork->of_flags |= AFPFORK_MODIFIED;
 
     /* update write count */
-    ofork->of_vol->v_written += reqcount;
+    ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0;
 
     *rbuflen = set_off_t (offset, rbuf, is64);
+    AFP_WRITE_DONE();
     return( AFP_OK );
 
 afp_write_err:
-    if (obj->proto == AFPPROTO_DSI) {
-        dsi_writeinit(obj->handle, rbuf, *rbuflen);
-        dsi_writeflush(obj->handle);
-    }
+    dsi_writeinit(dsi, rcvbuf, rcvbuflen);
+    dsi_writeflush(dsi);
+
     if (err != AFP_OK) {
         *rbuflen = 0;
     }
@@ -1364,20 +1280,20 @@ int afp_write(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbufl
     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 0);
 }
 
-/* ---------------------------- 
+/* ----------------------------
  * FIXME need to deal with SIGXFSZ signal
-*/
+ */
 int afp_write_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t *rbuflen)
 {
     return write_fork(obj, ibuf, ibuflen, rbuf, rbuflen, 1);
 }
 
 /* ---------------------------- */
-int afp_getforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
+int afp_getforkparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
 {
-    struct ofork       *ofork;
+    struct ofork    *ofork;
     int             ret;
-    u_int16_t          ofrefnum, bitmap;
+    uint16_t       ofrefnum, bitmap;
     size_t          buflen;
     ibuf += 2;
     memcpy(&ofrefnum, ibuf, sizeof( ofrefnum ));
@@ -1392,15 +1308,14 @@ int afp_getforkparams(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbu
         return( AFPERR_PARAM );
     }
 
-    if ( ad_meta_fileno( ofork->of_ad ) != -1 ) { /* META */
-        if ( ad_refresh( ofork->of_ad ) < 0 ) {
+    if (AD_META_OPEN(ofork->of_ad)) {
+        if ( ad_refresh(NULL, ofork->of_ad ) < 0 ) {
             LOG(log_error, logtype_afpd, "getforkparams(%s): ad_refresh: %s", of_name(ofork), strerror(errno) );
             return( AFPERR_PARAM );
         }
     }
 
-    if (AFP_OK != ( ret = getforkparams( ofork, bitmap,
-                               rbuf + sizeof( u_short ), &buflen ))) {
+    if (AFP_OK != (ret = getforkparams(obj, ofork, bitmap, rbuf + sizeof( u_short ), &buflen ))) {
         return( ret );
     }