]> arthur.barton.de Git - netatalk.git/commitdiff
Automatic conversion for OS X ._ AppleDouble files, bug #550
authorRalph Boehme <rb@sernet.de>
Fri, 14 Feb 2014 10:31:57 +0000 (11:31 +0100)
committerRalph Boehme <rb@sernet.de>
Fri, 14 Feb 2014 15:38:08 +0000 (16:38 +0100)
Add automatic on-access conversion for ._ AppleDouble files created by
OS X to Netatalk's AppleDouble format.

OS X .AppleDouble files most of the times contain a FinderInfo
AppleDouble entry with a size larger then 32 bytes, containing packed
xattrs. Netatalk fails to cleanly deal with these and logs them as
"bogus entry id 9".

NEWS
include/atalk/adouble.h
libatalk/adouble/ad_flush.c
libatalk/adouble/ad_open.c

diff --git a/NEWS b/NEWS
index 162ea29d69baf7c9a1f654eb1a4fff3001099d9a..9e62be4bf45990a6338f5106a47d6a564b3bb308 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,8 @@ Changes in 3.1.1
 * FIX: Saving from applications like Photoshop may fail, because
        removing the ressource fork AppleDouble file failed. Bug #542.
 * FIX: dbd: remove orphaned ._ AppleDouble files. Bug #549.
 * FIX: Saving from applications like Photoshop may fail, because
        removing the ressource fork AppleDouble file failed. Bug #542.
 * FIX: dbd: remove orphaned ._ AppleDouble files. Bug #549.
+* NEW: afpd: Automatic conversion of ._ AppleDouble files
+       created by OS X. Bug #550.
 
 Changes in 3.1.0
 ================
 
 Changes in 3.1.0
 ================
index 972ce26f7534e40d287d6a4af5d7faaeb2090a98..1881ba028240c0ff5f74edbd162e3d3083e6e5ae 100644 (file)
@@ -376,6 +376,7 @@ struct adouble {
 /* ad_flush.c */
 extern int ad_rebuild_adouble_header_v2(struct adouble *);
 extern int ad_rebuild_adouble_header_ea(struct adouble *);
 /* ad_flush.c */
 extern int ad_rebuild_adouble_header_v2(struct adouble *);
 extern int ad_rebuild_adouble_header_ea(struct adouble *);
+extern  int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf);
 extern int ad_copy_header (struct adouble *, struct adouble *);
 extern int ad_flush (struct adouble *);
 extern int ad_close (struct adouble *, int);
 extern int ad_copy_header (struct adouble *, struct adouble *);
 extern int ad_flush (struct adouble *);
 extern int ad_close (struct adouble *, int);
index 50fd8baef2a2acc7297d0dbb5178490cc456532f..76d44a98051a6820fc3c666491efec9beab499bb 100644 (file)
@@ -146,7 +146,7 @@ int ad_rebuild_adouble_header_ea(struct adouble *ad)
 /*!
  * Prepare adbuf buffer from struct adouble for writing on disk
  */
 /*!
  * Prepare adbuf buffer from struct adouble for writing on disk
  */
