]> arthur.barton.de Git - netatalk.git/commitdiff
Restructure fork opening, fix adouble refcounting
authorFrank Lahm <franklahm@googlemail.com>
Thu, 23 Aug 2012 08:06:04 +0000 (10:06 +0200)
committerFrank Lahm <franklahm@googlemail.com>
Thu, 23 Aug 2012 08:06:04 +0000 (10:06 +0200)
Fixes Bug ID 3559783

The bug is caused by passing ADFLAGS_NOHF to ad_open() when opening a
files data fork. For files without metadata then ad_open() doesn't
return an error for the ADFLAGS_HF request to open the metadata. As a
result of the successfull ad_open return AFPFORK_META is set in the
fork struct, which implies the meta is open. Later afp_close looked at
that flag and added ADFLAGS_HF to ad_close flags, resulting in a
refcount decement of 2 although the ad_open only incremented by 1. Eg
opening such a file twice, then closing once, close the fork. Later
operations on the other still open fork fail.

afp_openfork opens the fork and metadata in one call to ad_open which
lead to an insane if/else and error checking code path.  I've
seperated this in two distincs calls to ad_open(): the first opens the
fork (data or ressource), the second opens the metadata. This gives a
cleaner code path but it required a subtle modifications to the way we
refcount and flag forks as open inside ad_open: currently we use the
fd and it's recount but these do not differente between open data and
metadata in the case of adouble:ea as both use the data fork handle,
fd and it's refcount.  Now we add real refcounting for the forks
different from the recount on the file fd by adding three refcounting
variables to struct.adouble and change the AD_XXX_OPEN macros to use
these.

Added test-suite testcases 3, 4 and 7.

etc/afpd/fork.c
include/atalk/adouble.h
libatalk/adouble/ad_flush.c
libatalk/adouble/ad_open.c

index 8f5a2e671740c36029424008ecda65b1c67d6e2b..cd1580fa43005552880432d968571b7fe3b34248 100644 (file)
@@ -301,8 +301,11 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         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(obj, vol, upath, access ) < 0) {
             return AFPERR_ACCESS;
@@ -313,124 +316,97 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si
         }
     }
 
-    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))) {
         adsame = opened->of_ad;
     }
 
-    if ( fork == OPENFORK_DATA ) {
+    adflags = ADFLAGS_SETSHRMD;
+
+    if (fork == OPENFORK_DATA) {
         eid = ADEID_DFORK;
-        adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF;
+        adflags |= ADFLAGS_DF;
     } else {
         eid = ADEID_RFORK;
-        adflags = ADFLAGS_RF | ADFLAGS_HF | ADFLAGS_NOHF;
+        adflags |= ADFLAGS_RF;
         if (!(access & OPENACC_WR))
             adflags |= ADFLAGS_NORF;
     }
 
-    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(ofork->of_ad, upath,
-                    adflags | ADFLAGS_RDWR | ADFLAGS_SETSHRMD) < 0) {
-            switch ( errno ) {
-            case EROFS:
-                ret = AFPERR_VLOCK;
-            case EACCES:
-                goto openfork_err;
-            case ENOENT:
-                if (fork == OPENFORK_DATA) {
-                    /* try to open only the data fork */
-                    if (ad_open(ofork->of_ad, upath,
-                                ADFLAGS_DF | ADFLAGS_RDWR | ADFLAGS_SETSHRMD) < 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(ofork->of_ad, upath,
-                                adflags | ADFLAGS_RDWR | ADFLAGS_SETSHRMD | ADFLAGS_CREATE, 0666) < 0)
-                        goto openfork_err;
-                    ofork->of_flags |= AFPFORK_META;
-                }
-                break;
-            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;
-            }
-        }
-        else {
-            /* the ressource fork is open too */
-            ofork->of_flags |= AFPFORK_META;
+
+    /* 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(ofork->of_ad, upath, adflags | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
-            switch ( errno ) {
-            case EROFS:
-                ret = AFPERR_VLOCK;
-                goto openfork_err;
-            case EACCES:
-                goto openfork_err;
-            case ENOENT:
-                /* see if client asked for a read only data fork */
-                if (fork == OPENFORK_DATA) {
-                    if (ad_open(ofork->of_ad, upath, ADFLAGS_DF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
-                        goto openfork_err;
-                    }
-                    adflags = ADFLAGS_DF;
-                }
-                /* else we don't set AFPFORK_META because there's no ressource fork file
-                 * We need to check AFPFORK_META 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;
-            case EISDIR :
-                ret = AFPERR_BADTYPE;
-                goto openfork_err;
-            default:
-                LOG(log_error, logtype_afpd, "afp_openfork(\"%s\"): %s",
-                    fullpathname(s_path->m_name), strerror(errno) );
-                goto openfork_err;
-            }
-        } else {
-            ofork->of_flags |= AFPFORK_META;
+        adflags = ADFLAGS_HF | ADFLAGS_RDONLY;
+    }
+
+    if (ad_open(ofork->of_ad, upath, adflags, 0666) == 0) {
+        ofork->of_flags |= AFPFORK_META;
+    } 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;
         }
     }
 
@@ -500,6 +476,9 @@ 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 );
 
@@ -818,8 +797,8 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     size = ad_size(ofork->of_ad, eid);
 
     LOG(log_debug, logtype_afpd,
-        "afp_read(off: %" PRIu64 ", len: %" PRIu64 ", fork: %s, size: %" PRIu64 ")",
-        offset, reqcount, (ofork->of_flags & AFPFORK_DATA) ? "d" : "r", size);
+        "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;
@@ -835,18 +814,11 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si
     savereqcount = reqcount;
     saveoff = offset;
 
-    LOG(log_debug, logtype_afpd,
-        "afp_read(off: %" PRIu64 ", len: %" PRIu64 ", fork: %s)",
-        offset, reqcount, (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
-
     if (reqcount < 0 || offset < 0) {
         err = AFPERR_PARAM;
         goto afp_read_err;
     }
 
-    LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd)",
-        of_name(ofork), (intmax_t)offset, (intmax_t)reqcount);
-
     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;
@@ -1063,8 +1035,8 @@ int afp_closefork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, s
         return( AFPERR_PARAM );
     }
 
-    LOG(log_debug, logtype_afpd, "afp_closefork(fork: %s)",
-        (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
+    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(%s): of_closefork: %s", of_name(ofork), strerror(errno) );
@@ -1137,8 +1109,8 @@ 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(off: %" PRIu64 ", size: %" PRIu64 ", fork: %s)",
-        offset, reqcount, (ofork->of_flags & AFPFORK_DATA) ? "d" : "r");
+    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;
index a551d162585bf7074e399f740987ef4ddc1e4087..384486ea4633d8c14085af8fb998e7853e789e3f 100644 (file)
@@ -49,7 +49,6 @@
 
 /* version info */
 #define AD_VERSION2     0x00020000
-#define AD_VERSION2_OSX 0x00020001
 #define AD_VERSION_EA   0x00020002
 
 /* default */
@@ -201,9 +200,6 @@ struct adouble {
 
     struct ad_fd        ad_data_fork;     /* the data fork                            */
 
-    struct ad_fd        ad_metadata_fork; /* adouble:v2 -> unused                     *
-                                           * adouble:ea -> fd unused, only flags used */
-
     struct ad_fd        ad_resource_fork; /* adouble:v2 -> the adouble file           *
                                            * adouble:ea -> the EA fd                  */
 
@@ -211,13 +207,16 @@ struct adouble {
                                            * adouble:ea -> ad_resource_fork           */
 
     struct ad_fd        *ad_mdp;          /* adouble:v2 -> ad_resource_fork           *
-                                           * adouble:ea -> ad_metadata_fork           */
+                                           * adouble:ea -> ad_data_fork               */
 
     int                 ad_vers;          /* Our adouble version info (AD_VERSION*)   */
     int                 ad_adflags;       /* ad_open flags adflags like ADFLAGS_DIR   */
     uint32_t            ad_inited;
     int                 ad_options;
     int                 ad_refcount;       /* multiple forks may open one adouble     */
+    int                 ad_data_refcount;
+    int                 ad_meta_refcount;
+    int                 ad_reso_refcount;
     off_t               ad_rlen;           /* ressource fork len with AFP 3.0         *
                                             * the header parameter size is too small. */
     char                *ad_name;          /* name in server encoding (usually UTF8)  */
@@ -352,9 +351,9 @@ struct adouble {
 #define ad_meta_fileno(ad)  ((ad)->ad_mdp->adf_fd)
 
 /* -1: not open, AD_SYMLINK (-2): it's a symlink */
-#define AD_DATA_OPEN(ad) ((ad)->ad_data_fork.adf_fd >= 0)
-#define AD_META_OPEN(ad) ((ad)->ad_mdp->adf_fd >= 0)
-#define AD_RSRC_OPEN(ad) ((ad)->ad_rfp->adf_fd >= 0)
+#define AD_DATA_OPEN(ad) (((ad)->ad_data_refcount) && (ad_data_fileno(ad) >= 0))
+#define AD_META_OPEN(ad) (((ad)->ad_meta_refcount) && (ad_meta_fileno(ad) >= 0))
+#define AD_RSRC_OPEN(ad) (((ad)->ad_reso_refcount) && (ad_reso_fileno(ad) >= 0))
 
 #define ad_getversion(ad)   ((ad)->ad_version)
 
index eaf904cac77ab597d57e89ee0699a68d8788db01..a637b27f2ebeb108db55d5eeeb9674684e43beb2 100644 (file)
@@ -392,9 +392,12 @@ int ad_close(struct adouble *ad, int adflags)
     if (ad == NULL)
         return err;
 
+
     LOG(log_debug, logtype_default,
-        "ad_close(%s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
+        "ad_close(%s): BEGIN: {d: %d, m: %d, r: %d} "
+        "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         adflags2logstr(adflags),
+        ad->ad_data_refcount, ad->ad_meta_refcount, ad->ad_reso_refcount,
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
         ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
         ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
@@ -407,37 +410,48 @@ int ad_close(struct adouble *ad, int adflags)
     if ((ad->ad_vers == AD_VERSION2) && (adflags & ADFLAGS_RF))
         adflags |= ADFLAGS_HF;
 
-    if ((adflags & ADFLAGS_DF)
-        && (ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == AD_SYMLINK)
-        && --ad->ad_data_fork.adf_refcount == 0) {
-        if (ad_data_closefd(ad) < 0)
-            err = -1;
-        adf_lock_free(&ad->ad_data_fork);
+    if ((adflags & ADFLAGS_DF) && (ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == AD_SYMLINK)) {        
+        if (ad->ad_data_refcount)
+            ad->ad_data_refcount--;
+        if (--ad->ad_data_fork.adf_refcount == 0) {
+            if (ad_data_closefd(ad) < 0)
+                err = -1;
+            adf_lock_free(&ad->ad_data_fork);
+        }
     }
 
-    if ((adflags & ADFLAGS_HF)
-        && (ad_meta_fileno(ad) != -1) && !(--ad->ad_mdp->adf_refcount)) {
-        if (close( ad_meta_fileno(ad)) < 0)
-            err = -1;
-        ad_meta_fileno(ad) = -1;
-        if (ad->ad_vers == AD_VERSION2)
-            adf_lock_free(ad->ad_mdp);
+    if ((adflags & ADFLAGS_HF) && (ad_meta_fileno(ad) != -1)) {
+        if (ad->ad_meta_refcount)
+            ad->ad_meta_refcount--;
+        if (!(--ad->ad_mdp->adf_refcount)) {
+            if (close( ad_meta_fileno(ad)) < 0)
+                err = -1;
+            ad_meta_fileno(ad) = -1;
+            if (ad->ad_vers == AD_VERSION2)
+                adf_lock_free(ad->ad_mdp);
+        }
     }
 
-    if ((adflags & ADFLAGS_RF) && (ad->ad_vers == AD_VERSION_EA)) {
-        if ((ad_reso_fileno(ad) != -1)
-            && !(--ad->ad_rfp->adf_refcount)) {
-            if (close(ad->ad_rfp->adf_fd) < 0)
-                err = -1;
-            ad->ad_rlen = 0;
-            ad_reso_fileno(ad) = -1;
-            adf_lock_free(ad->ad_rfp);
+    if (adflags & ADFLAGS_RF) {
+        if (ad->ad_reso_refcount)
+            ad->ad_reso_refcount--;
+        if (ad->ad_vers == AD_VERSION_EA) {
+            if ((ad_reso_fileno(ad) != -1)
+                && !(--ad->ad_rfp->adf_refcount)) {
+                if (close(ad->ad_rfp->adf_fd) < 0)
+                    err = -1;
+                ad->ad_rlen = 0;
+                ad_reso_fileno(ad) = -1;
+                adf_lock_free(ad->ad_rfp);
+            }
         }
     }
 
     LOG(log_debug, logtype_default,
-        "ad_close(%s): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
+        "ad_close(%s): END: %d {d: %d, m: %d, r: %d} "
+        "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         adflags2logstr(adflags), err,
+        ad->ad_data_refcount, ad->ad_meta_refcount, ad->ad_reso_refcount,
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
         ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
         ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
index 98ec4c7f9182cd49e27fa0962be2d0a486aad521..70fba7d27ab5818a822a7269286dfdf1baab9833 100644 (file)
@@ -166,13 +166,6 @@ static const struct entry entry_order_ea[ADEID_NUM_EA + 1] = {
     {0, 0, 0}
 };
 
-/* fallback for EAs */
-static const struct entry entry_order_osx[ADEID_NUM_OSX +1] = {
-    {ADEID_FINDERI, ADEDOFF_FINDERI_OSX, ADEDLEN_FINDERI},
-    {ADEID_RFORK, ADEDOFF_RFORK_OSX, ADEDLEN_INIT},
-    {0, 0, 0}
-};
-
 #define ADFLAGS2LOGSTRBUFSIZ 128
 const char *adflags2logstr(int adflags)
 {
@@ -325,10 +318,9 @@ static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp,
 
     LOG(log_debug, logtype_default, "new_ad_header(\"%s\")", path);
 
-    if (stp == NULL) {
-        stp = &st;
-        if (lstat(path, &st) != 0)
-            return -1;
+    if (ad->ad_magic == AD_MAGIC) {
+        LOG(log_debug, logtype_default, "new_ad_header(\"%s\"): already initialized", path);
+        return 0;
     }
 
     ad->ad_magic = AD_MAGIC;
@@ -343,11 +335,8 @@ static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp,
         eid = entry_order2;
     else if (ad->ad_vers == AD_VERSION_EA)
         eid = entry_order_ea;
-    else if (ad->ad_vers == AD_VERSION2_OSX)
-        eid = entry_order_osx;
-    else {
+    else
         return -1;
-    }
 
     while (eid->id) {
         ad->ad_eid[eid->id].ade_off = eid->offset;
@@ -356,34 +345,39 @@ static int new_ad_header(struct adouble *ad, const char *path, struct stat *stp,
     }
 
     /* put something sane in the directory finderinfo */
-    if (ad->ad_vers != AD_VERSION2_OSX) {
-        if ((adflags & ADFLAGS_DIR)) {
-            /* set default view */
-            ashort = htons(FINDERINFO_CLOSEDVIEW);
-            memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
-        } else {
-            /* set default creator/type fields */
-            memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
-            memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
-        }
+    if (stp == NULL) {
+        stp = &st;
+        if (lstat(path, &st) != 0)
+            return -1;
+    }
 
-        /* make things invisible */
-        if ((ad->ad_options & ADVOL_INVDOTS)
-            && (*path == '.')
-            && !((adflags & ADFLAGS_DIR) && (path[1] == 0))
-            ) {
-            ashort = htons(ATTRBIT_INVISIBLE);
-            ad_setattr(ad, ashort);
-            ashort = htons(FINDERINFO_INVISIBLE);
-            memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
-        }
+    if ((adflags & ADFLAGS_DIR)) {
+        /* set default view */
+        ashort = htons(FINDERINFO_CLOSEDVIEW);
+        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
+    } else {
+        /* set default creator/type fields */
+        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4);
+        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4);
+    }
 
-        /* put something sane in the date fields */
-        ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, stp->st_mtime);
-        ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, stp->st_mtime);
-        ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, stp->st_mtime);
-        ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
+    /* make things invisible */
+    if ((ad->ad_options & ADVOL_INVDOTS)
+        && (*path == '.')
+        && !((adflags & ADFLAGS_DIR) && (path[1] == 0))
+        ) {
+        ashort = htons(ATTRBIT_INVISIBLE);
+        ad_setattr(ad, ashort);
+        ashort = htons(FINDERINFO_INVISIBLE);
+        memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
     }
+
+    /* put something sane in the date fields */
+    ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, stp->st_mtime);
+    ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, stp->st_mtime);
+    ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, stp->st_mtime);
+    ad_setdate(ad, AD_DATE_BACKUP, AD_DATE_START);
+
     return 0;
 }
 
@@ -1042,8 +1036,7 @@ static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble
     oflags = O_NOFOLLOW | (ad2openflags(ad, ADFLAGS_DF, adflags) & ~(O_CREAT | O_TRUNC));
 
     if (ad_meta_fileno(ad) == AD_SYMLINK)
-        /* symlink */
-        EC_EXIT;
+        goto EC_CLEANUP;
 
     if (ad_meta_fileno(ad) != -1) {
         /* the file is already open, but we want write access: */
@@ -1256,6 +1249,7 @@ static int ad_open_rf_ea(const char *path, int adflags, int mode, struct adouble
         /* This is a new adouble header file, create it */
         LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork, initializing: \"%s\"",
             path, rfpath);
+        EC_NEG1_LOG( new_ad_header(ad, path, NULL, adflags) );
         LOG(log_debug, logtype_default, "ad_open_rf(\"%s\"): created adouble rfork, flushing: \"%s\"",
             path, rfpath);
         ad_flush(ad);
@@ -1610,8 +1604,10 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...)
     mode_t mode = 0;
 
     LOG(log_debug, logtype_default,
-        "ad_open(\"%s\", %s): BEGIN [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
+        "ad_open(\"%s\", %s): BEGIN {d: %d, m: %d, r: %d}"
+        "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), adflags2logstr(adflags),
+        ad->ad_data_refcount, ad->ad_meta_refcount, ad->ad_reso_refcount,
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
         ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
         ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);
@@ -1646,15 +1642,27 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...)
     va_end(args);
 
     if (adflags & ADFLAGS_DF) {
-        EC_ZERO( ad_open_df(path, adflags, mode, ad) );
+        ad->ad_data_refcount++;
+        if (ad_open_df(path, adflags, mode, ad) != 0) {
+            ad->ad_data_refcount--;
+            EC_FAIL;
+        }
     }
 
     if (adflags & ADFLAGS_HF) {
-        EC_ZERO( ad_open_hf(path, adflags, mode, ad) );
+        ad->ad_meta_refcount++;
+        if (ad_open_hf(path, adflags, mode, ad) != 0) {
+            ad->ad_meta_refcount--;
+            EC_FAIL;
+        }
     }
 
     if (adflags & ADFLAGS_RF) {
-        EC_ZERO( ad_open_rf(path, adflags, mode, ad) );
+        ad->ad_reso_refcount++;
+        if (ad_open_rf(path, adflags, mode, ad) != 0) {
+            ad->ad_reso_refcount--;
+            EC_FAIL;
+        }
     }
 
     if (adflags & ADFLAGS_CHECK_OF) {
@@ -1663,8 +1671,10 @@ int ad_open(struct adouble *ad, const char *path, int adflags, ...)
 
 EC_CLEANUP:
     LOG(log_debug, logtype_default,
-        "ad_open(\"%s\"): END: %d [dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
+        "ad_open(\"%s\"): END: %d {d: %d, m: %d, r: %d}"
+        "[dfd: %d (ref: %d), mfd: %d (ref: %d), rfd: %d (ref: %d)]",
         fullpathname(path), ret,
+        ad->ad_data_refcount, ad->ad_meta_refcount, ad->ad_reso_refcount,
         ad_data_fileno(ad), ad->ad_data_fork.adf_refcount,
         ad_meta_fileno(ad), ad->ad_mdp->adf_refcount,
         ad_reso_fileno(ad), ad->ad_rfp->adf_refcount);