-static int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf)
+int ad_rebuild_adouble_header_osx(struct adouble *ad, char *adbuf)
 {
     uint32_t       temp;
     uint16_t       nent;
 {
     uint32_t       temp;
     uint16_t       nent;
index 92ffc8852f4a0cf2095eae57ae507d14d6cb6934..ea221ff4ec62964f827a8e2e8771a945d18c8b27 100644 (file)
@@ -407,15 +407,17 @@ static int parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
         len = ntohl( len );
         buf += sizeof( len );
 
         len = ntohl( len );
         buf += sizeof( len );
 
-        if (eid
-            && eid < ADEID_MAX
-            && off < sizeof(ad->ad_data)
-            && (off + len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
-            ad->ad_eid[ eid ].ade_off = off;
-            ad->ad_eid[ eid ].ade_len = len;
-        } else if (ret == 0) {
-            ret  = -11;
-            LOG(log_warning, logtype_ad, "parse_entries: bogus eid: %d", eid);
+        ad->ad_eid[eid].ade_off = off;
+        ad->ad_eid[eid].ade_len = len;
+
+        if (!eid
+            || eid > ADEID_MAX
+            || off >= sizeof(ad->ad_data)
+            || ((eid != ADEID_RFORK) && (off + len >  sizeof(ad->ad_data))))
+        {
+            ret = -1;
+            LOG(log_warning, logtype_ad, "parse_entries: bogus eid: %u, off: %u, len: %u",
+                (uint)eid, (uint)off, (uint)len);
         }
     }
 
         }
     }
 
@@ -480,6 +482,8 @@ static int ad_header_read(const char *path, struct adouble *ad, const struct sta
     if (parse_entries(ad, buf, nentries) != 0) {
         LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
             path ? fullpathname(path) : "");
     if (parse_entries(ad, buf, nentries) != 0) {
         LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
             path ? fullpathname(path) : "");
+        errno = EIO;
+        return -1;
     }
     if (!ad_getentryoff(ad, ADEID_RFORK)
         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
     }
     if (!ad_getentryoff(ad, ADEID_RFORK)
         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
@@ -553,6 +557,81 @@ EC_CLEANUP:
     return 0;
 }
 
     return 0;
 }
 
+/**
+ * Convert from Apple's ._ file to Netatalk
+ *
+ * Apple's AppleDouble may contain a FinderInfo entry longer then 32 bytes
+ * containing packed xattrs. Netatalk can't deal with that, so we
+ * simply discard the packed xattrs.
+ *
+ * As we call ad_open() which might result in a recursion, just to be sure
+ * use static variable in_conversion to check for that.
+ *
+ * Returns -1 in case an error occured, 0 if no conversion was done, 1 otherwise
+ **/
+static int ad_convert_osx(const char *path, struct adouble *ad)
+{
+    EC_INIT;
+    static bool in_conversion = false;
+    char *map;
+    int finderlen = ad_getentrylen(ad, ADEID_FINDERI);
+    ssize_t origlen;
+
+    if (in_conversion || finderlen == ADEDLEN_FINDERI)
+        return 0;
+    in_conversion = true;
+
+    LOG(log_debug, logtype_ad, "Converting OS X AppleDouble %s, FinderInfo length: %d",
+        fullpathname(path), finderlen);
+
+    origlen = ad_getentryoff(ad, ADEID_RFORK) + ad_getentrylen(ad, ADEID_RFORK);
+
+    map = mmap(NULL, origlen, PROT_WRITE, MAP_SHARED, ad_reso_fileno(ad), 0);
+    if (map == MAP_FAILED) {
+        LOG(log_error, logtype_ad, "mmap AppleDouble: %s\n", strerror(errno));
+        EC_FAIL;
+    }
+
+    memmove(map + ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI,
+            map + ad_getentryoff(ad, ADEID_RFORK),
+            ad_getentrylen(ad, ADEID_RFORK));
+
+    ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
+    ad->ad_rlen = ad_getentrylen(ad, ADEID_RFORK);
+    ad_setentryoff(ad, ADEID_RFORK, ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI);
+
+    EC_ZERO_LOG( ftruncate(ad_reso_fileno(ad),
+                           ad_getentryoff(ad, ADEID_RFORK)
+                           + ad_getentrylen(ad, ADEID_RFORK)) );
+
+    (void)ad_rebuild_adouble_header_osx(ad, map);
+    munmap(map, origlen);
+
+    /* Create a metadata EA if one doesn't exit */
+    if (strlen(path) < 3)
+        EC_EXIT_STATUS(0);
+    struct adouble adea;
+    ad_init_old(&adea, AD_VERSION_EA, ad->ad_options);
+
+    if (ad_open(&adea, path + 2, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
+        LOG(log_error, logtype_ad, "create metadata: %s\n", strerror(errno));
+        EC_FAIL;
+    }
+    if (adea.ad_mdp->adf_flags & O_CREAT) {
+        memcpy(ad_entry(&adea, ADEID_FINDERI),
+               ad_entry(ad, ADEID_FINDERI),
+               ADEDLEN_FINDERI);
+        ad_flush(&adea);
+    }
+    ad_close(&adea, ADFLAGS_HF);
+
+EC_CLEANUP:
+    in_conversion = false;
+    if (ret != 0)
+        return -1;
+    return 1;
+}
+
 /* Read an ._ file, only uses the resofork, finderinfo is taken from EA */
 static int ad_header_read_osx(const char *path, struct adouble *ad, const struct stat *hst)
 {
 /* Read an ._ file, only uses the resofork, finderinfo is taken from EA */
 static int ad_header_read_osx(const char *path, struct adouble *ad, const struct stat *hst)
 {
@@ -563,8 +642,13 @@ static int ad_header_read_osx(const char *path, struct adouble *ad, const struct
     int                 len;
     ssize_t             header_len;
     struct stat         st;
     int                 len;
     ssize_t             header_len;
     struct stat         st;
+    int                 retry_read = 0;
 
 
+reread:
+    LOG(log_debug, logtype_ad, "ad_header_read_osx: %s", path ? fullpathname(path) : "");
+    ad_init_old(&adosx, AD_VERSION_EA, ad->ad_options);
     memset(buf, 0, sizeof(adosx.ad_data));
     memset(buf, 0, sizeof(adosx.ad_data));
+    adosx.ad_rfp->adf_fd = ad_reso_fileno(ad);
 
     /* read the header */
     EC_NEG1( header_len = adf_pread(ad->ad_rfp, buf, AD_DATASZ_OSX, 0) );
 
     /* read the header */
     EC_NEG1( header_len = adf_pread(ad->ad_rfp, buf, AD_DATASZ_OSX, 0) );
@@ -605,6 +689,23 @@ static int ad_header_read_osx(const char *path, struct adouble *ad, const struct
             path ? fullpathname(path) : "");
     }
 
             path ? fullpathname(path) : "");
     }
 
+    if (ad_getentrylen(&adosx, ADEID_FINDERI) != ADEDLEN_FINDERI) {
+        LOG(log_warning, logtype_ad, "Convert OS X to Netatalk AppleDouble: %s",
+            path ? fullpathname(path) : "");
+
+        if (retry_read > 0) {
+            LOG(log_error, logtype_ad, "ad_header_read_osx: %s, giving up", path ? fullpathname(path) : "");
+            errno = EIO;
+            EC_FAIL;
+        }
+        retry_read++;
+        if (ad_convert_osx(path, &adosx) == 1) {
+            goto reread;
+        }
+        errno = EIO;
+        EC_FAIL;
+    }
+
     if (ad_getentryoff(&adosx, ADEID_RFORK) == 0
         || ad_getentryoff(&adosx, ADEID_RFORK) > sizeof(ad->ad_data)
         || ad_getentryoff(&adosx, ADEID_RFORK) > header_len
     if (ad_getentryoff(&adosx, ADEID_RFORK) == 0
         || ad_getentryoff(&adosx, ADEID_RFORK) > sizeof(ad->ad_data)
         || ad_getentryoff(&adosx, ADEID_RFORK) > header_len
@@ -673,6 +774,8 @@ static int ad_header_read_ea(const char *path, struct adouble *ad, const struct
     if (parse_entries(ad, buf + AD_HEADER_LEN, nentries)) {
         LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
             path ? fullpathname(path) : "");
     if (parse_entries(ad, buf + AD_HEADER_LEN, nentries)) {
         LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
             path ? fullpathname(path) : "");
+        errno = EINVAL;
+        EC_FAIL;
     }
 
     if (nentries != ADEID_NUM_EA
     }
 
     if (nentries != ADEID_NUM_EA
@@ -1430,7 +1533,7 @@ off_t ad_getentryoff(const struct adouble *ad, int eid)
 #ifdef HAVE_EAFD
         return 0;
 #else
 #ifdef HAVE_EAFD
         return 0;
 #else
-        return ADEDOFF_RFORK_OSX;
+        return ad->ad_eid[eid].ade_off;
 #endif
     default:
         return ad->ad_eid[eid].ade_off;
 #endif
     default:
         return ad->ad_eid[eid].ade_off;